{"github_data": {"url": "https://api.github.com/repos/Tautulli/Tautulli/compare/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8...5610c167a17b0898d93d0588a0df81c03306ac52", "html_url": "https://github.com/Tautulli/Tautulli/compare/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8...5610c167a17b0898d93d0588a0df81c03306ac52", "permalink_url": "https://github.com/Tautulli/Tautulli/compare/Tautulli:a96fd23...Tautulli:5610c16", "diff_url": "https://github.com/Tautulli/Tautulli/compare/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8...5610c167a17b0898d93d0588a0df81c03306ac52.diff", "patch_url": "https://github.com/Tautulli/Tautulli/compare/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8...5610c167a17b0898d93d0588a0df81c03306ac52.patch", "base_commit": {"sha": "a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "node_id": "C_kwDOAducuNoAKGE5NmZkMjNkNzJmNWFhMzA0MmQ2ZjlhNTVkODA1MTY1M2ZmNmYwYjg", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-01-11T23:27:24Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-01-11T23:27:24Z"}, "message": "v2.15.1", "tree": {"sha": "721e2530d6f00098bc3812c2ea9953ea3eea7e8f", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/721e2530d6f00098bc3812c2ea9953ea3eea7e8f"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmeC/l0ACgkQsfH5gHGE\naXoSXRAApHHOvJjPaIteCSyzf58svXlM2SsJMABS87N4gHlYkvitb8Pf6gYyOeu3\nGD/cd5tIH3Xkt4p/G9gON79ChCVQEQUVZFYb8wyaAO3EFwlQ+5kZi1CWFzHlD6Ib\nKB2XPXfGW/gLXNuP6mWy/QP50tZe7Ryvv2loWQNlKCjLrxeGvJ/rPKjtsNmp2lej\nITRbc2cDVUHfrqWbGhGSpPfrVRRfDBeLcuBlzO7/u6V8hW6L49P86Qhs6YrfSwCX\nRCTzaREMTgHwbUlm/2MKLpOwuZ9qp0vG7Kxhg8pe76BIfR5Zulgmn1rxB4Mw3/vy\nXOmIWxAVePRDoM2R5OVFXAg4EJ5oPCY2ymxDnIiPLRy7WD59xm1dTyoYP7omhvIy\nCGND5H5T6rLuC8SfR4svohAlrSVjzYa9DChTaQMk0eU565ph8UH7vAsFGwjkHL4b\nFSCUMM8DZAc812NfNGDMacxheZYIDC9e7oUkVYsb+/jmxFueWFMu+1WztZVJ+klS\nn8VlOkUFQ92bRETGDbGYeSa0dwyBJfG5PlrqD4Mr1kyQvyFVG9vsCxoLSN9UmlZw\nmxDbARzxPj2XCCcS/M03g8dYWAfERTXm/d1CJVCyl6HaNS3yox0RNT//yABbsOHC\nUWr+9Mk6N81cmVE4IDzu/NCMm9rhtjpd4Zo4Z6kq88KlNnX/N6A=\n=fTDE\n-----END PGP SIGNATURE-----", "payload": "tree 721e2530d6f00098bc3812c2ea9953ea3eea7e8f\nparent 65dc466c07f51e85542635e674ab0e9a09c7f91f\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1736638044 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1736638044 -0800\n\nv2.15.1\n", "verified_at": "2025-01-11T23:28:15Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "html_url": "https://github.com/Tautulli/Tautulli/commit/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "65dc466c07f51e85542635e674ab0e9a09c7f91f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/65dc466c07f51e85542635e674ab0e9a09c7f91f", "html_url": "https://github.com/Tautulli/Tautulli/commit/65dc466c07f51e85542635e674ab0e9a09c7f91f"}]}, "merge_base_commit": {"sha": "a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "node_id": "C_kwDOAducuNoAKGE5NmZkMjNkNzJmNWFhMzA0MmQ2ZjlhNTVkODA1MTY1M2ZmNmYwYjg", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-01-11T23:27:24Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-01-11T23:27:24Z"}, "message": "v2.15.1", "tree": {"sha": "721e2530d6f00098bc3812c2ea9953ea3eea7e8f", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/721e2530d6f00098bc3812c2ea9953ea3eea7e8f"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmeC/l0ACgkQsfH5gHGE\naXoSXRAApHHOvJjPaIteCSyzf58svXlM2SsJMABS87N4gHlYkvitb8Pf6gYyOeu3\nGD/cd5tIH3Xkt4p/G9gON79ChCVQEQUVZFYb8wyaAO3EFwlQ+5kZi1CWFzHlD6Ib\nKB2XPXfGW/gLXNuP6mWy/QP50tZe7Ryvv2loWQNlKCjLrxeGvJ/rPKjtsNmp2lej\nITRbc2cDVUHfrqWbGhGSpPfrVRRfDBeLcuBlzO7/u6V8hW6L49P86Qhs6YrfSwCX\nRCTzaREMTgHwbUlm/2MKLpOwuZ9qp0vG7Kxhg8pe76BIfR5Zulgmn1rxB4Mw3/vy\nXOmIWxAVePRDoM2R5OVFXAg4EJ5oPCY2ymxDnIiPLRy7WD59xm1dTyoYP7omhvIy\nCGND5H5T6rLuC8SfR4svohAlrSVjzYa9DChTaQMk0eU565ph8UH7vAsFGwjkHL4b\nFSCUMM8DZAc812NfNGDMacxheZYIDC9e7oUkVYsb+/jmxFueWFMu+1WztZVJ+klS\nn8VlOkUFQ92bRETGDbGYeSa0dwyBJfG5PlrqD4Mr1kyQvyFVG9vsCxoLSN9UmlZw\nmxDbARzxPj2XCCcS/M03g8dYWAfERTXm/d1CJVCyl6HaNS3yox0RNT//yABbsOHC\nUWr+9Mk6N81cmVE4IDzu/NCMm9rhtjpd4Zo4Z6kq88KlNnX/N6A=\n=fTDE\n-----END PGP SIGNATURE-----", "payload": "tree 721e2530d6f00098bc3812c2ea9953ea3eea7e8f\nparent 65dc466c07f51e85542635e674ab0e9a09c7f91f\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1736638044 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1736638044 -0800\n\nv2.15.1\n", "verified_at": "2025-01-11T23:28:15Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "html_url": "https://github.com/Tautulli/Tautulli/commit/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "65dc466c07f51e85542635e674ab0e9a09c7f91f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/65dc466c07f51e85542635e674ab0e9a09c7f91f", "html_url": "https://github.com/Tautulli/Tautulli/commit/65dc466c07f51e85542635e674ab0e9a09c7f91f"}]}, "status": "ahead", "ahead_by": 151, "behind_by": 0, "total_commits": 151, "commits": [{"sha": "2ed603f2886caa301ae11701fc7358374183766e", "node_id": "C_kwDOAducuNoAKDJlZDYwM2YyODg2Y2FhMzAxYWUxMTcwMWZjNzM1ODM3NDE4Mzc2NmU", "commit": {"author": {"name": "luzpaz", "email": "luzpaz@users.noreply.github.com", "date": "2025-03-16T19:25:29Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-03-16T19:25:29Z"}, "message": "Fix typos (#2520)\n\nFound via codespell", "tree": {"sha": "818f05878d1c0d63b51a640de74240b3f871509e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/818f05878d1c0d63b51a640de74240b3f871509e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/2ed603f2886caa301ae11701fc7358374183766e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJn1yWpCRC1aQ7uu5UhlAAA22QQAArq18hBF25T0DoriKcW+ZTG\njjDSXI6kgJXfpLuTQu+QRlo7+Qi/YE0Wd2ABwwZY3tTF5D5UeIIwggOSKaCst4rJ\niOGSxH5D+/ZZu+csYYS35Jg77uMXXHTlNcaQN74J/nbYz7BVQ3UYvllgJRxCIETQ\nGreeysQycg8bOTuItK3C+TP218/OUTxAdwXjZvHCIbcnWw4D4EgOy+p5VJOIMikO\n+EmAQ0EZv3/OXyNpH6a3HrrnmitHe9WK+uKp6oD0s0DwHJWY5eUv+FQiheWstAnK\ngS4u35WH6Qy5AsjFg8kAjMYN55KqGzkgy3MKbT6xkxSk09pO+gy1RNEYDfTiuzk8\nEhqAsJMXab8SZEHJ/SrDV2RHAfioKuu/y6ZgMwR1YFxSCxfX4ehheBlELulrI8vb\nd972PMl3qRd3Om241TVBTM+te8M24ip9qeFETcGdGqo3GNXw21lQ8uefrEtXMl9s\njF9Zkd+6Fw42A/D+5cRrsQ5ZNwS40YQ18dbLa5rdv1dw4kPJIHTJG3feT5LrJzHg\n4o0UmnPy2maLwFJNAHrwT+03pzSBZw3xMpvwB2Poiv6qeoTVYp4ceNq/Xy0esP8+\nBOeOMjdP+PklmsRd6/gVZqZA2huh1N/ct4SMM1ZrtyC0ApGWrpGIkxgdPU22N1G7\nZvKLKo/q6ApOknc2gP/z\n=x+gt\n-----END PGP SIGNATURE-----\n", "payload": "tree 818f05878d1c0d63b51a640de74240b3f871509e\nparent a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8\nauthor luzpaz <luzpaz@users.noreply.github.com> 1742153129 -0400\ncommitter GitHub <noreply@github.com> 1742153129 -0700\n\nFix typos (#2520)\n\nFound via codespell", "verified_at": "2025-03-16T19:26:04Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2ed603f2886caa301ae11701fc7358374183766e", "html_url": "https://github.com/Tautulli/Tautulli/commit/2ed603f2886caa301ae11701fc7358374183766e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2ed603f2886caa301ae11701fc7358374183766e/comments", "author": {"login": "luzpaz", "id": 4140247, "node_id": "MDQ6VXNlcjQxNDAyNDc=", "avatar_url": "https://avatars.githubusercontent.com/u/4140247?v=4", "gravatar_id": "", "url": "https://api.github.com/users/luzpaz", "html_url": "https://github.com/luzpaz", "followers_url": "https://api.github.com/users/luzpaz/followers", "following_url": "https://api.github.com/users/luzpaz/following{/other_user}", "gists_url": "https://api.github.com/users/luzpaz/gists{/gist_id}", "starred_url": "https://api.github.com/users/luzpaz/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/luzpaz/subscriptions", "organizations_url": "https://api.github.com/users/luzpaz/orgs", "repos_url": "https://api.github.com/users/luzpaz/repos", "events_url": "https://api.github.com/users/luzpaz/events{/privacy}", "received_events_url": "https://api.github.com/users/luzpaz/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "html_url": "https://github.com/Tautulli/Tautulli/commit/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}]}, {"sha": "f23d3eb81c5057633eb104963d50ccccf14a2ade", "node_id": "C_kwDOAducuNoAKGYyM2QzZWI4MWM1MDU3NjMzZWIxMDQ5NjNkNTBjY2NjZjE0YTJhZGU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-01-11T23:42:15Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-16T19:26:28Z"}, "message": "Fix changelog username", "tree": {"sha": "69c549ad90994e8c851613fffed7932979ee6092", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/69c549ad90994e8c851613fffed7932979ee6092"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/f23d3eb81c5057633eb104963d50ccccf14a2ade", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfXJeUACgkQsfH5gHGE\naXqi7A//aiT4BJxeMtcD4cN4pvktZ+OFBQmDDhWakW5skREpLVnCniTP8P3Bp2ge\nsZqM0yr8s2G3J9nU24WP3Ckq07uqJBjLiTTb5O+o1yOhYQ7MQfa3Xc0GtAV0K72l\nfKM1glh3RkEvvBEDJFAl9lZM9EyOPriiEKW7SvfJqcbn7KaClR3Ji4QUzV4QEtRI\noUrz10VOzz5AIDDsdC/3c0TceS0d+mRoFG8E0FhpYTxjNOdTnl4X+IXrJSqzx4hx\nkJG21KsVAwNHOLDmRM8JURN09SLS/lVwx8kVmalgPKrFGr+eJKaushQW+SJHIsqh\n37pwp4zQbJK9GofaoEi9pW8KGd0E/uddgvINivocRJFFBiX5PECBEO2Zc7g5FTbh\n9/FEpNpT7rbVHRtMktnamNHu52PR/u6DCn5rOjsjOgD39aP0r1VZqORSeE6ol2+v\nEiLfGf6g33HBe4za7o1Nh+9R/gfXFq6QMt83USQ1ao4fE1a2RR5u88usA8JmQPGk\nMkoK9OkzRoTr/nXGp4iX2xw+RF3Wn2JppSWoKGGEUoCuNvzn6m5scXBloIiLllQq\nc/C2fPDy4YyjkqWNLkEbv9e9CKOUX5clAScIm5EI+abry8tNzhX2uox22yv0Lzz0\nMbEYoF6a4RWQHeRR1ka3g7Rh/C4vPnbk6LIf9x9Nn7Cz+MlRiG4=\n=LgBi\n-----END PGP SIGNATURE-----", "payload": "tree 69c549ad90994e8c851613fffed7932979ee6092\nparent 2ed603f2886caa301ae11701fc7358374183766e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1736638935 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742153188 -0700\n\nFix changelog username\n", "verified_at": "2025-03-23T23:13:22Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f23d3eb81c5057633eb104963d50ccccf14a2ade", "html_url": "https://github.com/Tautulli/Tautulli/commit/f23d3eb81c5057633eb104963d50ccccf14a2ade", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f23d3eb81c5057633eb104963d50ccccf14a2ade/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "2ed603f2886caa301ae11701fc7358374183766e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2ed603f2886caa301ae11701fc7358374183766e", "html_url": "https://github.com/Tautulli/Tautulli/commit/2ed603f2886caa301ae11701fc7358374183766e"}]}, {"sha": "c70381c3ff4dec8d62d7e35be811ae93a139f05a", "node_id": "C_kwDOAducuNoAKGM3MDM4MWMzZmY0ZGVjOGQ2MmQ3ZTM1YmU4MTFhZTkzYTEzOWYwNWE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-01-23T21:30:45Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-16T19:26:30Z"}, "message": "Fix ntfy notifications not sending if provider link is blank", "tree": {"sha": "bec16f30889ffe5d69db5dc942e25545c9b2531e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/bec16f30889ffe5d69db5dc942e25545c9b2531e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/c70381c3ff4dec8d62d7e35be811ae93a139f05a", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfXJecACgkQsfH5gHGE\naXrDuw/7BVzr8085els15mZFS9AsSrQ/zccsh1p/zvsWQSrtfZoiX/mOARrYvF9B\nV5TxzHw6jmAYKQuzQr9g+aSkjPamxu6wMdVU1BZoosNRPCS6bhJFgyjI9a1Xe1FG\n0m/GOuwkZF5CkYHtRlfJQT+sf6trYElzKrdx0TkTIGLIb5ap4rt+ICxhft8P1UTI\nxka63aCgR1jxC3Ag+rnsNXkOOlR/+lPqVQvPUY6EtDa5fQ6X+ctr6rsj/N/hVjWA\n5K/c7tSBzygpGP25I2qq/lRQePX//VMVBtbeqhMckJvZLNTVJzl1B0Zb/Wj+pm6n\ns150a8HOQ4RLR3c6iVlHNVQz6sDrS/cy0gP010Gi8g61F6oHcDl9Szqw2nUKzYup\n2hkOsD/8x5ZGtxIFJ1JU7XWsSxOs6aqX6/kWSFVCXD2Bv+jLNAZOneYKTB1tQ2Q5\nzBujp/JOJpEVXdQqx6SlE2oCge7mclphrRdmgk6F8z3C3JzfIZgWrLl7iZ/7uulU\nlJw0tZvzNNmdzkzdSp79Pa8VuazhtrG+YRnKnVRR7TqcLnARiKeG3gk53v69UgRd\njaqZs4v+CVeuBi9zA64E85rKtIpgsxFFBYqA1qNZSflvdnliQ/rhAMYGVOBaBHni\nr7aS2UoN7YvZxMQRewfMkPDs/9dGAjhYkxEOXOKdoKmxRe4Xop8=\n=pizG\n-----END PGP SIGNATURE-----", "payload": "tree bec16f30889ffe5d69db5dc942e25545c9b2531e\nparent f23d3eb81c5057633eb104963d50ccccf14a2ade\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1737667845 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742153190 -0700\n\nFix ntfy notifications not sending if provider link is blank\n", "verified_at": "2025-03-23T23:13:22Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c70381c3ff4dec8d62d7e35be811ae93a139f05a", "html_url": "https://github.com/Tautulli/Tautulli/commit/c70381c3ff4dec8d62d7e35be811ae93a139f05a", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c70381c3ff4dec8d62d7e35be811ae93a139f05a/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "f23d3eb81c5057633eb104963d50ccccf14a2ade", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f23d3eb81c5057633eb104963d50ccccf14a2ade", "html_url": "https://github.com/Tautulli/Tautulli/commit/f23d3eb81c5057633eb104963d50ccccf14a2ade"}]}, {"sha": "0879b848b970ab05e67ea843bca49f607a8b0a5e", "node_id": "C_kwDOAducuNoAKDA4NzliODQ4Yjk3MGFiMDVlNjdlYTg0M2JjYTQ5ZjYwN2E4YjBhNWU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-01-24T18:54:15Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-16T19:26:32Z"}, "message": "Add link to library page from activity card media type icon", "tree": {"sha": "562fb0e201a68cc5896f97fbe6a955d558169fac", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/562fb0e201a68cc5896f97fbe6a955d558169fac"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/0879b848b970ab05e67ea843bca49f607a8b0a5e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfXJegACgkQsfH5gHGE\naXpBLw//UzF/ys5Qmog+TwayI6icpsk5fW9sKNKOECjJ/W6hAUMoK6sNbskIOdfb\nxr0MYI9GKa+euR/uRDEgctUuJdo2iPEZOFBBGrBr8/fa5NkKEnHjev/pOCIYZkbZ\nIL2wwV1d+0UOO/LZKQlzgdMAuigvDAbkNpvfXwKsYrI7NOTXsX1v6lT21R02pR7R\niigNdkzS4QJAOCn7C6zfiDjHA4LHiOFSx/cuscy3LWQGHAvVcS532EBR/sy5u3OV\nAS0Q/2YHQ531ZUOlql2tnu3UXhO6KbVB599urWd+UZB33U5r659uCKTJZSKEs1U/\nRd3vTnN5E+39JnvWyf77tfCmPdjHNdDd9CoLKbrfr3XvQjGtYlnToDfSRZ+CFRYT\nuiMf5VfULt5XWVSsO8K4tpltjpn406GCnoc9Gqza/Avhg8UklaYZhYTZNoz41L22\nUds6tp7x+RYk6SJAEJqmKkHeFxOVHKvw4hSBusUUS08tMy8z261XRhpuCDveq3rr\nTXDiGyP9sIFuAZ07ZzWKcoVUWybzCMOF2wJjUz3M3Z7BBQwl0MQjCkSZZFAYIWDI\n80DVe/1hnYblrS3ogAcRu8HsBVKMQ0EDHwKqOFFZR+hdYO1zeWcqJP9oOHk3C+AU\n1ECiWkYL7mTRwYpHew/1w7KUWJO1rQd3r8GeKpi4Q6tBDiLOKIw=\n=FnP7\n-----END PGP SIGNATURE-----", "payload": "tree 562fb0e201a68cc5896f97fbe6a955d558169fac\nparent c70381c3ff4dec8d62d7e35be811ae93a139f05a\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1737744855 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742153192 -0700\n\nAdd link to library page from activity card media type icon\n", "verified_at": "2025-03-23T23:13:22Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0879b848b970ab05e67ea843bca49f607a8b0a5e", "html_url": "https://github.com/Tautulli/Tautulli/commit/0879b848b970ab05e67ea843bca49f607a8b0a5e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0879b848b970ab05e67ea843bca49f607a8b0a5e/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "c70381c3ff4dec8d62d7e35be811ae93a139f05a", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c70381c3ff4dec8d62d7e35be811ae93a139f05a", "html_url": "https://github.com/Tautulli/Tautulli/commit/c70381c3ff4dec8d62d7e35be811ae93a139f05a"}]}, {"sha": "53e5f89725b7a174f2e2afb2b6134f60de1348ea", "node_id": "C_kwDOAducuNoAKDUzZTVmODk3MjViN2ExNzRmMmUyYWZiMmI2MTM0ZjYwZGUxMzQ4ZWE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-13T04:39:36Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-16T19:26:33Z"}, "message": "Add audio profile notification parameters", "tree": {"sha": "e39bcbd9a5a1438aa6ff9657c2945f8b97233d87", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/e39bcbd9a5a1438aa6ff9657c2945f8b97233d87"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/53e5f89725b7a174f2e2afb2b6134f60de1348ea", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfXJekACgkQsfH5gHGE\naXqsjQ//X3Cpt1lEr1hTF41uMzXhqDJSOMB7eE51LMcXMieSHSyPopGCZ3ALOR/y\nQirxBzTwtTU3e2H+xYJr7y5D4W4xrS+nk3xMZ+t8xDw8DYnc55KXDTZcHKz7WGeK\nj/IzIIDXQgtIthIeEGamdQ6oaL0YLQmBS1WSOa4rdm5hYYwpdCOSq7dHwdbSo37g\nlUVPnIEXvRXizQvIcZmaCqk1Rms0qqevaz4eouzprt8r6YrcA8L4z/Q/hJSIAD3G\nBk31VZSkqHRPtOu2hoKf2wmZvcmdJ2Wh2LH/YnCz1Yu4XZzM/BT7Uhalc+WoY5Yo\nFBguAGcUojY4CjuPV4qG/fMernSsuyWSXR6Ylchgmf5/JrwIm5Bu75rSVp8g5s98\nd8SsFbXwvVTnlS62S0IinXU0grr5FwRj0GRNuYPFifSAT3bM4sZm8jZd4h7RhOCx\nUZzch0PuqruRAH5Vp4bf6Kl4XSSNdTGX+VccomExRbvEgzzo22siTBSeyBmmaEIC\nPJlQ7pRzmlXsZ09gBhf7ED/9+nKtPyIUyyAb+Ub941g+pnhNQCHyVcmZNahyke9G\ndd9vutVZ5EFfhj2WgbHzCgOP2xQ3pTh9SAh8Oau1AhjBcEumSRrt/M90AKMlQ8X2\nSyE3eOMXPlM8j2f1VVGUaFX1Tq5+y7iD5ndtXX7T5x5FYqaPpIc=\n=rEC/\n-----END PGP SIGNATURE-----", "payload": "tree e39bcbd9a5a1438aa6ff9657c2945f8b97233d87\nparent 0879b848b970ab05e67ea843bca49f607a8b0a5e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1741840776 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742153193 -0700\n\nAdd audio profile notification parameters\n", "verified_at": "2025-03-23T23:13:22Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/53e5f89725b7a174f2e2afb2b6134f60de1348ea", "html_url": "https://github.com/Tautulli/Tautulli/commit/53e5f89725b7a174f2e2afb2b6134f60de1348ea", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/53e5f89725b7a174f2e2afb2b6134f60de1348ea/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "0879b848b970ab05e67ea843bca49f607a8b0a5e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0879b848b970ab05e67ea843bca49f607a8b0a5e", "html_url": "https://github.com/Tautulli/Tautulli/commit/0879b848b970ab05e67ea843bca49f607a8b0a5e"}]}, {"sha": "e4d181ba5b7eeca08a488d358004fd65dbd82ab0", "node_id": "C_kwDOAducuNoAKGU0ZDE4MWJhNWI3ZWVjYTA4YTQ4OGQzNTgwMDRmZDY1ZGJkODJhYjA", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-13T04:40:38Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-16T19:26:34Z"}, "message": "Add PATCH method for webhooks", "tree": {"sha": "d08a712922a3fa327267e029780453a74eb3a318", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/d08a712922a3fa327267e029780453a74eb3a318"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/e4d181ba5b7eeca08a488d358004fd65dbd82ab0", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfXJesACgkQsfH5gHGE\naXqQUw//UTo/xmBRO8SlG4oEXICpRSASj3rksWsswPfynuiOsniGs2TV78VC0A0w\nREMUnwFjY4OBMUtcnqdS0T/PfwyvqspG7j+4p8eCowUr8PjPunw7P9XFSqg7TCMc\nPl4efdV6oJbzMqUP7ONc8NDzVvwEXKqhckRso5dQnboE/wqStuN1Ob5eg3tamlgX\nZU9lrbgyUueKJHdkKE2yyD/s/Y27zbeCHdZi3JiPRLHfQy6AFyu8++hCfgG58mG/\nY3Xw5CEs3X06pzipvOxint5ncefo1BFIRBp1vi7In9U1Xxy6wPpnNAJSKzIPpkXr\naR2RHxITV4bZGqhqYa3cy19Aysk/ESYcFRpyruOjnOWvKSQUiwHh+ElQBgpWZKrD\nNjdy3U8PIFhf8WYg4Tb5LlmaFE15Ek1eA2q2szXRM9ULFh4/PIBM4+ut9rdmvEgV\nWD3qkEg63A0iHMITfeJAe5nToQt/DKUCtONioTvBBTwUnZ7EZT26XiPhsgIonDer\nhekcs0x0KYRDK/XDFOLtZQraxRm+JB8Mov/w7jt+Jy4M5hlGmwb1snYZGjlvAnjv\nt1irEHwWt1K9XV5LtVVZjicgah2o6apE9TxGJely45i393dijoNwqZK7l6u7u0P2\nMiMspPza1FrYyxtxw7M2pK0BgMOZRevFZPCIQt9aBnwJ5+TyTv4=\n=ArE3\n-----END PGP SIGNATURE-----", "payload": "tree d08a712922a3fa327267e029780453a74eb3a318\nparent 53e5f89725b7a174f2e2afb2b6134f60de1348ea\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1741840838 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742153194 -0700\n\nAdd PATCH method for webhooks\n", "verified_at": "2025-03-23T23:13:22Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e4d181ba5b7eeca08a488d358004fd65dbd82ab0", "html_url": "https://github.com/Tautulli/Tautulli/commit/e4d181ba5b7eeca08a488d358004fd65dbd82ab0", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e4d181ba5b7eeca08a488d358004fd65dbd82ab0/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "53e5f89725b7a174f2e2afb2b6134f60de1348ea", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/53e5f89725b7a174f2e2afb2b6134f60de1348ea", "html_url": "https://github.com/Tautulli/Tautulli/commit/53e5f89725b7a174f2e2afb2b6134f60de1348ea"}]}, {"sha": "b3aa29c677bfeb6e29c63e5e28b3c59579d75cb6", "node_id": "C_kwDOAducuNoAKGIzYWEyOWM2NzdiZmViNmUyOWM2M2U1ZTI4YjNjNTk1NzlkNzVjYjY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-23T23:05:01Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-23T23:05:01Z"}, "message": "Swap source and stream columns in steam info modal", "tree": {"sha": "82104575e0ee16737a79b89f027a0781a80e69d2", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/82104575e0ee16737a79b89f027a0781a80e69d2"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/b3aa29c677bfeb6e29c63e5e28b3c59579d75cb6", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfgk6EACgkQsfH5gHGE\naXoaDBAAmWetxzPutjDgbUljfXGFoWt6A/44OkwLgXu5r2PpGSdpO7+KHTgv5NGx\n8xaveINWJdY+BgH/Fz8LuH9pBITy1cGwL42g0HgalXC3aPDiG57HcAupdYmWfLS0\nxMr74C58m5GVEXNd/GqtFeIK8c3HbnLJIzLN3Q3DOikodoXUPGBv2JPK2yJ25Dnn\n26ISBmwUNakbaOWuisER3Ydeh09up18zUtCDpUhNKaAcAwglxKsErNPHClogbgUN\nPBtmt+1YUE6tg2KSwQ0DFd1X5EhlTMP1++f+WxTIQaOCsZHIV9kwsW6ZRTKfLPKd\nlLG6H74RsKcWk7TG7zakf8Cr0RB07J/Dohm1exZrmOVfTNuXRVqTKhZ6DhVMp42G\nIkreFNUjhPubmWQ2QZGkcou5U4gBlayNou2huEbvERib049zIIUNafjQxioUXzXs\nkcBjHeC3XGZPjYiAy8jSs0+SkpHdMZSaC3tbMoEqHq+Njm3KWKNu8rlS25AeLIsq\n7HE0RfQJu6oRz7gfcxFfs9Bon2rurcBK4914RcTA8BE+Yb7lcuWMKijC6E52BbzP\nbzuGYah1lObxDgQzeE7h7JMxzfT4EzZkBem85hUp+o/xF3GbFO0U+y7WFqUdFFeY\ntqTfvTdBrD5hN1TsLurK2L7Jw/1QWuNdq8XKOYElQQsfdkWFzUM=\n=g2Eq\n-----END PGP SIGNATURE-----", "payload": "tree 82104575e0ee16737a79b89f027a0781a80e69d2\nparent e4d181ba5b7eeca08a488d358004fd65dbd82ab0\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742771101 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742771101 -0700\n\nSwap source and stream columns in steam info modal\n", "verified_at": "2025-03-23T23:12:32Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b3aa29c677bfeb6e29c63e5e28b3c59579d75cb6", "html_url": "https://github.com/Tautulli/Tautulli/commit/b3aa29c677bfeb6e29c63e5e28b3c59579d75cb6", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b3aa29c677bfeb6e29c63e5e28b3c59579d75cb6/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "e4d181ba5b7eeca08a488d358004fd65dbd82ab0", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e4d181ba5b7eeca08a488d358004fd65dbd82ab0", "html_url": "https://github.com/Tautulli/Tautulli/commit/e4d181ba5b7eeca08a488d358004fd65dbd82ab0"}]}, {"sha": "cfc7b817b3fd092330192176e37186f82e110b7e", "node_id": "C_kwDOAducuNoAKGNmYzdiODE3YjNmZDA5MjMzMDE5MjE3NmUzNzE4NmY4MmUxMTBiN2U", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-23T23:19:54Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-23T23:19:54Z"}, "message": "Downgrade pyinstaller to 6.10.0", "tree": {"sha": "83907899f2a7ab019eb910de876a1b8ce5a1fcf3", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/83907899f2a7ab019eb910de876a1b8ce5a1fcf3"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/cfc7b817b3fd092330192176e37186f82e110b7e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfglxsACgkQsfH5gHGE\naXqT5g/+JhdC0Jkn/31uhEGA+ow0Op+HtgpEoKvr7OX+qEvwQV2DGXPk9olpWxTo\narDEDtsRYznct1bhUeZ2E8kyTMz5gaUG3Vw8Lt8qEFAGi6lFi13n8yglkPFMIF/c\nQO9WSMDPB9x/LCaKDEG669wF6oZqtkY8hs6VwZdJVlfu80MjxNtrugpE2qZvTS0F\npnoDabCGx2pbXhNYvbm4g7cOaupwbQE9NFCTkLcaU83R1oydsVWgxr2hCYT7GhCo\ncPAj5MgJ9rnSxyF8Rgrzsd2kaOrvsfW2bub8ymVTqKI6ySWb/J7NXs+ijTE2Mbxs\ny7/diOUSASw/b/yDkALG7OCYpAO+FOs7iHYnF7sCWsFTNTORUiibeL3b9Yn/tQ5J\nk2HBrj5T9Xq49aHBWl7zz4ytIau+CbLwpGSmunbEKhc1lO+yhRJMFoRrGbL8D25q\nv60+zUxSjKNul4/M/rcnvHZ5FE4PE8YANqGloThbJLwm0RMVcl76Vo+zEX3Sf7N6\n/sED/H+mzB1RpjmQkavHLNJIbv8VK2m6fYy7XcBUA6rCc2JGIg3Q3bf3ikkPkBsQ\nRnkMZItH10ztwGFMuA0BwZVTuIhs0jYIkPmckNqnsLjTHkJ2UtkbqcOOiJAmH29e\nozpIU6Y/A9qV+XMOTCf2AmmuE89g4NHfjexfru9oU9xxlBBGe90=\n=JEye\n-----END PGP SIGNATURE-----", "payload": "tree 83907899f2a7ab019eb910de876a1b8ce5a1fcf3\nparent b3aa29c677bfeb6e29c63e5e28b3c59579d75cb6\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742771994 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742771994 -0700\n\nDowngrade pyinstaller to 6.10.0\n", "verified_at": "2025-03-24T03:44:17Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cfc7b817b3fd092330192176e37186f82e110b7e", "html_url": "https://github.com/Tautulli/Tautulli/commit/cfc7b817b3fd092330192176e37186f82e110b7e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cfc7b817b3fd092330192176e37186f82e110b7e/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "b3aa29c677bfeb6e29c63e5e28b3c59579d75cb6", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b3aa29c677bfeb6e29c63e5e28b3c59579d75cb6", "html_url": "https://github.com/Tautulli/Tautulli/commit/b3aa29c677bfeb6e29c63e5e28b3c59579d75cb6"}]}, {"sha": "09c28e434dd46b954a7149a0200d41c0a3d28a7e", "node_id": "C_kwDOAducuNoAKDA5YzI4ZTQzNGRkNDZiOTU0YTcxNDlhMDIwMGQ0MWMwYTNkMjhhN2U", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T01:12:08Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T01:12:08Z"}, "message": "Check Pushover attachment under 5MB limit\n\nFixes #2396", "tree": {"sha": "02060d098e27438732061e797ed6d2e6ea858ddb", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/02060d098e27438732061e797ed6d2e6ea858ddb"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/09c28e434dd46b954a7149a0200d41c0a3d28a7e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfgssIACgkQsfH5gHGE\naXrZbQ/9Edkhp14nYWZUkiohw8etzXyfXtQo4P+FtV6xYw2H8EpAch8QyyPdJl0v\nEC3K73LiXIqorHdKi4QOYY/eq25s1I0sn8AnbTuin4y2ZPFJN9QEibK0sez9kqoe\nR3iQaNN5SaaXr7lPXMyq3TQGyvKCDhEFYi/d4Bq73jhZDIsHbfhG05uxAlzwohBB\n9eOjvgIGcCorFJiU7JriSOqUgleaOuHZ06oWJksb/9W18Bla7ESwzLCziVUZRgkp\nOZ6EgEMEkjTqSwnfghMFWG3Bp4JmHakHq5g6coRtiPloGrCfgx7cv5gs9Sqs9ZLZ\nWhTiWmBQFNU8BXX7dNsVjGr44JV+eT+GqqtXsW6Zoc8iAFJSmGUxiqDVKbWRbeJD\nJVFRKxfd9rYP2t2ITScNSPoVh73LMkIpvg4TgYTEGrMoeVzi/ZmciqJLM0cdtT3e\nEGWsuXdnrwNFvzOPmF7wswF3zz8tH8bJxK0IGqyt8S9JZVUY39z8c3iYbfaOBB9y\nx3iZtk6GHIYh8wxVRYtpycWy6l57Y6BD8Jpquhsom/JQLjtcC13E7HApRaR8zm8I\nbP10Gt/RXAqf65bTo3sqJjPkd9uJn7JF1RFwN1TH0glWGhqBDwbN7KigWj8zsedu\ncODwyz+dH7gikyLRcE8DvwQv2lApXWASNKkxoWkyMLxnU8D98Sg=\n=fYaB\n-----END PGP SIGNATURE-----", "payload": "tree 02060d098e27438732061e797ed6d2e6ea858ddb\nparent cfc7b817b3fd092330192176e37186f82e110b7e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742778728 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742778728 -0700\n\nCheck Pushover attachment under 5MB limit\n\nFixes #2396\n", "verified_at": "2025-03-24T03:43:51Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/09c28e434dd46b954a7149a0200d41c0a3d28a7e", "html_url": "https://github.com/Tautulli/Tautulli/commit/09c28e434dd46b954a7149a0200d41c0a3d28a7e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/09c28e434dd46b954a7149a0200d41c0a3d28a7e/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "cfc7b817b3fd092330192176e37186f82e110b7e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cfc7b817b3fd092330192176e37186f82e110b7e", "html_url": "https://github.com/Tautulli/Tautulli/commit/cfc7b817b3fd092330192176e37186f82e110b7e"}]}, {"sha": "ad2ec0e2bfd74bb22b5912dba6f75fa3345ad52d", "node_id": "C_kwDOAducuNoAKGFkMmVjMGUyYmZkNzRiYjIyYjU5MTJkYmE2Zjc1ZmEzMzQ1YWQ1MmQ", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T02:44:10Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T02:44:10Z"}, "message": "Fix CherryPy CORS response headers\n\nFixes #2279", "tree": {"sha": "3be1d707ede54d69d62d0568b9192ccd6b11d271", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/3be1d707ede54d69d62d0568b9192ccd6b11d271"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/ad2ec0e2bfd74bb22b5912dba6f75fa3345ad52d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfgxvsACgkQsfH5gHGE\naXr4BhAAhqZIvpcW6TfmPFLksguW6w18q/Xh2mwg3aPVMO8iUhZgw2KEOeF6JC35\nIqC6bGYG2WpLhlhQqKGTo0vR989yQo2K7/RW4pe2c8nDwMzzkxzVeYTkH0t9lb9D\niwOkKHvUnCLesn07L2Y9kqBIaoTuWsT1tKyxnZ33HX7apD9RJ3K/ZJmzOQ06t/+S\njUu/4oRTDAoYaQ/w71Rr0D0LmGp5dBMxHwsXhLjxli8zsYDsWmEi2NpXh8m8/NxN\nlogDHjcck31c3raGCwmQm9S7Kkt10fXDpVWFDT5zf5OtC7pyrbDkhZGr0OJTdFkO\ngBAQYuosQLJlKgKA011zWd/OoXrktYm8acBZ12r8h24JcIaHXaT4Dex/rVwUdIKz\nJWWGjEkFsJ30CQBBjeuZmbPqWq6KTwsylFFvC0AcLFq3yaGcj0gvYtGXZHkJexQL\nfDgwfUqqzy/ZmN8okgxEU453WWBry0wTATJu9Kk19UyaaSnb/tnU0YL34nfZEQtR\ndEA5P27yHNyPH2hvKUyhcpFKBs3h+EzZPcXHsM+NGuHn2lxVtICpUg8jyV1SW7K5\n3Uu2GHpQb7OZCkNWd7F1cHe7rfZJ1JlUJ+A46WgPGGckesmA2LfRxml8zx22s/Xq\naDimOJFGwz9BqNVwPoD4xPhzdcXV80WCoNOjJyK8jSORaDSya2o=\n=njpN\n-----END PGP SIGNATURE-----", "payload": "tree 3be1d707ede54d69d62d0568b9192ccd6b11d271\nparent 09c28e434dd46b954a7149a0200d41c0a3d28a7e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742784250 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742784250 -0700\n\nFix CherryPy CORS response headers\n\nFixes #2279\n", "verified_at": "2025-03-24T03:43:48Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ad2ec0e2bfd74bb22b5912dba6f75fa3345ad52d", "html_url": "https://github.com/Tautulli/Tautulli/commit/ad2ec0e2bfd74bb22b5912dba6f75fa3345ad52d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ad2ec0e2bfd74bb22b5912dba6f75fa3345ad52d/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "09c28e434dd46b954a7149a0200d41c0a3d28a7e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/09c28e434dd46b954a7149a0200d41c0a3d28a7e", "html_url": "https://github.com/Tautulli/Tautulli/commit/09c28e434dd46b954a7149a0200d41c0a3d28a7e"}]}, {"sha": "68dc095c835efd3e53e4bedc071cc66024541f22", "node_id": "C_kwDOAducuNoAKDY4ZGMwOTVjODM1ZWZkM2U1M2U0YmVkYzA3MWNjNjYwMjQ1NDFmMjI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T03:10:43Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T03:10:43Z"}, "message": "Do not redirect API requests to login page\n\nFixes #2490", "tree": {"sha": "d30726f904563f96fd06407ee38f0848f4ffca45", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/d30726f904563f96fd06407ee38f0848f4ffca45"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/68dc095c835efd3e53e4bedc071cc66024541f22", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfgzTQACgkQsfH5gHGE\naXqHxw/9HgqqThFoWFICHk2jYlaFIXy8BvEa5NgxQ1Gr8b611Vy6v07byHF13PBA\nQYU3D6MU2KbK3WTke0ErqTjPsVSSIVXTUtcf+omG5fVSRSsfN3eRN87uP3f1TwYt\nM8UxwYKXvHON66Aaj7mbqnIT/mVFWa8k8m7CW4ecddo5+38sRXrdXTMXBwQ2kiUJ\ny7003BXSeNiAo3PbviKXFjvEQW+PMry34lEMbAsCvF3347amxp1urEpd/D9GZrdJ\n/y2xOpN7PemDGHJKEyGtzHsh5+AttT4Yq4ftZPeWHqOLEWqo9/fXwj3Yjw3QeuAT\np0WKhxT2dDv9R9qQk3nrsnU5ZccaOm1ge30t2T2vLJZtHM3keOp3RTAeYrXVUH57\nQC8+EnTWIHWPum7L7czouLl+NgfXoQbYMGShpA97ZNOFgK3TRhtUI1LuN/2DEJIP\nU4umuCve14RwddnpEPKzGZbVmzCnH+lE7ce2dwRO58bgoWVPDKeLRindslFJJsm6\nrcFqcs8T+yMiUz49+QWKRLETLEqkE+cfWCmU9QXSHklK6kbfyk+SV2vSgd4HbZEu\nMwHmplT8vmA4DhFJWfDqP0QmTJx0JOh2hSCj8kR65no3/dAPMYvhAuxBmKkv20lX\nc052aXPTrVlgMaxwec71lbYMqq+gDipsDakfgX6ERgQt4Cs/K78=\n=DuOb\n-----END PGP SIGNATURE-----", "payload": "tree d30726f904563f96fd06407ee38f0848f4ffca45\nparent ad2ec0e2bfd74bb22b5912dba6f75fa3345ad52d\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742785843 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742785843 -0700\n\nDo not redirect API requests to login page\n\nFixes #2490\n", "verified_at": "2025-03-24T03:43:52Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/68dc095c835efd3e53e4bedc071cc66024541f22", "html_url": "https://github.com/Tautulli/Tautulli/commit/68dc095c835efd3e53e4bedc071cc66024541f22", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/68dc095c835efd3e53e4bedc071cc66024541f22/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "ad2ec0e2bfd74bb22b5912dba6f75fa3345ad52d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ad2ec0e2bfd74bb22b5912dba6f75fa3345ad52d", "html_url": "https://github.com/Tautulli/Tautulli/commit/ad2ec0e2bfd74bb22b5912dba6f75fa3345ad52d"}]}, {"sha": "3e784c7495f5f7ccd0c358e4c1583af3a996ece3", "node_id": "C_kwDOAducuNoAKDNlNzg0Yzc0OTVmNWY3Y2NkMGMzNThlNGMxNTgzYWYzYTk5NmVjZTM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T03:41:30Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T03:41:30Z"}, "message": "Check stream watched status before stopped status\n\nFixes #2506", "tree": {"sha": "49ec550537d897d98201723b20f6250c92a2bf97", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/49ec550537d897d98201723b20f6250c92a2bf97"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/3e784c7495f5f7ccd0c358e4c1583af3a996ece3", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfg1GsACgkQsfH5gHGE\naXrqIA/+OoVFfQhL6fwXSF1sSt43p1nqFdfn4v9MDdSYW06dU1RoqzcaAW+OghaM\ngrc3SRq3WmDVM98VjTZp1PpSgJaBlZ3EftiG/dbOorahlgrApXGTpOfq1SOtzTwA\nxyAmocZeQBRXJEkNdon/ZVVp0ygmws8WL7Q6C0ni1VIxdR4oncTkgKbGaXdvSeg8\n7tWusNvgpRw0IdBkItosTjyHgENnnkcanpQ4nfwYHgDTbWLjbGdQ61uFw0UIZdrp\nyqrYBk9vnB6FaqjyHLKNllgNEGI6sUvgzEpS99SD7zvD0zaiduzIeCHViAsnbCIc\nsGKs0AQZZb6euAHxP1PWYJDJtEFbXADzSnECDWgu7eBUDe68trf1JhCAJb7hYHWM\nZqsqBhe+sT6dcopTekNeSrUd3DjOfshg5z1/1ZQsMNJcNu0qbqxHzsxnzarzlNc4\nRW+OQt5K9nPFCq58O2lJg9DoJGZCZC809s2frewODLEtq19bfFw9pVkItj29QSSb\nJgRErI5Ai3f42drSnCToaDu4/xEd9H4gNf32ZUKQxlnxe46j3UOqYtl/Wk2Hv6SM\niT6ArcKBGf/+rpJqpRO43hU2xDPSnM9/zEWHiVfxW8Vx3NgH3EMoo5K1z3Dg/ia2\nFjUvjyuU85kXo3NfE1n7tW5EurINM1hzCluuM+fdvyytyzVhgvY=\n=4zLv\n-----END PGP SIGNATURE-----", "payload": "tree 49ec550537d897d98201723b20f6250c92a2bf97\nparent 68dc095c835efd3e53e4bedc071cc66024541f22\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742787690 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742787690 -0700\n\nCheck stream watched status before stopped status\n\nFixes #2506\n", "verified_at": "2025-03-24T03:43:42Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3e784c7495f5f7ccd0c358e4c1583af3a996ece3", "html_url": "https://github.com/Tautulli/Tautulli/commit/3e784c7495f5f7ccd0c358e4c1583af3a996ece3", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3e784c7495f5f7ccd0c358e4c1583af3a996ece3/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "68dc095c835efd3e53e4bedc071cc66024541f22", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/68dc095c835efd3e53e4bedc071cc66024541f22", "html_url": "https://github.com/Tautulli/Tautulli/commit/68dc095c835efd3e53e4bedc071cc66024541f22"}]}, {"sha": "99e575383cc631a45a756354b62488051000b31f", "node_id": "C_kwDOAducuNoAKDk5ZTU3NTM4M2NjNjMxYTQ1YTc1NjM1NGI2MjQ4ODA1MTAwMGIzMWY", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2025-03-24T21:04:46Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-03-24T21:04:46Z"}, "message": "Bump pyopenssl from 24.2.1 to 25.0.0 (#2482)\n\nBumps [pyopenssl](https://github.com/pyca/pyopenssl) from 24.2.1 to 25.0.0.\n- [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst)\n- [Commits](https://github.com/pyca/pyopenssl/compare/24.2.1...25.0.0)\n\n---\nupdated-dependencies:\n- dependency-name: pyopenssl\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "c4e3b702135bdff9a1cc0845aac70fa422c4ffa9", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/c4e3b702135bdff9a1cc0845aac70fa422c4ffa9"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/99e575383cc631a45a756354b62488051000b31f", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJn4cjuCRC1aQ7uu5UhlAAATTIQACRE6WwMxCu6oFAuav9c3L6j\nWXJDWZhqS932r4OJHnAnpwmK78kuSt/8PVHUZl8ka8rIrvUIoJfP0Xowwp593EhU\niPYD7JOhcMKVv2GWHoyewlSBBEvfop/zGsZQF7f5uBsCX/3kOftnt5T8Ii6J/dLI\nmWT59Fk5Oy8QhuNkmhS8Tl5k659mEsA5I0fx7Tio0MoOWRxOl1WMOqCMdzJaITCT\np6U+jrvZlyQl+sA/dWSPJl+06SNmInLQlg1JNz+ZblCMP2CpURuzO6lB3weGgzOu\neksPC6eCb8crYzQk/Xf1LBu4tq4CjA4xX3xhQqNrPx0ncuTDr5l3LShBkNTUFMEV\nubIjnE63d7tk0vuBM8z4cfQG0wnwmKobLIhf3YrlC2zx+QQv9cqWpkqkxAC1W0OC\nhpkJBXlukD3RijLJ7nOBUBEeyvOziXTQrj01ks1CCQ7L+EzgVt5Y3EdXkjniOOcB\nSQe+kc6CM/HUrfyqV7OGyXacSWYHfH/frr4apvCt6tEzKB44PNeKr9bsTYEL0whj\nNL+lF2SjlH+zt1dDjaNRkDaAe2szNqDYd+epiTpsPtHD4WLgFBnAts9C8vByMK9A\n5ybfW9ZCfqEYAsIaHahRat/z7LZH0q1W2n3WdE5hVOxKPVQcT1iq+kPcvbAlrUHV\n4+RJMEFqdNEoBjCCL+kP\n=JF6n\n-----END PGP SIGNATURE-----\n", "payload": "tree c4e3b702135bdff9a1cc0845aac70fa422c4ffa9\nparent 3e784c7495f5f7ccd0c358e4c1583af3a996ece3\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1742850286 -0700\ncommitter GitHub <noreply@github.com> 1742850286 -0700\n\nBump pyopenssl from 24.2.1 to 25.0.0 (#2482)\n\nBumps [pyopenssl](https://github.com/pyca/pyopenssl) from 24.2.1 to 25.0.0.\n- [Changelog](https://github.com/pyca/pyopenssl/blob/main/CHANGELOG.rst)\n- [Commits](https://github.com/pyca/pyopenssl/compare/24.2.1...25.0.0)\n\n---\nupdated-dependencies:\n- dependency-name: pyopenssl\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2025-03-24T21:05:01Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/99e575383cc631a45a756354b62488051000b31f", "html_url": "https://github.com/Tautulli/Tautulli/commit/99e575383cc631a45a756354b62488051000b31f", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/99e575383cc631a45a756354b62488051000b31f/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "3e784c7495f5f7ccd0c358e4c1583af3a996ece3", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3e784c7495f5f7ccd0c358e4c1583af3a996ece3", "html_url": "https://github.com/Tautulli/Tautulli/commit/3e784c7495f5f7ccd0c358e4c1583af3a996ece3"}]}, {"sha": "74786f0ed1563fbcc1b254e48a8f30e421830b16", "node_id": "C_kwDOAducuNoAKDc0Nzg2ZjBlZDE1NjNmYmNjMWIyNTRlNDhhOGYzMGU0MjE4MzBiMTY", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2025-03-24T21:05:05Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-03-24T21:05:05Z"}, "message": "Bump cryptography from 43.0.3 to 44.0.2 (#2519)\n\nBumps [cryptography](https://github.com/pyca/cryptography) from 43.0.3 to 44.0.2.\n- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)\n- [Commits](https://github.com/pyca/cryptography/compare/43.0.3...44.0.2)\n\n---\nupdated-dependencies:\n- dependency-name: cryptography\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "tree": {"sha": "126b517e4be75ce4e5f65eb4b5a11373512206ea", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/126b517e4be75ce4e5f65eb4b5a11373512206ea"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/74786f0ed1563fbcc1b254e48a8f30e421830b16", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJn4ckBCRC1aQ7uu5UhlAAAJV8QABAiVZpgTQugK2DmzKUQU6FB\np+F2J9KWo80WsneMIg5pAekNKXDHok+UAS+ujkz4B9hccJBL8TRD955DP605yGEb\nHA3PJV/2egS0sgGkcDzChBojxdKLwBA9HYSN4US0kf7lN7vaKNybnnzbXyJmdLqe\n8apZ6S9Hxx63pVNZRQCigHBpTxqWgbLFJ48OWlR5xFKkWBV1rEIanTYVJ3t2Xqs9\ndyGVXJRIjYuoCgrMYxkLyGqZlyACwxLhgL6bKGyzSK7472Owr4SRCDc1SOvdvnts\nFezbxv83ciC0dzMxJ9Qj6Cy07leKLmsvDWOOte7NHWZldZgWw6N6GgPLxdq5JGNm\nQ1Y6kZ75GR1W1i/8MUgKfR0tZd+Temk5r0VVKIpx+O2hqvyb+dM1rxhbeetu88Ig\nBuDKPzrl6zQ4MIN0iRt06RWeFMehoTBpZgNVdt4/RSC4ZYF7qx+ccA0IWEbB9j/H\niaOQhCy+I3z/3wfUJiuQaS2731kFAZAEmksNSBFYqFYJ7hNx1RM3zK6lZU2J1yDb\nZR8tXjDPHeVV1hSjxPYSJPPkSTi8+jdC20h0rS7eYKrFMBDEk1O0kZPv652hPYG1\n2xh0YuE1I/qR4TzRf+p1fDGmZSDVlsTKN7xkYO2Hfvl08GEQNOuyMc2PNwFQXfzP\nwzLx+lMmHuo9YF1DKGlI\n=0ewg\n-----END PGP SIGNATURE-----\n", "payload": "tree 126b517e4be75ce4e5f65eb4b5a11373512206ea\nparent 99e575383cc631a45a756354b62488051000b31f\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1742850305 -0700\ncommitter GitHub <noreply@github.com> 1742850305 -0700\n\nBump cryptography from 43.0.3 to 44.0.2 (#2519)\n\nBumps [cryptography](https://github.com/pyca/cryptography) from 43.0.3 to 44.0.2.\n- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)\n- [Commits](https://github.com/pyca/cryptography/compare/43.0.3...44.0.2)\n\n---\nupdated-dependencies:\n- dependency-name: cryptography\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "verified_at": "2025-03-24T21:05:18Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/74786f0ed1563fbcc1b254e48a8f30e421830b16", "html_url": "https://github.com/Tautulli/Tautulli/commit/74786f0ed1563fbcc1b254e48a8f30e421830b16", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/74786f0ed1563fbcc1b254e48a8f30e421830b16/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "99e575383cc631a45a756354b62488051000b31f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/99e575383cc631a45a756354b62488051000b31f", "html_url": "https://github.com/Tautulli/Tautulli/commit/99e575383cc631a45a756354b62488051000b31f"}]}, {"sha": "a876e006d6a8f0f1283629ef69a5651973a4503d", "node_id": "C_kwDOAducuNoAKGE4NzZlMDA2ZDZhOGYwZjEyODM2MjllZjY5YTU2NTE5NzNhNDUwM2Q", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T04:29:18Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-30T03:42:44Z"}, "message": "Fix Trakt URL redirect to media page\n\nFixes #2513", "tree": {"sha": "9acc57a7b54ebc339118869c0bedd8f0a7f5cc5b", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/9acc57a7b54ebc339118869c0bedd8f0a7f5cc5b"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/a876e006d6a8f0f1283629ef69a5651973a4503d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfovb0ACgkQsfH5gHGE\naXqK8xAAlysRNCXEZwb24Epw7OYrxNQWeutZYuJx9Vmm5ilVArgMIFbJa4J9cOi4\n3y7MdsQEZHHDB/2LfytJ2bv+vVBIREHwlNX8JnjgBFlVoC1NQu6J+IqJAdS6tftn\nrrtoOhiShL0ySayLd1w0knWWyrnEsaYm/czspFn2Flu7TyKD/S5Z4RPfQnIPfsLd\njh0ShGjMOLCqfwz5s10gYsVeqDs5Wj4vJkClH3pYGFUpApyLmtFWLqSbiLbPGYQu\n9kLGDbB1IY/C2j6fLU1p3QMGY/NorcUF+mKZ/aDZ/c2g4uvz35nUi16ADGQxsi0u\n+iMoRF47H+rfY/Mc5O3E/GvKsZADhivatDa9Ges2FPXKUbQa/HlzpfLFwQOsxryh\n0hYrzQUElhTXHXtQRzsM3ClGO6pLwuiKKW6jRzy8G9X7klmanSd8nSi/28R80S2A\nz10hWcA0KI96uLH0fzi9zxIUlOVn3r4xJjQHvjuGGGYjYakHZBh/zK3jcInM8CDU\nfYXEwejiRlbtwpmJljUJ89m52HEAzJ7jrviFs9U2da5s3tG59zv0IhR5CwMDdG5d\ntYWv9iOsiNGsLX0idaqkNjoHrxdCLzaT2Cyy4BW2plICutS5ax852FpldehZxHVP\nICb7ZLdORQv7JYq2K2LunCtUxbnhcOZuIejDwyxltMCroxjL8Oc=\n=8UDB\n-----END PGP SIGNATURE-----", "payload": "tree 9acc57a7b54ebc339118869c0bedd8f0a7f5cc5b\nparent 74786f0ed1563fbcc1b254e48a8f30e421830b16\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742790558 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1743306164 -0700\n\nFix Trakt URL redirect to media page\n\nFixes #2513\n", "verified_at": "2025-03-31T03:30:25Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a876e006d6a8f0f1283629ef69a5651973a4503d", "html_url": "https://github.com/Tautulli/Tautulli/commit/a876e006d6a8f0f1283629ef69a5651973a4503d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a876e006d6a8f0f1283629ef69a5651973a4503d/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "74786f0ed1563fbcc1b254e48a8f30e421830b16", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/74786f0ed1563fbcc1b254e48a8f30e421830b16", "html_url": "https://github.com/Tautulli/Tautulli/commit/74786f0ed1563fbcc1b254e48a8f30e421830b16"}]}, {"sha": "24dd403a72d8e8f1db853e37ed0725e4a55770c9", "node_id": "C_kwDOAducuNoAKDI0ZGQ0MDNhNzJkOGU4ZjFkYjg1M2UzN2VkMDcyNWU0YTU1NzcwYzk", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-24T16:25:32Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-30T03:42:53Z"}, "message": "Activity card only link to library if section_id available", "tree": {"sha": "227c3229f43afe3f4c247aedf159d83ee514b366", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/227c3229f43afe3f4c247aedf159d83ee514b366"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/24dd403a72d8e8f1db853e37ed0725e4a55770c9", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfovb0ACgkQsfH5gHGE\naXpgAhAAjLnezLIibCYuASH4KwO9/CUMRgHN1jGM/pWvtPxFRs/fhjRRq9M/edqb\nyFG5kQ7dsxZLIpido1BRhbUgtyqkN5g72FMxe9IEU33mxu7Fx7r+EF1Kb+0i2BCI\n1X1PeX6PQU+8UAQyUqtVKLB1sBifgTuUopWgqQrE862UGGuCg7RBJGTek4sOGE49\nGdh5b0UWA5f6UkpCbXricxAnB63Q9D5ecfrUIjGUwWgS0l7y/Xnb01IPUQe5QAsg\nLIfcy1Y6Ia+Qrdd/GuhkL6/GycPxUsPzm20A3rwt6D8hXFlh9BT4sySYO5jOFon1\ns1KyD8PfIFn3wIhgKEmUWI/qiEBahgQnm73nKvR+6LJJ2004ViZ7x3akuSmBJKMQ\nqeVjD79O/m/dY/xLNsjSi+h+VFwExSs9YMrewev0oT7PHp8Fj7qme6OsRJvxPxYx\ne+Z6NRqfcppbBgMf1jTLzZV8DRXXiGUXL6ez8z9m3hwVdVvHzdqqpQKKCw0FWluy\nlDotcDnXF9uGzAa1umuisJ9w6lGNXHPLPPrQlALp4Bmv3RDJCcEwHcyKFvZOdlK3\nfFwWbYJtihPFxAyKsSDfLJ2MX3e4ijQC31TxSfEBylHsghQgzA1sH5H8mwFIGy4T\nVLmyDnWWKk97YkAYNCsywNJjmR6ERbS7xDtfNqwqP+q9dyT8OAY=\n=VLb1\n-----END PGP SIGNATURE-----", "payload": "tree 227c3229f43afe3f4c247aedf159d83ee514b366\nparent a876e006d6a8f0f1283629ef69a5651973a4503d\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1742833532 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1743306173 -0700\n\nActivity card only link to library if section_id available\n", "verified_at": "2025-03-31T03:30:25Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/24dd403a72d8e8f1db853e37ed0725e4a55770c9", "html_url": "https://github.com/Tautulli/Tautulli/commit/24dd403a72d8e8f1db853e37ed0725e4a55770c9", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/24dd403a72d8e8f1db853e37ed0725e4a55770c9/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "a876e006d6a8f0f1283629ef69a5651973a4503d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a876e006d6a8f0f1283629ef69a5651973a4503d", "html_url": "https://github.com/Tautulli/Tautulli/commit/a876e006d6a8f0f1283629ef69a5651973a4503d"}]}, {"sha": "e6c0a12dd5df8ac947fff04e9b458955b2eba506", "node_id": "C_kwDOAducuNoAKGU2YzBhMTJkZDVkZjhhYzk0N2ZmZjA0ZTliNDU4OTU1YjJlYmE1MDY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-31T03:30:01Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-03-31T03:30:01Z"}, "message": "Add stream count to tab title on homepage\n\nCloses #2517", "tree": {"sha": "a2855a56b198027c947aa17ff81bc36b60b6adec", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/a2855a56b198027c947aa17ff81bc36b60b6adec"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/e6c0a12dd5df8ac947fff04e9b458955b2eba506", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmfqDD0ACgkQsfH5gHGE\naXro3w//W6QL2pc8FKJ1N1BELpp123WVNLLiM/3D5BMrqKMdLG5dtbTC39mPk2La\nXEppWpiilGHoi+85Sn82v4B86B5HlMILw7YMgOmLJPsW94UC/t5FmKKFcIiF5mOe\nhSXPx49WfAXPbtFIwCDR0JpRSPa5sCT+F0RRwrx3mWRGmNlLvyTWkTr20u/kf5UN\nf5PvsCsCUlsUdQoV2IcvRw0UlegzrktU3gqYrpEopuvXm0/0V6gRTrjFPnixtp7r\nbs7AQTa2Wp2gj54zOHID3aMd9jqUQjM+8pC5brsQxkq20SVhs4/kmhTOFlfS3cFC\nFSnR8NIju5sE4VG/wwbApdfA35DKz0gA5A4NI2n3rMgTeHQKsG1vZPDwawk9/+rW\nuFcfdXYMSOiQJYf5hjD90Hpu8T1/TRLymeQF11poc8cX+7ISQ3EdfMCGVcgZ/GZr\n4g7QA908PE8P+LJW5g+bU1HResPdQ+EThIM8YKCb3aWb2+lsEBfvow35cInVpBK6\nAjTfhSPFILiTaqlHggojuIYnqaipdHeps5cdxlP3OWLtiuB6nJW8jbLDGu5YEB6P\nZHG3J0BziPwQDiY/yiS3bf0/5PJR6IsoP/IZDyHc1LvGwB/xm4yC/Jfa2pNYlF2z\ndLA1U+TvsMMyGF6kcbNXm+Xgx6pLveISAo+cozYnQ2RrrgfIXzc=\n=KFAH\n-----END PGP SIGNATURE-----", "payload": "tree a2855a56b198027c947aa17ff81bc36b60b6adec\nparent 24dd403a72d8e8f1db853e37ed0725e4a55770c9\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1743391801 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1743391801 -0700\n\nAdd stream count to tab title on homepage\n\nCloses #2517\n", "verified_at": "2025-03-31T03:30:13Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e6c0a12dd5df8ac947fff04e9b458955b2eba506", "html_url": "https://github.com/Tautulli/Tautulli/commit/e6c0a12dd5df8ac947fff04e9b458955b2eba506", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e6c0a12dd5df8ac947fff04e9b458955b2eba506/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "24dd403a72d8e8f1db853e37ed0725e4a55770c9", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/24dd403a72d8e8f1db853e37ed0725e4a55770c9", "html_url": "https://github.com/Tautulli/Tautulli/commit/24dd403a72d8e8f1db853e37ed0725e4a55770c9"}]}, {"sha": "d2a14ea6c020aefe12bc47b9e38946981a6774b1", "node_id": "C_kwDOAducuNoAKGQyYTE0ZWE2YzAyMGFlZmUxMmJjNDdiOWUzODk0Njk4MWE2Nzc0YjE", "commit": {"author": {"name": "Tom Niget", "email": "zippedfire@free.fr", "date": "2025-04-12T22:58:28Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-04-12T22:58:28Z"}, "message": "Add hidden-by-default Total curve to the daily stream graph (#2497)\n\n* Add hidden-by-default Total curve to the daily stream graph\n\n* Update curve color\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n---------\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "tree": {"sha": "5316ee1329c7ce5283b1602a80014edef571d500", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/5316ee1329c7ce5283b1602a80014edef571d500"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/d2a14ea6c020aefe12bc47b9e38946981a6774b1", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJn+vAUCRC1aQ7uu5UhlAAAdRAQAIMosI5F0FE3ws3WnKaT5E0E\nKyhRVZuAQyl3hkD6ruo7/k5OQ3cwD3awAZCYgq9y39GNnxn9YkeSZL3U1pNpiDk2\n9IyVMqmdy17VUQvtbJ7sDpNXyB5dcYR4WJD16h11NBRektRMHqCOmfSqRiO+vvkR\niJoZrBRd4++bfiiS8uUa3n+5KAHIpVavVyjbqnFZ0597ConC/wXwvtJnFBQYWjv9\nsXCkxMu6UgAqWq4+tBkHIgGnExG839SNrMxZ0VJEmMNJcQgU2YGGQRG4r8n4TuC6\nOoS/DZWHC5Rg9h+aMmPiweAvzSeswOHeWVnL4n9tndqj2DDsadts5SvTrNg8nEZt\nFey2BDo9y659A8c/i8qTZwHCkkSz52iTdxIxPLjgZHR8w9/UKWwc4g0YulkJkhyw\nGUCeAmvWY9lQS8c2CkX2CYR7HEZfSiIuR61X9S3hPYq5xDSfcJvhzms0mp7HUbLL\nBsFc63rEobpCEZYoqIjtQ67odli/Z4u5e1c6I4iyZd0e4cGl4HhsGAdxBa60tP6e\n+YNiGh7zOG1PcHh5Eif652fSQpHNm3qqtR7gsfV/2Gr44XcooZfxtBEN5235JyV6\nKqxopx33Afl1C6ySshhOeh7oYm9r9wagiTIqhtOm1tYIKtV20ODYr9yYnPjUG0Bf\n/2q5xoGAT3u1qBIndbfI\n=iguG\n-----END PGP SIGNATURE-----\n", "payload": "tree 5316ee1329c7ce5283b1602a80014edef571d500\nparent e6c0a12dd5df8ac947fff04e9b458955b2eba506\nauthor Tom Niget <zippedfire@free.fr> 1744498708 +0200\ncommitter GitHub <noreply@github.com> 1744498708 -0700\n\nAdd hidden-by-default Total curve to the daily stream graph (#2497)\n\n* Add hidden-by-default Total curve to the daily stream graph\n\n* Update curve color\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n---------\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "verified_at": "2025-04-12T22:58:42Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d2a14ea6c020aefe12bc47b9e38946981a6774b1", "html_url": "https://github.com/Tautulli/Tautulli/commit/d2a14ea6c020aefe12bc47b9e38946981a6774b1", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d2a14ea6c020aefe12bc47b9e38946981a6774b1/comments", "author": {"login": "zdimension", "id": 4533568, "node_id": "MDQ6VXNlcjQ1MzM1Njg=", "avatar_url": "https://avatars.githubusercontent.com/u/4533568?v=4", "gravatar_id": "", "url": "https://api.github.com/users/zdimension", "html_url": "https://github.com/zdimension", "followers_url": "https://api.github.com/users/zdimension/followers", "following_url": "https://api.github.com/users/zdimension/following{/other_user}", "gists_url": "https://api.github.com/users/zdimension/gists{/gist_id}", "starred_url": "https://api.github.com/users/zdimension/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/zdimension/subscriptions", "organizations_url": "https://api.github.com/users/zdimension/orgs", "repos_url": "https://api.github.com/users/zdimension/repos", "events_url": "https://api.github.com/users/zdimension/events{/privacy}", "received_events_url": "https://api.github.com/users/zdimension/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "e6c0a12dd5df8ac947fff04e9b458955b2eba506", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e6c0a12dd5df8ac947fff04e9b458955b2eba506", "html_url": "https://github.com/Tautulli/Tautulli/commit/e6c0a12dd5df8ac947fff04e9b458955b2eba506"}]}, {"sha": "76f6a2da6b3b394765203b7317cd97e7701a3067", "node_id": "C_kwDOAducuNoAKDc2ZjZhMmRhNmIzYjM5NDc2NTIwM2I3MzE3Y2Q5N2U3NzAxYTMwNjc", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-04-12T23:02:46Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-04-12T23:02:46Z"}, "message": "v2.15.2", "tree": {"sha": "9d3286787b921a4d51510f33b9b4bf55238cc068", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/9d3286787b921a4d51510f33b9b4bf55238cc068"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/76f6a2da6b3b394765203b7317cd97e7701a3067", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmf68SMACgkQsfH5gHGE\naXqylA//V+ucQQW4ntsPsPwWbviGkdIWbqpIZPFbgKG+/+JjGN7RSYI5EfSIf9z6\nJeMkI9E7YVYE5QHd4RaOJ5nFx0vcpRJxkWDdCipPQD6Bp8WYQicE1aFWcIAvMZjk\nhehOZ9Mq+gCQmF8FhCNiFLcHdObyTPbMyhb1uaudCBjIAa+lBGj3L2DlEwSYruzm\nsiT8e0lj2GH8+xf/whkIcKtewqNoGVd13sjivEO/aYi3310TeL1fyaKMx0gveTJ8\ngRM19KPUwF8YAsGI9XnYLEoA42K2TilXKm0JkXdipS14sY5L8pcLKI0ZZ8/O+iE2\nS1TDVwA7WuweBGAqbqP99+MTbraIeojPB2sefvhJPgtLHNnD4KThvrHtajMTnyj7\nPKKFg8WhT4GlqSEnDawQv9Ox/nbAzV5k1Tf0bv+sW1IjazUjy5+UPP/ZR0D5t92J\niMuDNoaKz3kQystGZfBRGXoXOaHC7WUbKvlx4pI0EJsST0gtaf72RxLtVu7kwWr0\ndUahAGHN+oLd6TvgvS2j8wfjGYGgxzEgODPosZ05mQjc+OhLMI+/ZF8HNG2/ICM/\nLd2xlTwr09gCAYX4cD+tuof5JNLe5QO/M5TnwOZcd2yTgRmPQS59IlIF3UTl0oZQ\nisZ7GBm7EFgnXbpkOpkS2uFJITrysj67x0RASm+X/djbeftZGcA=\n=MQM3\n-----END PGP SIGNATURE-----", "payload": "tree 9d3286787b921a4d51510f33b9b4bf55238cc068\nparent d2a14ea6c020aefe12bc47b9e38946981a6774b1\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1744498966 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1744498966 -0700\n\nv2.15.2\n", "verified_at": "2025-04-12T23:23:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/76f6a2da6b3b394765203b7317cd97e7701a3067", "html_url": "https://github.com/Tautulli/Tautulli/commit/76f6a2da6b3b394765203b7317cd97e7701a3067", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/76f6a2da6b3b394765203b7317cd97e7701a3067/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "d2a14ea6c020aefe12bc47b9e38946981a6774b1", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d2a14ea6c020aefe12bc47b9e38946981a6774b1", "html_url": "https://github.com/Tautulli/Tautulli/commit/d2a14ea6c020aefe12bc47b9e38946981a6774b1"}]}, {"sha": "889026b092f9716238f8bfd8e99bc1b859a9611f", "node_id": "C_kwDOAducuNoAKDg4OTAyNmIwOTJmOTcxNjIzOGY4YmZkOGU5OWJjMWI4NTlhOTYxMWY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-04-12T23:56:10Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-04-12T23:56:10Z"}, "message": "Add auto sync winget fork to workflow", "tree": {"sha": "ea86a784413c4b209fb4f9d79c01161ea9ee406e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/ea86a784413c4b209fb4f9d79c01161ea9ee406e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/889026b092f9716238f8bfd8e99bc1b859a9611f", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmf6/ZsACgkQsfH5gHGE\naXrpNg//S3/iZbeDNShQxGHKaFUM1oyUYrzWP6SW/jMv45qmj9OKA0F7XkwDR8z/\nRDk7dvurVM+hN5T7np+bDY8veOXFPbnudBLTsrZ2bzMx2RitAMDoVM1iOzRnBWno\newcyGBuexr5qFHODPzEGOB2fDDO8G4wJ+lt+DKdmlxfvRORsrh/JfCvyjyuybcDc\nyU2Yxv3egJU/crsScWmLhp5Zb4rIsrZCCHolultEjOgUUobNp8PBQLMoli7KuUBj\nnUBh84Gt0evprUHtKaNZnQT+/O9J4QYXeukFImgoAqJNg50dZMo6zWtQHZI5bvtT\n/ec1jw4Hb+YxtHoonOHuOzLVNBV2UbSx8XYN0fdLURkSN9251xx6DalA/Dw5q6BQ\nKKXtmjguiixrkfSVxdGpsFusFu0CYMp9+/0t6PcR9R1pQzKr8usNV+8MKtHKL7vi\ntlmqO3yYUarzKw3jRx8x4gXR2bcsOST2nFe38QEmZasaH8zVQX3xzjt6fbvvtZI7\nuUVvEwkHINzMVYFg68/3K/T1Okp11qL1OFNLx833JW9jO35Eem0v1uLEZfddR8hp\nzj0LYFD+uWjE3F6Qq0Cko0LohAVFVS4oTl/dFSYXYqjUkMFh9gKCTgKxCjWJN6pj\nijBqGy67a8D8RUyGCLqe6QxKWxwNfWDX7Lx1XwNeSRrveYVuJpo=\n=pVrY\n-----END PGP SIGNATURE-----", "payload": "tree ea86a784413c4b209fb4f9d79c01161ea9ee406e\nparent 76f6a2da6b3b394765203b7317cd97e7701a3067\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1744502170 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1744502170 -0700\n\nAdd auto sync winget fork to workflow\n", "verified_at": "2025-05-10T23:08:58Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/889026b092f9716238f8bfd8e99bc1b859a9611f", "html_url": "https://github.com/Tautulli/Tautulli/commit/889026b092f9716238f8bfd8e99bc1b859a9611f", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/889026b092f9716238f8bfd8e99bc1b859a9611f/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "76f6a2da6b3b394765203b7317cd97e7701a3067", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/76f6a2da6b3b394765203b7317cd97e7701a3067", "html_url": "https://github.com/Tautulli/Tautulli/commit/76f6a2da6b3b394765203b7317cd97e7701a3067"}]}, {"sha": "e735294e1c14f380b73a853f6e25686aa133baff", "node_id": "C_kwDOAducuNoAKGU3MzUyOTRlMWMxNGYzODBiNzNhODUzZjZlMjU2ODZhYTEzM2JhZmY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-04-19T01:21:27Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-04-19T01:21:27Z"}, "message": "Uppercase ZIP archive export download tooltip", "tree": {"sha": "1de0708374944a487f6922fe3172c4e6cd5cf952", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/1de0708374944a487f6922fe3172c4e6cd5cf952"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/e735294e1c14f380b73a853f6e25686aa133baff", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmgC+pcACgkQsfH5gHGE\naXqdLRAAmoy3FxxD9pRiE4/6wacQhfhEAx0wNZXTmmf7DYt+JnjHLtTbyOMSRb7k\nO/BzP8u/nnQ+i2y+PNQzAgLDFQv7NHgQBL+KnUaLdTHZykLKnEddA44hTuzNvRly\nM5m8936yU6S0a/3HjymcyGpWDjxSpxgkWIKuFqAUA4lF1ZsVMKEXB0rtCb4YAr7m\nryIJQhiOA56YsJQyEftQOjnmvhw+ZRb1ymuXC/DHaiMvBc3mTkF05uCopUGTXeZS\n/qYNVYePiUFPV5EcW4n9kVD/nIUehjNAGxPHg3JFuVHn+b0dau2BqjA8DdCGd3VA\nyTkNM7TZ3CSxKcFrLqbwZRFFdAWWVL9+iITMBoZtIcs08Xo8j23OMnJwxBAcQA7z\nF9JCGBFlpeP+7BWZknjgWxuGw2Qe7qXUdIe38FKB+ZVl/oXsVVwCHsWGxWOcgNl5\nrNxvoZ03Duqs+rkT8n1FWrbTuSjxl8UvQRHbW7tq4OpmFCf+ZzVKjUAbgS3bmL9k\nT/2kGpl2pwKcTo8GwKd2ivLlqsaZxBPmngvdKzeoKrNvUdwMF0BGIvBS+EChOeM7\nvmZ3C9rZYpQjD1Dt11RKGptySoDMs7WxMHJnmR40eS9scNjkSPbiHdUt7k3PNd30\n6EocrV2oFFErWzBm6OCJew+oRiaqvWqyR2lkbYasOjhNBCqYQT4=\n=dwoR\n-----END PGP SIGNATURE-----", "payload": "tree 1de0708374944a487f6922fe3172c4e6cd5cf952\nparent 889026b092f9716238f8bfd8e99bc1b859a9611f\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1745025687 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1745025687 -0700\n\nUppercase ZIP archive export download tooltip\n", "verified_at": "2025-05-10T23:08:58Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e735294e1c14f380b73a853f6e25686aa133baff", "html_url": "https://github.com/Tautulli/Tautulli/commit/e735294e1c14f380b73a853f6e25686aa133baff", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e735294e1c14f380b73a853f6e25686aa133baff/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "889026b092f9716238f8bfd8e99bc1b859a9611f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/889026b092f9716238f8bfd8e99bc1b859a9611f", "html_url": "https://github.com/Tautulli/Tautulli/commit/889026b092f9716238f8bfd8e99bc1b859a9611f"}]}, {"sha": "cc88cffc1fd20b3bc1d558128e7aa5cf5cfbf4b7", "node_id": "C_kwDOAducuNoAKGNjODhjZmZjMWZkMjBiM2JjMWQ1NTgxMjhlN2FhNWNmNWNmYmY0Yjc", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-03T23:08:17Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-03T23:08:17Z"}, "message": "Fix retrieving history for collections/playlists with over 1000 items", "tree": {"sha": "929cffc0f9927a67cead56b3856af5ecb720186d", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/929cffc0f9927a67cead56b3856af5ecb720186d"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/cc88cffc1fd20b3bc1d558128e7aa5cf5cfbf4b7", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmgWoecACgkQsfH5gHGE\naXrlnA/+IoyLI9WKCNtPvhuNQ9hNq/0LoArfanLSHYKu4V934Ht1tOWmgRusa2Mn\n6T5bqrQGckVWT5ZOlqCkXAVLgmV/WAuj7nZwYWHx9HZEyNwO/mf5+BUozitf3reQ\nGFGw3huJIWp5wv+ctd4ZJEyNuzQ6VMMIyC+fPLp99wxUd/xSbXwzBx3IWxvbpDvG\nlLbj6arkP+uV2AuVidpzv8s4ALXsr1kR4uxxGWLjDHnUqBITmE1Sjs384eMOeHMc\ngxwuvbDEEdIa/kH8OR0QIGazoBZbpD6wq/1PmZRtVdRYBrQ6D18NlwhyOXxMsXA8\nzor9dflF4R8jJqMKWJ3mMwiiVBQddZnZ1sUCBGrnrMniUuvgFBVd/aiOGwx7c0nX\n3Sr240rePcqISAhXaioiLrO5zjkX0uec7zRf2DbxjcH08q8tVph/DJW06PPIneFt\nRSd+eoPwm89JrZiLE21uFBZgIG+MkxMw3I4FOLKFyrpABKICHm3vyJkvxtfJgDe1\nRBUs90FxPEbxUxeUIEXZxCIFCuspm+w+QGGVdTDnfghVjyuLd4tvbXaRf/Y+Su1k\n6uyA5nmTwntmW8bFoAoLP446dsMB4D8mE5HFabHoWs6hkC4pXWJSL208dTke0rTU\nuPNUGdMR8AAdwxcZkJCz7j6OFYge4oeUoG+6hEvivLJde8iw8jM=\n=MaGp\n-----END PGP SIGNATURE-----", "payload": "tree 929cffc0f9927a67cead56b3856af5ecb720186d\nparent e735294e1c14f380b73a853f6e25686aa133baff\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746313697 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746313697 -0700\n\nFix retrieving history for collections/playlists with over 1000 items\n", "verified_at": "2025-05-10T23:08:58Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cc88cffc1fd20b3bc1d558128e7aa5cf5cfbf4b7", "html_url": "https://github.com/Tautulli/Tautulli/commit/cc88cffc1fd20b3bc1d558128e7aa5cf5cfbf4b7", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cc88cffc1fd20b3bc1d558128e7aa5cf5cfbf4b7/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "e735294e1c14f380b73a853f6e25686aa133baff", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e735294e1c14f380b73a853f6e25686aa133baff", "html_url": "https://github.com/Tautulli/Tautulli/commit/e735294e1c14f380b73a853f6e25686aa133baff"}]}, {"sha": "ff5edc06fe7d7e161005dfb6809b173631fc1984", "node_id": "C_kwDOAducuNoAKGZmNWVkYzA2ZmU3ZDdlMTYxMDA1ZGZiNjgwOWIxNzM2MzFmYzE5ODQ", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T22:56:52Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T22:56:52Z"}, "message": "Update crypto donation", "tree": {"sha": "52883ab73856ebe799493957583638edcf92def5", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/52883ab73856ebe799493957583638edcf92def5"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/ff5edc06fe7d7e161005dfb6809b173631fc1984", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmgf2bQACgkQsfH5gHGE\naXrulg//R6DEhdVcVYwAzlLtozkAs95/nsUmWtJifRUTy4kzYiKvMhTj68HC54k+\nSpUmP1wcAao0K3eV4CAa0dr+c3Yo6wxV1SOGzk9LIX1VYGgRLZK3VwmF+VI0zhut\nFEG8nokKvuqbFdcBelxLsRuPnb0oHwaP4mA5UX/Rvi4Ai9EOc+dikc06zIZebbQO\nk1F4jW92F3jDlcw0jW8DIuAVsqmvvgZCBWNbeOReT0wFbWynN82M4kV65szo/O4v\nqwmlQjaHKBs/pFQu9au/7w6U7f+2vZ1xYyGH82X0zMctMY9BMa23VKL8dIHu0s2J\naZH8cBTP3doxIFwHr9KKODXduwhtY4Okg5oNdF/u9j7CM/caZ9jjeYK1iQxiQWGu\n7mLab53ryDSYWhICBUFRCqSqnfnOOljOJ3av8qGhvhj7A/+LdsDzGFqBmfd2k3qP\nFgbWQbyGHY8CRLXiNIfV/EG9Qg0i8p5a2vnS3kOXo8BGvCTMtWIHCdZcE54ei4Nf\nhVmTGjkQQHA0EMV66XiQcsgiJmOTeqbNFRw5U5R8/xE33LNxUX3MqgnSo/HZF2zE\nXUohccuN+pRvJ+aYrUcI9/n28JcTa22IeNB2i2wrFBRXE0XvYuBeW52RvI8fhpr9\nMCgPFeu9WT/PZfe7se/Qfia7w0lzjquuqEzDEnaVyLNjcGkTm/g=\n=/JN9\n-----END PGP SIGNATURE-----", "payload": "tree 52883ab73856ebe799493957583638edcf92def5\nparent cc88cffc1fd20b3bc1d558128e7aa5cf5cfbf4b7\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746917812 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746917812 -0700\n\nUpdate crypto donation\n", "verified_at": "2025-05-10T23:08:58Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ff5edc06fe7d7e161005dfb6809b173631fc1984", "html_url": "https://github.com/Tautulli/Tautulli/commit/ff5edc06fe7d7e161005dfb6809b173631fc1984", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ff5edc06fe7d7e161005dfb6809b173631fc1984/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "cc88cffc1fd20b3bc1d558128e7aa5cf5cfbf4b7", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cc88cffc1fd20b3bc1d558128e7aa5cf5cfbf4b7", "html_url": "https://github.com/Tautulli/Tautulli/commit/cc88cffc1fd20b3bc1d558128e7aa5cf5cfbf4b7"}]}, {"sha": "3cb71f94a3b9590bc809d8fc087095b53f2afa8a", "node_id": "C_kwDOAducuNoAKDNjYjcxZjk0YTNiOTU5MGJjODA5ZDhmYzA4NzA5NWI1M2YyYWZhOGE", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2025-05-10T23:10:03Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-05-10T23:10:03Z"}, "message": "Bump plexapi from 4.16.1 to 4.17.0 (#2538)\n\nBumps [plexapi](https://github.com/pushingkarmaorg/python-plexapi) from 4.16.1 to 4.17.0.\n- [Release notes](https://github.com/pushingkarmaorg/python-plexapi/releases)\n- [Commits](https://github.com/pushingkarmaorg/python-plexapi/compare/4.16.1...4.17.0)\n\n---\nupdated-dependencies:\n- dependency-name: plexapi\n  dependency-version: 4.17.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "394bf69c683e65a80a4b2d20de668a2b7260b918", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/394bf69c683e65a80a4b2d20de668a2b7260b918"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/3cb71f94a3b9590bc809d8fc087095b53f2afa8a", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJoH9zLCRC1aQ7uu5UhlAAA89gQAAg+OjWXxa/PcaGfLP6vPHwF\nRq/7g+mGEfdYr/tCIPqX8Fc6eObJKAlrNFJ/a8W/fxslGxe30NVOYkdVJfW1T+uk\nuTYxIsWYPrTQZEkGQWwZRFzF9eWlpF233IjnaZr1yYLg6bcHXweKZYdz6p6Ok6RU\nb+UU9Zikn81Ye4XUheNJOOOvqlEWJd0euMqMw0Fq3JRv4kt5Wd9EYil2C3bCnfSi\nCV2wmDDb8iv7zztOY6NE9WPXseJaYaMFxFo70ljo9htA21PifKcOtcba0XfJ0sA4\n+UaHZISusSltc/i+Zv38YqypIyy0HXSOVPcVE7niuxN215ufpl+136S59DFuwxzq\nphNe3RpR6EAGUzaLOOIiewr61A8jLjUeBrS5OJSWlIQTYWItrOsUXm5hlCmxj4U+\nms5xVgaJ9dd3TkbHtWBOpr1OuKkeAH7v6+InNs6xwtxr3m8JwDgX4n73/ljfEGgN\nna237TsQ62RJnAQ6Q/ZQlWyKf20nY4NlC0l+luCP0geUsQcb5MpT2tywljICMYzW\nhb7JOq7hGwv1Zug4fjnLEa9mBISPm/dtIxv5CvtdsjWV+aeHAV23jX1Lr9GCVx+R\nsR/v1GbU6xSxuKyBhTk2NZNaFZr93Bp/Dim0Ci4ZevO7nTtMAzYTmc/hhgRs3dC8\noiqjO0/MwdX/AhkuabiD\n=2/di\n-----END PGP SIGNATURE-----\n", "payload": "tree 394bf69c683e65a80a4b2d20de668a2b7260b918\nparent ff5edc06fe7d7e161005dfb6809b173631fc1984\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1746918603 -0700\ncommitter GitHub <noreply@github.com> 1746918603 -0700\n\nBump plexapi from 4.16.1 to 4.17.0 (#2538)\n\nBumps [plexapi](https://github.com/pushingkarmaorg/python-plexapi) from 4.16.1 to 4.17.0.\n- [Release notes](https://github.com/pushingkarmaorg/python-plexapi/releases)\n- [Commits](https://github.com/pushingkarmaorg/python-plexapi/compare/4.16.1...4.17.0)\n\n---\nupdated-dependencies:\n- dependency-name: plexapi\n  dependency-version: 4.17.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2025-05-10T23:10:37Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3cb71f94a3b9590bc809d8fc087095b53f2afa8a", "html_url": "https://github.com/Tautulli/Tautulli/commit/3cb71f94a3b9590bc809d8fc087095b53f2afa8a", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3cb71f94a3b9590bc809d8fc087095b53f2afa8a/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "ff5edc06fe7d7e161005dfb6809b173631fc1984", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ff5edc06fe7d7e161005dfb6809b173631fc1984", "html_url": "https://github.com/Tautulli/Tautulli/commit/ff5edc06fe7d7e161005dfb6809b173631fc1984"}]}, {"sha": "f6bffe1850d4c05d491ebb440289c7de6343ed33", "node_id": "C_kwDOAducuNoAKGY2YmZmZTE4NTBkNGMwNWQ0OTFlYmI0NDAyODljN2RlNjM0M2VkMzM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T23:13:23Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T23:13:23Z"}, "message": "Update plexapi==4.17.0", "tree": {"sha": "04ac497d876333fd134c91fa86a5acfdcf951752", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/04ac497d876333fd134c91fa86a5acfdcf951752"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/f6bffe1850d4c05d491ebb440289c7de6343ed33", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmgf3Z4ACgkQsfH5gHGE\naXpe9A//ZDQp3UyNekQdoOqrETCvRYbQfdBUCuZdBepSxLUwUTgwRyRLuIIjwa5a\nSAtuZbfjhNaLBuNoRd80ZhN2MLmwwuyCxXHJH9w395UR61wOG1yJ8sXopwoYnDEC\nbbEgKMpZ52n7PPMNb+RCZU9inYw6t1UgSYJS7ZpBynmliZoCLueTY0I5ZOQ1gEL5\nGqPkkLPK3Oyrheva4OnWPLArLy08uvU9twKC4CyYbnw2apiedpFZQdZcI9+95qr5\nIZ4I4iJ/vLcxRxXihR9DIqYWKJHjlVnqvRMGHceSVzxpuq3JwloXTcNegYzV36LF\nLqoWlAuSaOzrmP0RA12jpF8stqScwUyeraKTQCf4BV+13gLh2c7ZAe9xyfNGV4+8\nA9MBCLXaTj/wjPsZpayQUDGVR56g91mSO1NEPe+YZwg40BXqQlDlljDvG1qPbQCy\nn51OCn1OBA+SjVnTi6MIQlwi/1JQJtTn2J8LVTZ4jQSdPO0k6flPXPgi1WieQ++K\nFgAmRlHBRvFRWuEzpNTtQ4WIXh4oVLM4D5PtcYyn57ki/DlYm4kOUxKDOw9fv6YS\nGGqZXFTF4vIHH0fAqySxXuZnoh9T2ksZgsw8i6zBOwkaetcYor8Ewr/7FsO865W+\n3IF4R8TtG5xsw7ZCr2vxy1bI56uiKiMdCfN4B4S6x1HcY5/2GCw=\n=GNng\n-----END PGP SIGNATURE-----", "payload": "tree 04ac497d876333fd134c91fa86a5acfdcf951752\nparent 3cb71f94a3b9590bc809d8fc087095b53f2afa8a\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746918803 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746918803 -0700\n\nUpdate plexapi==4.17.0\n", "verified_at": "2025-05-10T23:30:20Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f6bffe1850d4c05d491ebb440289c7de6343ed33", "html_url": "https://github.com/Tautulli/Tautulli/commit/f6bffe1850d4c05d491ebb440289c7de6343ed33", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f6bffe1850d4c05d491ebb440289c7de6343ed33/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "3cb71f94a3b9590bc809d8fc087095b53f2afa8a", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3cb71f94a3b9590bc809d8fc087095b53f2afa8a", "html_url": "https://github.com/Tautulli/Tautulli/commit/3cb71f94a3b9590bc809d8fc087095b53f2afa8a"}]}, {"sha": "c4f8a811902160b2752eb4f3c424be6350cb0846", "node_id": "C_kwDOAducuNoAKGM0ZjhhODExOTAyMTYwYjI3NTJlYjRmM2M0MjRiZTYzNTBjYjA4NDY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T23:24:36Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T23:25:39Z"}, "message": "Add hearingImpaired and visualImparied to exporter fields\n\n* `hearingImpaired` for `SubtitleStreams`\n* `visualImpaired` for `AudioStreams`", "tree": {"sha": "c59ce0d178d8164c9d50ee6af225f441b340d4a6", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/c59ce0d178d8164c9d50ee6af225f441b340d4a6"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/c4f8a811902160b2752eb4f3c424be6350cb0846", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmgf4HMACgkQsfH5gHGE\naXreUA//aRlnE4V4Ju86LofMqDycj3dwU2QqnQ89kt4LeDuEp+nAUCEEVpkwGbLJ\n3O26/kk7EVkuZhye29dc2HPebMQsGtBKGh91J9jpvd5I1SSl7kpb2DBdQY10gynw\n6ugwtR2UXcQ/Wcv4YnNLHgO8/plWOZpe4ulGgwOOiGHodQBfZO9tEWi8ATsiQBpc\nQGRFiKfFtsK5ZNNRF0hkMNepeoja30ksc4LT86vvFFGG2Gz9lkDnMZ8WgleczcPG\nJX17Nug/lEVDeHk4GUjc0r89p9OwsFBM/hSK9enMSqFLiK8J+OcJJv6XwBUpmR8v\nduAjw+iACt3midMq3ePhZW/WWpDLFY/IX5Mm5DnruM5Cia3OzNrV8Eb0Xst4nV4o\noflW4KIqol/peFcfyjvEICy238+fCegqy78XcApK0zOjrQX2oX/ODBxiIKJ/VqOz\ndv3mGgeP+XpFNYOi7lDJ9cBL6kJmeM+JJAOK06jz6des8aNQFK/kqFOFvjuRH+Yq\nNgVgvBQP0QGe5CiIwQHmvAAHpQbEvNc7c5a14JLJPGJud+wfXFiHnn/p6CmNYYnH\na04gd8/BjoKtPiQK3NM8hsRdFQZMJJKltOYs+RBEsRtfnFTm9BkLYPXer+F9/16P\nM6w8GDV7ztKuMksrhGvGGvCEeX8Z62/0r2bVO7ZN/Yxbd+/Cs1c=\n=0fr7\n-----END PGP SIGNATURE-----", "payload": "tree c59ce0d178d8164c9d50ee6af225f441b340d4a6\nparent f6bffe1850d4c05d491ebb440289c7de6343ed33\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746919476 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746919539 -0700\n\nAdd hearingImpaired and visualImparied to exporter fields\n\n* `hearingImpaired` for `SubtitleStreams`\n* `visualImpaired` for `AudioStreams`\n", "verified_at": "2025-05-10T23:30:20Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c4f8a811902160b2752eb4f3c424be6350cb0846", "html_url": "https://github.com/Tautulli/Tautulli/commit/c4f8a811902160b2752eb4f3c424be6350cb0846", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c4f8a811902160b2752eb4f3c424be6350cb0846/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "f6bffe1850d4c05d491ebb440289c7de6343ed33", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f6bffe1850d4c05d491ebb440289c7de6343ed33", "html_url": "https://github.com/Tautulli/Tautulli/commit/f6bffe1850d4c05d491ebb440289c7de6343ed33"}]}, {"sha": "43fc7eebfeb820b7113e7cd4642f29158a75a6e3", "node_id": "C_kwDOAducuNoAKDQzZmM3ZWViZmViODIwYjcxMTNlN2NkNDY0MmYyOTE1OGE3NWE2ZTM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T23:26:12Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T23:26:12Z"}, "message": "Update is_hdr helper function", "tree": {"sha": "dc0e69235b8092d16d88a7e010465d1946e53972", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/dc0e69235b8092d16d88a7e010465d1946e53972"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/43fc7eebfeb820b7113e7cd4642f29158a75a6e3", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmgf4JUACgkQsfH5gHGE\naXpqlw//ZUMiQzBd9VX/anlIx+7Zu80OGJkpM8vYYXS7BLxe0hozpPNLFg8BO36w\nsU/iS66tYaroV6LOUJDVCoRPbvTpQ38vN8U1Atsb0IsZ+54W1Ny24JCGTHciRuXn\n0L4u1QZeGA1BznT02ac6j8I1hWJq/3ljTCpmDicsOSd0672Ld7BSzy/1eeaSt8B8\nBoqXz0Z8QjKZnzu7oGTvHIB5O4s47d9OvwR0voKkHIr+ghAZL/2OLt68qmtIAg3j\n9nHIBcQubqAI5GPAE9lJFWkmvML5fs/DdUKK+i6Wh3rq3uw55nBeVjT/g/rNUiXK\nzq1epX5PeZnZFiX6DXf3BwvLDn+zQiDlboJjGP9Q/mwaOvBZlN/4q5qyCRp2c8qq\nwf6b7V5pbB0EMYQqjp1Gl2eVjhB7ma9jCHoB7bJnXzBXTf4qYMfjmPJva0aK5IVq\nMpRQ39jUJq0vR8uAu5KXztscE8TA6as/8wRoRr65Zl/anBerDW4XGiRqiCqdCbcE\nGu2/aZSA6ZaaE0aq8m71fijBsRvx1BuFTzRw+8prZfJTSKcjJEp8JO4rAVc5eA1/\nLnd33BnJ6l0XmDhFJTcvB/37U9lB1ILAUWAWoiUOX7ciUDFYSdn4pXDQj+yfJL7a\nUthfzEAestWAvujy1vY9gmPK2k1ARuOpraHGDY/ojOPjQcxtTYs=\n=1p88\n-----END PGP SIGNATURE-----", "payload": "tree dc0e69235b8092d16d88a7e010465d1946e53972\nparent c4f8a811902160b2752eb4f3c424be6350cb0846\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746919572 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746919572 -0700\n\nUpdate is_hdr helper function\n", "verified_at": "2025-05-10T23:30:20Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/43fc7eebfeb820b7113e7cd4642f29158a75a6e3", "html_url": "https://github.com/Tautulli/Tautulli/commit/43fc7eebfeb820b7113e7cd4642f29158a75a6e3", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/43fc7eebfeb820b7113e7cd4642f29158a75a6e3/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "c4f8a811902160b2752eb4f3c424be6350cb0846", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c4f8a811902160b2752eb4f3c424be6350cb0846", "html_url": "https://github.com/Tautulli/Tautulli/commit/c4f8a811902160b2752eb4f3c424be6350cb0846"}]}, {"sha": "9a6253d775fdc405fd3a5506e4ae34a8b782a1f7", "node_id": "C_kwDOAducuNoAKDlhNjI1M2Q3NzVmZGM0MDVmZDNhNTUwNmU0YWUzNGE4Yjc4MmExZjc", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T23:28:32Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-05-10T23:30:02Z"}, "message": "Enable jquery.scrollbar on macosx and webkit\n\nFixes #2221\n\nRef: gromo/jquery.scrollbar#134", "tree": {"sha": "ed8732835113f3a5171cbb70b83b5d13842e42d3", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/ed8732835113f3a5171cbb70b83b5d13842e42d3"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/9a6253d775fdc405fd3a5506e4ae34a8b782a1f7", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmgf4XoACgkQsfH5gHGE\naXph0Q/+MmPcGBvVegVdm6gWdBkwrfFIeSI8+yVWnNRjpgK1bH7mdHlhuI0TWOpo\n0UKwbPom6BSsAEhdboLrk/7ZgW30YwyPLuMb4/P4XKQDge9K8vO6SKRU6rKoniqd\nx5siPEfaSn8AjPX+mN0wePoS3mkVJL9/G1CwcZfVEKDvCO8iExr7zHLcZeOXeEqZ\nk49GiUgmDMo4rTy9ENwVVk9WBW1hwe8Dx1lhFpw+Wif9eRaXRyOZxqJ4quiTaE0a\nulpho0QVaH37NEj+R/dFnLBnyJ4I1PP1XysNhlxcsq1/BeK6O5dTaY+SoB1mDA+c\nTgraUV2OFM1ETip6tt8se1v8a+VjLEtbFLw/PD/ZXtZsIHFBKmTdDsyHdsPu12yQ\nFLgqq9c5RxSCY+1lGMa+NPnpbCI5ex8Dd726DjpN3Tb6JcEnjjNqiZMzIRb0cE+h\n6dQtFFi2QDY5tW2PPdlFhwjIK9/J/vJZzxChjDL/VUQ5hGzzTPFpty4LXM6wr1j5\n/OIDCxo5se5xwPIGKhucZVO+YESZyD4jqy2Qjs8ETlMzjyWI8SjIZJQUVtAaiKOw\nE9n6Wv7J7b5upXV1fwCH7wtS7oBT2B/fShMeU5JqXJPPlLVSTwYRsOR+Y/1P/TZs\nvNtz5TyRpBzn2MBarv9QW2Da+Wk5qICRULsK3kU2bSo3udi2cMw=\n=5CAg\n-----END PGP SIGNATURE-----", "payload": "tree ed8732835113f3a5171cbb70b83b5d13842e42d3\nparent 43fc7eebfeb820b7113e7cd4642f29158a75a6e3\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746919712 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1746919802 -0700\n\nEnable jquery.scrollbar on macosx and webkit\n\nFixes #2221\n\nRef: gromo/jquery.scrollbar#134\n", "verified_at": "2025-05-10T23:30:10Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9a6253d775fdc405fd3a5506e4ae34a8b782a1f7", "html_url": "https://github.com/Tautulli/Tautulli/commit/9a6253d775fdc405fd3a5506e4ae34a8b782a1f7", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9a6253d775fdc405fd3a5506e4ae34a8b782a1f7/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "43fc7eebfeb820b7113e7cd4642f29158a75a6e3", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/43fc7eebfeb820b7113e7cd4642f29158a75a6e3", "html_url": "https://github.com/Tautulli/Tautulli/commit/43fc7eebfeb820b7113e7cd4642f29158a75a6e3"}]}, {"sha": "5921a7d83fbf9a452478b93639d1e844b3191fe0", "node_id": "C_kwDOAducuNoAKDU5MjFhN2Q4M2ZiZjlhNDUyNDc4YjkzNjM5ZDFlODQ0YjMxOTFmZTA", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-07-15T02:54:45Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-07-15T02:54:45Z"}, "message": "Fix rounding of minutes in global stats play duration", "tree": {"sha": "151d8e450355562cb2b12fe3c505924c4bddc232", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/151d8e450355562cb2b12fe3c505924c4bddc232"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/5921a7d83fbf9a452478b93639d1e844b3191fe0", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmh1wvkACgkQsfH5gHGE\naXpjCxAAkryBwKNtN/t/DNUrJLOhmu1k/jdnE2I1zyybqd5htLGcCgnf54h/aMlY\nH5jU6vT89oLCa6CGIIW7FsSF+vImgSqy8m2dyzvH4mkNps0w0jL89sle+FI3X51S\nqToQc4Fx/QNBxHtLhiC4jM10vMp9eHkRWG5w2vOMIpNDBYEFcMc+91/mY/UtOtEU\nAU81dK100FmzkMf46xcFBLN7h+XEFrT3gGPvfjgeeaXLLtkIgSRbkHc9BseNvvMS\nVLLNwLv3Czn9Nvw1Xmovpz3aHAQ/k/kcHa00dTttWrFq5wviySam6MYQAoh/2s8+\niIa0seFNfFUjvqwy1U1/ISu5AU/yypQFHSIdoG9BjjGkKA7HtcdZz98VhtZPck5f\n3GlFz4NYuz5i+TZPTJhGJGq8f3JwAQLTxYw1eveBaTj9XYIwpgwvJLc1nzfRJ223\nMXnQEatq0Yz+TdMb7g3S50dueHBJx38/xy2Q494koILhO4bg9xE8JFVh80bj/ch9\nrXyBX0TYqCuNPfcNTl975SQc8D+uPPkHWZXQEKp/RLuJrLOq2sdhcNN5daBtaQzE\ny4bpyv9m1BQ6XPrHy6RCWOz46H3+Wpe9wfIuPkGQANsifqjgcD/HUQsYdb9cM1hq\nXIbIN7sbHvXT9FdoRK513OT4IP66lcYecqTdRJ4/w4VyzX5Z3n0=\n=bo/J\n-----END PGP SIGNATURE-----", "payload": "tree 151d8e450355562cb2b12fe3c505924c4bddc232\nparent 9a6253d775fdc405fd3a5506e4ae34a8b782a1f7\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1752548085 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1752548085 -0700\n\nFix rounding of minutes in global stats play duration\n", "verified_at": "2025-07-15T02:55:18Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5921a7d83fbf9a452478b93639d1e844b3191fe0", "html_url": "https://github.com/Tautulli/Tautulli/commit/5921a7d83fbf9a452478b93639d1e844b3191fe0", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5921a7d83fbf9a452478b93639d1e844b3191fe0/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "9a6253d775fdc405fd3a5506e4ae34a8b782a1f7", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9a6253d775fdc405fd3a5506e4ae34a8b782a1f7", "html_url": "https://github.com/Tautulli/Tautulli/commit/9a6253d775fdc405fd3a5506e4ae34a8b782a1f7"}]}, {"sha": "d46710962cebc05e257c41454ac8e15d9933828a", "node_id": "C_kwDOAducuNoAKGQ0NjcxMDk2MmNlYmMwNWUyNTdjNDE0NTRhYzhlMTVkOTkzMzgyOGE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-08-03T16:35:06Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-08-03T16:35:06Z"}, "message": "Add ability to return svg files using pms_image_proxy", "tree": {"sha": "8fad6d90651e1427f592bb5c4fa7ed50db5cc3aa", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/8fad6d90651e1427f592bb5c4fa7ed50db5cc3aa"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/d46710962cebc05e257c41454ac8e15d9933828a", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmiPj78ACgkQsfH5gHGE\naXoqoQ//fr9G0GOYJpph9cqFHkDmajZYyEbwDEBXnqr84uhMUBa+j79p+Zj2hHth\nZJZTFrJwZ8BzwJtpDNEtl3mXOTWXpOk00d9PHOCKRRZSnUQgxpEKOnydHD7fRaca\nu1XUcvUJ51L8E7sheUiK2QA51pE3YIM2YuLewQc6rYG24HaMgXIMyybc/haiPW4p\nUBOcgiXA1vqW+nY2lBIVRv4Cha+VLadEYXlgFJJydRjsO72wjbqnyWI5aAdAObGQ\nb5Lbm5sKZhr0+H71DdQRCmJJ4c/CKEBSOLaMvzsCb2EGpP4WL2vsHkr5QsIv8NDM\nxKeC2P9EDN+ItOOE8imIOdUYq9KrSSU/NUypz3ewfAdtyJ9mf2jPs6zXSMAk3+q4\n6pVIDvgW90/bKC41MYThnRvDV1NkDHkqeBhXd2QHTGK3umD9OYA61lCE6LqU9UNa\npwV6HaZXvaLqJmXuMBuycu3ZZZLR/mCoJlFfrzGpVRPzkYknDteIlkNq/IBESIKC\n+BdHYLjhBfHvG1BK8W9LCKFSrMfm50fX7S17UwOGhmyYap+1oL1Vg1V0YuGf/DGv\n5Fx4H+Fa2M3Ul/vkFx23QQoWMMw0uIxgHVYusaZenFA8n4ZgPBB2zmHNxEk8WW1B\nscp2VfxKhfAhSFSmN9u+zCtXvnRy8t/dVSyIiJSc3ZlNyS/SCwg=\n=upQ5\n-----END PGP SIGNATURE-----", "payload": "tree 8fad6d90651e1427f592bb5c4fa7ed50db5cc3aa\nparent 5921a7d83fbf9a452478b93639d1e844b3191fe0\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1754238906 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1754238906 -0700\n\nAdd ability to return svg files using pms_image_proxy\n", "verified_at": "2025-08-03T16:37:07Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d46710962cebc05e257c41454ac8e15d9933828a", "html_url": "https://github.com/Tautulli/Tautulli/commit/d46710962cebc05e257c41454ac8e15d9933828a", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d46710962cebc05e257c41454ac8e15d9933828a/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "5921a7d83fbf9a452478b93639d1e844b3191fe0", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5921a7d83fbf9a452478b93639d1e844b3191fe0", "html_url": "https://github.com/Tautulli/Tautulli/commit/5921a7d83fbf9a452478b93639d1e844b3191fe0"}]}, {"sha": "b8589513c18564f11f014704a1b939f149b4511e", "node_id": "C_kwDOAducuNoAKGI4NTg5NTEzYzE4NTY0ZjExZjAxNDcwNGExYjkzOWYxNDliNDUxMWU", "commit": {"author": {"name": "Tom Niget", "email": "zippedfire@free.fr", "date": "2025-08-03T16:42:44Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-08-03T16:42:44Z"}, "message": "Remove duplicate \"Total\" entry in graph tooltips (#2534)", "tree": {"sha": "c6d063c6475376d02f4866469475c5afe0ecd3d4", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/c6d063c6475376d02f4866469475c5afe0ecd3d4"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/b8589513c18564f11f014704a1b939f149b4511e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJoj5GECRC1aQ7uu5UhlAAAhUEQAEZ4RxD3KoJkq4FM6zAE8QvZ\nhX/5kk8TYRg9qnwXhxWCR36U1hpRiAgN7LFfHqFywn1jWzN0MPE9SzgJx3TkfV4T\nNAoa6vPDoU6CnIoADR2geJuG1x0rz40iD6mMjBE4HfA5fe91FHiVanh09zW25Rxy\na9/QBLhVztlxZXo3pwRQCwek44OULLwNvHwx7YFNMs4nLDYoUijgeG3ODTjwcdTL\n+HNUzvF7qrikR9A3gCGdwtwijXMuTZ4ntisEF0xfZTzcnxRKutOeZLg6OZdxw4b2\nLeV8yOjLXm9lAFlqC4u//Mi1cuHmvBU3kHZptppMbof0oaVkhfh9hkblZO3RqGOq\nz2nKu1vrpAH7+gALb9Vn/UR+GcJf9PdQiJs+eltYJzu5lqCfMge6xocsv6mK3aQn\nM1Y+iLvtADbzBcSEth+W42nfu8yIIc41KH6foEhtIqBQzbS61We5CIQinWYVnPGL\n41zMG84eVopizrWhuCfzP+hP0NLmzICoqOqXvZF1uPqZCk/kWuAMb2hRRKQvZG/+\nV4O5UN+T3PP3ZduRlzsSvAvJEy2A7Z96oR3V+55+NQjdEV43JzK1o0IrQ1aARQNL\nnNBasCe3w8NCzIC5DFzUhPUpEgPbybuVkfSj3G4WvL8o4wce1IXZT/jKRcoWVSzi\nBkAfYM6Gycb58lN1ZeeF\n=1rLJ\n-----END PGP SIGNATURE-----\n", "payload": "tree c6d063c6475376d02f4866469475c5afe0ecd3d4\nparent d46710962cebc05e257c41454ac8e15d9933828a\nauthor Tom Niget <zippedfire@free.fr> 1754239364 +0200\ncommitter GitHub <noreply@github.com> 1754239364 -0700\n\nRemove duplicate \"Total\" entry in graph tooltips (#2534)\n\n", "verified_at": "2025-08-03T16:42:44Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b8589513c18564f11f014704a1b939f149b4511e", "html_url": "https://github.com/Tautulli/Tautulli/commit/b8589513c18564f11f014704a1b939f149b4511e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b8589513c18564f11f014704a1b939f149b4511e/comments", "author": {"login": "zdimension", "id": 4533568, "node_id": "MDQ6VXNlcjQ1MzM1Njg=", "avatar_url": "https://avatars.githubusercontent.com/u/4533568?v=4", "gravatar_id": "", "url": "https://api.github.com/users/zdimension", "html_url": "https://github.com/zdimension", "followers_url": "https://api.github.com/users/zdimension/followers", "following_url": "https://api.github.com/users/zdimension/following{/other_user}", "gists_url": "https://api.github.com/users/zdimension/gists{/gist_id}", "starred_url": "https://api.github.com/users/zdimension/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/zdimension/subscriptions", "organizations_url": "https://api.github.com/users/zdimension/orgs", "repos_url": "https://api.github.com/users/zdimension/repos", "events_url": "https://api.github.com/users/zdimension/events{/privacy}", "received_events_url": "https://api.github.com/users/zdimension/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "d46710962cebc05e257c41454ac8e15d9933828a", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d46710962cebc05e257c41454ac8e15d9933828a", "html_url": "https://github.com/Tautulli/Tautulli/commit/d46710962cebc05e257c41454ac8e15d9933828a"}]}, {"sha": "5ecd570f95552e4e2c0c7b18ef8e12f26dc021ef", "node_id": "C_kwDOAducuNoAKDVlY2Q1NzBmOTU1NTJlNGUyYzBjN2IxOGVmOGUxMmYyNmRjMDIxZWY", "commit": {"author": {"name": "Komu Wairagu", "email": "komuw05@gmail.com", "date": "2025-08-03T16:42:58Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-08-03T16:42:58Z"}, "message": "Allow users to set config values through environment variables. (#2543)\n\n* - Allow users to set config values through environment variables.\n- Fixes: https://github.com/Tautulli/Tautulli/issues/2309\n\n* Prefix environment variables with `TAUTULLI_`\nFor: https://github.com/Tautulli/Tautulli/issues/2309\n\n* Update handling Tautulli environment variables\n\n* Add log message when environment variable not saved to config\n\n---------\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "tree": {"sha": "794c5d1231468069af7592eef0406fe97192d0c9", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/794c5d1231468069af7592eef0406fe97192d0c9"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/5ecd570f95552e4e2c0c7b18ef8e12f26dc021ef", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJoj5GSCRC1aQ7uu5UhlAAAG24QAJafkBt3d35gdsK8T/Bq0eGH\n0qRZkQSJt+RZrrlKrEmgxm9gDAT+px4eYJRx+BjQOFOt9DxfL0LtzdopZ4/RVcUH\ntrzsTJ3I/I7UDxmb7vOMcr/jzEHHvs4i8eRUJyGr8YO2avaCr4SDPV98/GwqZUFd\nukkgNynINqcKAegiQ7Fg9rtQxbP1+bUmwzLNuPJd3AYxqacSV+36xtlNLW81Z4Op\njHfUFYqvJ4CtiNATnvzbXkK+mwllrPmVPk/6U/jCHbMgzpuppTIB5y9kRLv2sFau\nILAD37Ko4fMiAVJE6lH/+RI+BlNlnLJSKBhWQlfSV3HhjJSFv7xpQF4gCYYKmyPs\nt6tiPkIpDCypyW3lwsadldt0Yi0jD8SiIiYGfJb7Z6fchDcHAjGOiH8b/L5Uc6M9\nhZ1FS/pQbyRDVdMb7k4vtF8lYcN1/coCc6ujF5q1VSjWrbF2k5e6h48W9Nb8lRjL\n9itoDpJCHcNbolyqawxh/5YeJZc4tpIfJ2cRdDNQCYd+wfYSF9mS93SjidfOfr1D\nQppmquOWlori4gyeG4s5bmpmiBjuo9hs0a6zXl9tjR1OmJ476CKdIBj7YnDPJQd7\nujw9lacy/Nfu1eJiKGQSAuYg76NXyU/Z0LoWhcnYFdiPLTjE4NPJmfX/rGTFQqdI\nscwhFbHjh5lbKZEL/gyY\n=a4VB\n-----END PGP SIGNATURE-----\n", "payload": "tree 794c5d1231468069af7592eef0406fe97192d0c9\nparent b8589513c18564f11f014704a1b939f149b4511e\nauthor Komu Wairagu <komuw05@gmail.com> 1754239378 +0300\ncommitter GitHub <noreply@github.com> 1754239378 -0700\n\nAllow users to set config values through environment variables. (#2543)\n\n* - Allow users to set config values through environment variables.\n- Fixes: https://github.com/Tautulli/Tautulli/issues/2309\n\n* Prefix environment variables with `TAUTULLI_`\nFor: https://github.com/Tautulli/Tautulli/issues/2309\n\n* Update handling Tautulli environment variables\n\n* Add log message when environment variable not saved to config\n\n---------\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "verified_at": "2025-08-03T16:42:58Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5ecd570f95552e4e2c0c7b18ef8e12f26dc021ef", "html_url": "https://github.com/Tautulli/Tautulli/commit/5ecd570f95552e4e2c0c7b18ef8e12f26dc021ef", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5ecd570f95552e4e2c0c7b18ef8e12f26dc021ef/comments", "author": {"login": "komuw", "id": 5163857, "node_id": "MDQ6VXNlcjUxNjM4NTc=", "avatar_url": "https://avatars.githubusercontent.com/u/5163857?v=4", "gravatar_id": "", "url": "https://api.github.com/users/komuw", "html_url": "https://github.com/komuw", "followers_url": "https://api.github.com/users/komuw/followers", "following_url": "https://api.github.com/users/komuw/following{/other_user}", "gists_url": "https://api.github.com/users/komuw/gists{/gist_id}", "starred_url": "https://api.github.com/users/komuw/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/komuw/subscriptions", "organizations_url": "https://api.github.com/users/komuw/orgs", "repos_url": "https://api.github.com/users/komuw/repos", "events_url": "https://api.github.com/users/komuw/events{/privacy}", "received_events_url": "https://api.github.com/users/komuw/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "b8589513c18564f11f014704a1b939f149b4511e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b8589513c18564f11f014704a1b939f149b4511e", "html_url": "https://github.com/Tautulli/Tautulli/commit/b8589513c18564f11f014704a1b939f149b4511e"}]}, {"sha": "443eb8da155d98a314c7fd462f1e1831e996e546", "node_id": "C_kwDOAducuNoAKDQ0M2ViOGRhMTU1ZDk4YTMxNGM3ZmQ0NjJmMWUxODMxZTk5NmU1NDY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-08-03T17:09:49Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-08-03T17:10:01Z"}, "message": "Disable browser autocomplete in notification and newsletter configs\n\nFixes #2557", "tree": {"sha": "a018cb332da7b07b84c7cf9be4d90f17a04276f6", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/a018cb332da7b07b84c7cf9be4d90f17a04276f6"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/443eb8da155d98a314c7fd462f1e1831e996e546", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmiPl+kACgkQsfH5gHGE\naXqFvA/+Lctlu6gYDw0DHyrSEVdk6Z2Dw4sxM8jzvsyKwzAUgFoFzDUQBwJmlmAO\niUm1QhEMZfhbSg1KeEYPFNKnhLjzHKYwuKJ39LGoCBBajdCwroJTX2zCOkuIREay\nWzH3sjQb1rHY971vawJYRcPDsB5gxN3odhm7cmYKltzHvYAmCe5/sPI4OerhRwsM\nS3Fq3MF2u0oQHU4N6a18A5YoFFGuprN2APoCq4J8rl4tg+Sx/FrrylxyBgg1KSts\n+kYE88D+s+OS0b/4vL9ieBnqaw1iA7FITfkeErBLDszsVhBorNcu6ma5gBLWbYpg\nFwT9CUPvl0Jg94fyC+pWKuQh7iAyWUhST6+GkuZ8gEJKqReeIrnzh9Ic64Rs5oZa\ndrd0+KsN9r4modgd7F3gnaWczOFhjxyXO9hVGJCWLoG37ghPyXPCt28txh56xD2R\nlop96gbAAths0RonScERwAH0BeniC3wfnCgePV5oVezSwBEA+vCrEyC7fWXs3kMI\ngrxIPOVrgdSqyPBUL1+LPiij/aID9cSZ7twnPQbl5fUBd7NXkTMKzmn+XmkL62zD\nfi3Ebb9m4nJhsPQDyc6lyEplsxi+yhc9RD6wGHOAjixc6tZ+MwpTSkmsfhByaeqg\np0Xc6cxVDGwYsE42TXnQFbYagSN9ttE0IFP0/ZiOdqE74u8FBao=\n=cLhf\n-----END PGP SIGNATURE-----", "payload": "tree a018cb332da7b07b84c7cf9be4d90f17a04276f6\nparent 5ecd570f95552e4e2c0c7b18ef8e12f26dc021ef\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1754240989 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1754241001 -0700\n\nDisable browser autocomplete in notification and newsletter configs\n\nFixes #2557\n", "verified_at": "2025-08-03T17:10:45Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/443eb8da155d98a314c7fd462f1e1831e996e546", "html_url": "https://github.com/Tautulli/Tautulli/commit/443eb8da155d98a314c7fd462f1e1831e996e546", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/443eb8da155d98a314c7fd462f1e1831e996e546/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "5ecd570f95552e4e2c0c7b18ef8e12f26dc021ef", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5ecd570f95552e4e2c0c7b18ef8e12f26dc021ef", "html_url": "https://github.com/Tautulli/Tautulli/commit/5ecd570f95552e4e2c0c7b18ef8e12f26dc021ef"}]}, {"sha": "1144bba580fff1fbb220e104b9e66be78482842b", "node_id": "C_kwDOAducuNoAKDExNDRiYmE1ODBmZmYxZmJiMjIwZTEwNGI5ZTY2YmU3ODQ4Mjg0MmI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-08-03T17:17:15Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-08-03T17:17:15Z"}, "message": "v2.15.3", "tree": {"sha": "1974a34edeb4dfab4394e033785736ad40cad930", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/1974a34edeb4dfab4394e033785736ad40cad930"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/1144bba580fff1fbb220e104b9e66be78482842b", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmiPmZsACgkQsfH5gHGE\naXrkJw/9HOOyeQwxyZZ92H2Ky4jxILW3/toZ7bo2D9hoYallGk2cAAyfVBrxtKwt\nnvr9DYJYIff4vGLtry1pYMwK20VCZLW2GTCEbdRLS6+QW+BceMYIZOVFpYapTpXW\ndRf3ecICsYFCYLHm9h+AevmJIW0gpFvttnJwqR5JzuGDb4WaWlMpRfxVtvdNIh7p\nR6c5Fa/i3GMJlett6uJmDxGw+nEyl9i436RNXJI/36tnSjFejkqjlzBCiuMa86Rx\n/wCDsUERCx2tdzSBnQ/IoKHxrE96h1ifQtCFquJhzhSHfSETCKmzKXzeMMrL3cWY\nAYUPB7U99ltEa231URExIjcAQ0VDMf/ytXkrzsouvhy8Y7ybJykZU1nSHhI2hoh6\n8eutm1FOu7xI68Pk8raL0QiyHbeqGYdbgq0hX84YldMoLL1ZcyEcRx7bEhOXBnNv\n0nLaFRRQGBkJrTaIl7gX7EChJmBF3hjGgfH6ljV3D7mfqm5L0qrAadOOndHII9f/\noXnDFBe1uK02XuthoIaZN5jZewALRxIyt+pDisvwZQk9WqbplRexHxDJuGOPr6U0\nhBBZdpoWJU+2urxZtdwynEgiysxgQkN0CeL+VYRyxreC9gNteUlL4j3n3tFOew/M\na5uKrfngFO92g+c9cPJV+c+DBWoiI9NcBjJxW1s5uO2bBVlT+T8=\n=YdV/\n-----END PGP SIGNATURE-----", "payload": "tree 1974a34edeb4dfab4394e033785736ad40cad930\nparent 443eb8da155d98a314c7fd462f1e1831e996e546\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1754241435 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1754241435 -0700\n\nv2.15.3\n", "verified_at": "2025-08-03T17:22:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/1144bba580fff1fbb220e104b9e66be78482842b", "html_url": "https://github.com/Tautulli/Tautulli/commit/1144bba580fff1fbb220e104b9e66be78482842b", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/1144bba580fff1fbb220e104b9e66be78482842b/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "443eb8da155d98a314c7fd462f1e1831e996e546", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/443eb8da155d98a314c7fd462f1e1831e996e546", "html_url": "https://github.com/Tautulli/Tautulli/commit/443eb8da155d98a314c7fd462f1e1831e996e546"}]}, {"sha": "47566128e2e5dde98980d59b7a51b98173bc0b40", "node_id": "C_kwDOAducuNoAKDQ3NTY2MTI4ZTJlNWRkZTk4OTgwZDU5YjdhNTFiOTgxNzNiYzBiNDA", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-01T19:22:40Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-01T19:22:40Z"}, "message": "Validate image path is a subdirectory of Tautulli's resource directory", "tree": {"sha": "44a665a6fbdee878820f56078c5e0055893eeada", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/44a665a6fbdee878820f56078c5e0055893eeada"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/47566128e2e5dde98980d59b7a51b98173bc0b40", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmi18oAACgkQsfH5gHGE\naXrrXxAAnymHfMkF84Bs3ZTLCcRe3NeiBn/4oiX/RK3E2H8UlAENhUKLCoIU5Sey\nGDVWCrHqbeuPs/DILFrLixLjvye5WtND6vYmfZW3FcgA8Nf+F/0q52QEhurR9x6K\nbdoKFTGHD/qkraQUcXgkqPQJbnzHPNILo5/cPEmQCR6dDkIUXYqv48jE5G7zWBhI\njjIN4yQgMUuibXPjFO5IZWOLcH8hBa2ZQgL86TIwGQPK+tDSpvkwiQ0wlLL0jQCj\nGYGdfWpMxOOydNGiq4XxdaUbruMlEDD4ERFjc/EykpZ6uisUFNPfe4rqnoL/rZKh\ne3kKfqQnS1QCd92ZPsFDZDw3JrkvOun1GXtwAcL1TMyGGYS1L5rJ+E2XqrUhX14f\nvTme5GhicHCh1NXa9eFNv1ENoD3CSa1CjeNJJGdKND37lYytk5IBDbsWxSQUbqIn\nsiVVmWSMHMsBb4jnq7LVh2QqcJZgjd8pjBZ5A6WNt2Fsh34qmXNxPsdLT4yl+wlF\nFbIrOhkbdH4FZyi4Q41OZl4jnjPuowfEnQD5TjOEO22WL9KBhHLvYM+vhbtA3v7E\nTVTGCkBIYAH9sFn7QunVP6nmrD/OaexApbTRFOpL/psoeQ8HHUd954JyFtNNGlht\nSZy1wSUR0Y5nQqK2Oy/+1AkcUDxV8iLkPSiUQUR/mukdcu8CiXk=\n=ydVv\n-----END PGP SIGNATURE-----", "payload": "tree 44a665a6fbdee878820f56078c5e0055893eeada\nparent 1144bba580fff1fbb220e104b9e66be78482842b\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1756754560 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1756754560 -0700\n\nValidate image path is a subdirectory of Tautulli's resource directory\n", "verified_at": "2025-09-09T00:56:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/47566128e2e5dde98980d59b7a51b98173bc0b40", "html_url": "https://github.com/Tautulli/Tautulli/commit/47566128e2e5dde98980d59b7a51b98173bc0b40", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/47566128e2e5dde98980d59b7a51b98173bc0b40/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "1144bba580fff1fbb220e104b9e66be78482842b", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/1144bba580fff1fbb220e104b9e66be78482842b", "html_url": "https://github.com/Tautulli/Tautulli/commit/1144bba580fff1fbb220e104b9e66be78482842b"}]}, {"sha": "ec77a70aafc555e1aad0d9981f719d1200c117f1", "node_id": "C_kwDOAducuNoAKGVjNzdhNzBhYWZjNTU1ZTFhYWQwZDk5ODFmNzE5ZDEyMDBjMTE3ZjE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-01T19:29:57Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-01T19:29:57Z"}, "message": "Validate image format in pms_image_proxy", "tree": {"sha": "3653de68bf103bff7bb86619cb5da0284c1c6ed1", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/3653de68bf103bff7bb86619cb5da0284c1c6ed1"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/ec77a70aafc555e1aad0d9981f719d1200c117f1", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmi19DUACgkQsfH5gHGE\naXq8xxAAiYG1Mi3vKNE8fldEpByrszI0bBxbE9QBbOFj9MFQEIdDLsdqj/PkV3XI\nhaKE60U4wN2IxlaDt9bVB/24mE00y9Z6h5/SmG+al+Cu9k3i/zDmHw/qrerxR0AA\nlCcrPV3qUuf3uHCzsayICcAOygMRYQeph6ExtWYGvRqfpIXJrPLlV7sime3l35Yl\nCJk7LnalIm3l7YUhy7pDCQg4EQw2yFBNZ7QwAG8b/wPsq5bxeyWsEsiH6fTixIPu\nOFEb/AEqeV+2/pjDKDoQIs+Pnl0EWADk5qgmYqu+dG9uRgzS2mscxR6Bwxh0WZQy\n9d5yyfav6pwIM2bdoaakDxJ0E++BskqLHzE10YD4AsMrraIsnwW8KGV4C+mFUhK+\nl2AZs+agvipLZkq2uJGTEfV9NYLAoYGEqjnPfEbHXWMAY251w5eh8vmIF+p/UecB\nqAVYItDKxoOOqJ5GfMNn2z7QtKJbmf2HI+Ls/9QtiR2sibfirObvQn1lTWKU3+c/\nM2yeANQHjWsXDpFlv2k4HkIPICe6O9AAyGoeqMHS0yZUGEiYH52XHb4QqZlY+wCD\nPt9FXj9y8ViKuLUe+m63KojIw/I+DS4q/rDqFqCGhVjkXTnikmQtzGrD1Ad7GnI/\n5AOcpZqn+dHPz5I/1wqEZ3h70bT9hNMX4HnGL5bi9hKNo6hv0/g=\n=Hjg7\n-----END PGP SIGNATURE-----", "payload": "tree 3653de68bf103bff7bb86619cb5da0284c1c6ed1\nparent 47566128e2e5dde98980d59b7a51b98173bc0b40\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1756754997 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1756754997 -0700\n\nValidate image format in pms_image_proxy\n", "verified_at": "2025-09-09T00:56:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ec77a70aafc555e1aad0d9981f719d1200c117f1", "html_url": "https://github.com/Tautulli/Tautulli/commit/ec77a70aafc555e1aad0d9981f719d1200c117f1", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ec77a70aafc555e1aad0d9981f719d1200c117f1/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "47566128e2e5dde98980d59b7a51b98173bc0b40", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/47566128e2e5dde98980d59b7a51b98173bc0b40", "html_url": "https://github.com/Tautulli/Tautulli/commit/47566128e2e5dde98980d59b7a51b98173bc0b40"}]}, {"sha": "5a484252becdd7b098988805625bc614cf39a281", "node_id": "C_kwDOAducuNoAKDVhNDg0MjUyYmVjZGQ3YjA5ODk4ODgwNTYyNWJjNjE0Y2YzOWEyODE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-01T19:53:23Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-01T19:53:23Z"}, "message": "Don't run git command with shell", "tree": {"sha": "183b3a24407e6b8b14c11309bf69d5e4b4e14e49", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/183b3a24407e6b8b14c11309bf69d5e4b4e14e49"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/5a484252becdd7b098988805625bc614cf39a281", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmi1+bMACgkQsfH5gHGE\naXr8RA/+J1Nlaz0V8NIqkaxiQDwzEwHUp1hCEVSq/JDBwv15JC9IJySWYyb+0i0u\njsk0aSlfI0dBfoirvLSWpsA72EYzMoa62uZI1DgEkm69NODPj13waZEljd3Kr2QS\nHXbwBxhkBPoU8XRvT8UEcw6r47uLd9cmhFco71ew6ut4DlexWHCl3Rg7H80cf1AW\n5oDhO+KgdZ3Hzz2unkx9WpORxm4OJWMxfcJ3+bR+noaLfHd2Ji5yyE/sEgv6nLBo\nwfosmPe4utIv+qaXgWeLEmMT40B15HTe8XxhEKKYjz9NNSQtag0o9gCe2oUTPiOD\nQ/tDcOrlMAeC4tyyYPlSoXmaGw+y7ZRxPWs5EaKnkEj883HO8ivrpZqT3cGlzSFw\n21FFLqxLuEXIOq9IunVzhmYdubtFp6kjeNMYQT6DxcIqEAwxlCEvPqVpa5DWdS1E\nOB7S+E8cOQ/8n6eD7l6eCNsrkbBTo6DGn7nff/lzfTRfGKLFljyZEp7a9cli5423\nVc+6BbzHAyLDpNuwc3lYcoKSCV3yUXh0+rY+CvAKFZ63kDDHqiumU3CGMWr88Z4g\nXf5dqVDlBXYjno1pZHvw9NzzHKXrvWcVNpKNKR2KOdq63MIBGUpWAAImnAXJP+Xw\nNPtrdljerL5Ue/t+hp8QGpdpRaRSepcDEd3qobLMPEAfVGQTO4Q=\n=VSUV\n-----END PGP SIGNATURE-----", "payload": "tree 183b3a24407e6b8b14c11309bf69d5e4b4e14e49\nparent ec77a70aafc555e1aad0d9981f719d1200c117f1\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1756756403 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1756756403 -0700\n\nDon't run git command with shell\n", "verified_at": "2025-09-09T00:56:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5a484252becdd7b098988805625bc614cf39a281", "html_url": "https://github.com/Tautulli/Tautulli/commit/5a484252becdd7b098988805625bc614cf39a281", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5a484252becdd7b098988805625bc614cf39a281/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "ec77a70aafc555e1aad0d9981f719d1200c117f1", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ec77a70aafc555e1aad0d9981f719d1200c117f1", "html_url": "https://github.com/Tautulli/Tautulli/commit/ec77a70aafc555e1aad0d9981f719d1200c117f1"}]}, {"sha": "26e6b328112eb2cf35c164f981e0718f3a3d31a7", "node_id": "C_kwDOAducuNoAKDI2ZTZiMzI4MTEyZWIyY2YzNWMxNjRmOTgxZTA3MThmM2EzZDMxYTc", "commit": {"author": {"name": "Keith Herrington", "email": "keith@hadm.net", "date": "2025-09-05T05:45:15Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-09-05T05:45:15Z"}, "message": "Fix race condition in image cache directory creation (#2580)\n\nReplace os.mkdir() with os.makedirs(exist_ok=True) in real_pms_image_proxy\nto prevent FileExistsError when multiple threads try to create the same\ndirectory simultaneously.\n\nFixes the following error:\nFileExistsError: [Errno 17] File exists: '/config/cache/images'\n\n\ud83e\udd16 Generated with [Claude Code](https://claude.ai/code)\n\nCo-authored-by: Claude <noreply@anthropic.com>", "tree": {"sha": "5dd4c947ba5000f39c04d0f121b296fda7921774", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/5dd4c947ba5000f39c04d0f121b296fda7921774"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/26e6b328112eb2cf35c164f981e0718f3a3d31a7", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJounjrCRC1aQ7uu5UhlAAAtfAQACB9/ePLwW7ixrwivJqCW56x\noEDVk1nwjWNLA5f+QRdYAtRQ/y6AJn7azIqlCSwD1K6XmVNmEPYQZ5YK2T8iSrh2\nR+jH26wlzjBfzQx64TOsksaX/1+bfa/m4Pd3+ON8ISlkLMEicSEvxXjSLNo+mX1T\naeTG+SZwFnZtDzmQ1Bim6P5K8l5PL3l0p9q+Hr6n7kWzZKWMZl1XRNdp02OHw9IM\naxpgq88rZDpQc0iu7SDZg0Q7P9x8nCCpoXndPA4a0v+POwLtqPLyrTikKXyP7Pz+\nOBwSOO68gpaL7dRFwkTfxejEMpCdyo0+51Kp8jbC+3vldkqJJVSwML9wee+i1Td1\n5zR8kS0eicL4tbBFi2591q1oZi8yCTcGxjZ+trd8ZtL8u6hsdBibCqY2TT7v4hg4\nSJrIeVnsY88gCyKBHpXt625SkVejcyl7wlLDkCqjO8If38AfvcQS+w9IMoQeQSdB\nJRQBTEbjRfFuuzKpJLO7+iGfU6nAHAT4UmvU3RZk/t91Ovy6V6sXV7LzqQtHxBki\nnkZKsieNbOqIJPpxbo+IsGSScEKHXmWEG7g12WqZGfp2+7YP2AW3sSIhknPCMkAC\nUJ3rxHcGhHc3QGbeRf7tQ7pqVk6rlugaSxyQLJIEbvQOdQu0rZOc4znU7li7eKGR\nlaWWx+uF69N0OPPLx6Jd\n=itLR\n-----END PGP SIGNATURE-----\n", "payload": "tree 5dd4c947ba5000f39c04d0f121b296fda7921774\nparent 1144bba580fff1fbb220e104b9e66be78482842b\nauthor Keith Herrington <keith@hadm.net> 1757051115 -0700\ncommitter GitHub <noreply@github.com> 1757051115 -0700\n\nFix race condition in image cache directory creation (#2580)\n\nReplace os.mkdir() with os.makedirs(exist_ok=True) in real_pms_image_proxy\nto prevent FileExistsError when multiple threads try to create the same\ndirectory simultaneously.\n\nFixes the following error:\nFileExistsError: [Errno 17] File exists: '/config/cache/images'\n\n\ud83e\udd16 Generated with [Claude Code](https://claude.ai/code)\n\nCo-authored-by: Claude <noreply@anthropic.com>", "verified_at": "2025-09-05T05:45:16Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/26e6b328112eb2cf35c164f981e0718f3a3d31a7", "html_url": "https://github.com/Tautulli/Tautulli/commit/26e6b328112eb2cf35c164f981e0718f3a3d31a7", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/26e6b328112eb2cf35c164f981e0718f3a3d31a7/comments", "author": {"login": "keithah", "id": 684920, "node_id": "MDQ6VXNlcjY4NDkyMA==", "avatar_url": "https://avatars.githubusercontent.com/u/684920?v=4", "gravatar_id": "", "url": "https://api.github.com/users/keithah", "html_url": "https://github.com/keithah", "followers_url": "https://api.github.com/users/keithah/followers", "following_url": "https://api.github.com/users/keithah/following{/other_user}", "gists_url": "https://api.github.com/users/keithah/gists{/gist_id}", "starred_url": "https://api.github.com/users/keithah/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/keithah/subscriptions", "organizations_url": "https://api.github.com/users/keithah/orgs", "repos_url": "https://api.github.com/users/keithah/repos", "events_url": "https://api.github.com/users/keithah/events{/privacy}", "received_events_url": "https://api.github.com/users/keithah/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "1144bba580fff1fbb220e104b9e66be78482842b", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/1144bba580fff1fbb220e104b9e66be78482842b", "html_url": "https://github.com/Tautulli/Tautulli/commit/1144bba580fff1fbb220e104b9e66be78482842b"}]}, {"sha": "74e4991535d540e3031e0d1f79179220ea024a61", "node_id": "C_kwDOAducuNoAKDc0ZTQ5OTE1MzVkNTQwZTMwMzFlMGQxZjc5MTc5MjIwZWEwMjRhNjE", "commit": {"author": {"name": "TheMeanCanEHdian", "email": "drivard100@gmail.com", "date": "2025-09-05T05:47:35Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-09-05T05:47:35Z"}, "message": "Update to new Plex logo. (#2584)", "tree": {"sha": "8e4a9f6eb1f0aa64cf92e93a332fdd61e38bfc9b", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/8e4a9f6eb1f0aa64cf92e93a332fdd61e38bfc9b"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/74e4991535d540e3031e0d1f79179220ea024a61", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJounl3CRC1aQ7uu5UhlAAAn3UQAKkeILLuhHyjKd56R5q7XcoW\nj0UcR1jeI6nnDLsoKGfQyiQnSGfYULRB0oKfBJef4/0k1rnr8mbIiYEhIpdYGTaA\nGjaA4xfHpoHoXAnThidczTolVcv7QD6VkrMtWgyShWBnXV0h7efNa7jQgVdJV5rZ\nuYl8nj2f6xSQbUcaHCIFcHm/Nx1D/7G/qG/b22+WZ5au3sn7TUwBJHGxpc1zFhYn\nVxFlTrzBQv7yFAQ9NjW1yt9/aLisEeuV+WeogzXk0lOQTqKWzGLozKo85uX5G4E7\npv89tqApCvxAmMTfz1IXIDsaZT1NR5LbELe4wbePNhi/cLz53rIERExU+8qoYsZe\nVX3mkPAg6jZn77GHYOL3i+OCL5A3DWUyRWGzEk1Kvj7etbHJubtN6u8ftJ+8Yu9u\nullJrP40Ho+n1NIvszPvKWsJ8A50x7kRr9BeH1xUpafPd2mbrFbLYFBrvJglBJx0\nlMwCOxWCTKPKCv2WYzdmBIb1uotRyD7CCUhUD+u+d2pxDxpFGyL5ZKkvdp4A3ccu\nEBremJp6cUci0gZCTmISlgGj5tnS9tyju96v1FMou/a5xxIxH1LP70jd/oGORRoX\n/884+MnU+mli5YvddB2buiRJYzd1rNRoC1Bf25OV5Qyp3rMfKPpKote8rHV0wARk\nZaBCAZd5Y0eCB9gWcaGw\n=eE5p\n-----END PGP SIGNATURE-----\n", "payload": "tree 8e4a9f6eb1f0aa64cf92e93a332fdd61e38bfc9b\nparent 26e6b328112eb2cf35c164f981e0718f3a3d31a7\nauthor TheMeanCanEHdian <drivard100@gmail.com> 1757051255 -0500\ncommitter GitHub <noreply@github.com> 1757051255 -0700\n\nUpdate to new Plex logo. (#2584)\n\n", "verified_at": "2025-09-05T05:47:36Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/74e4991535d540e3031e0d1f79179220ea024a61", "html_url": "https://github.com/Tautulli/Tautulli/commit/74e4991535d540e3031e0d1f79179220ea024a61", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/74e4991535d540e3031e0d1f79179220ea024a61/comments", "author": {"login": "TheMeanCanEHdian", "id": 16025103, "node_id": "MDQ6VXNlcjE2MDI1MTAz", "avatar_url": "https://avatars.githubusercontent.com/u/16025103?v=4", "gravatar_id": "", "url": "https://api.github.com/users/TheMeanCanEHdian", "html_url": "https://github.com/TheMeanCanEHdian", "followers_url": "https://api.github.com/users/TheMeanCanEHdian/followers", "following_url": "https://api.github.com/users/TheMeanCanEHdian/following{/other_user}", "gists_url": "https://api.github.com/users/TheMeanCanEHdian/gists{/gist_id}", "starred_url": "https://api.github.com/users/TheMeanCanEHdian/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/TheMeanCanEHdian/subscriptions", "organizations_url": "https://api.github.com/users/TheMeanCanEHdian/orgs", "repos_url": "https://api.github.com/users/TheMeanCanEHdian/repos", "events_url": "https://api.github.com/users/TheMeanCanEHdian/events{/privacy}", "received_events_url": "https://api.github.com/users/TheMeanCanEHdian/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "26e6b328112eb2cf35c164f981e0718f3a3d31a7", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/26e6b328112eb2cf35c164f981e0718f3a3d31a7", "html_url": "https://github.com/Tautulli/Tautulli/commit/26e6b328112eb2cf35c164f981e0718f3a3d31a7"}]}, {"sha": "8bc5533d04fcd97a5136759f825f6507aafe2809", "node_id": "C_kwDOAducuNoAKDhiYzU1MzNkMDRmY2Q5N2E1MTM2NzU5ZjgyNWY2NTA3YWFmZTI4MDk", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-08-03T17:28:35Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-05T05:51:11Z"}, "message": "Update urllib3==2.5.0", "tree": {"sha": "49114b06c8e6fa3d8bbcad19999905ccac70c5c2", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/49114b06c8e6fa3d8bbcad19999905ccac70c5c2"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/8bc5533d04fcd97a5136759f825f6507aafe2809", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmi6elEACgkQsfH5gHGE\naXoB4g/+MEiSqT/geqeYaBf8riSySLI1KOPu4VQDfOpj86+uPQDvwCXymmooLotq\ngSUzEuNz5bZjxonIKG6JwYD3xubWHnDpJLiFSnqci01ZeG4isH4FY4S3X3oTGySJ\n8zl2tSO1v38ncNBv3q6dxn1ruBAs6NARV9fKZ4ZMEEF1wsx6EuE3OQRMSM2NFI1G\nI+v1K8rAyA/sferulZSrUNA5CAX++7zK2lV2h8xe9wn/3LvKOTwIpq1ZbKl4D5D2\nMMUqSaYiC/wGoFJJ3p2p3lqRbA4tzz6vRcjGvVrFGEcJuRy83192kNAz7NtWcgwu\nkq3bcqy8kcduweHhyvOnnqb0P7WqqxiGrxaFrkODAGuNp9lr2yG0hn218xT+J6Iw\npAGUOt7s9XWghbRUxVK+S8rn/8L/2Bu7L499AY/IkcWd7e8nqTf6teQgEObDMY6l\nR34y85eG33kbUhUBrhYIHCoPHfHEcBwVCiYAZjRvIEhnfAf0rHaiLmq7Rf7WVgDS\nwLivVsfGppv1X67TYuDE5AiosMPK5DIvrgJREwUai3lCggyrH74nJKkoFUgT4Yux\nZWJvJWclH3thzA0Sr2STBVaIIX6KBeLo1Ad4NJNMJs6EGk+UClVStwNHZVAfLmjA\nxhlp+J/XDdSQCjDdUU6A6US+18sGe8jqFLrFP+mYWfyPwT0iPEI=\n=wcFi\n-----END PGP SIGNATURE-----", "payload": "tree 49114b06c8e6fa3d8bbcad19999905ccac70c5c2\nparent 74e4991535d540e3031e0d1f79179220ea024a61\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1754242115 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1757051471 -0700\n\nUpdate urllib3==2.5.0\n", "verified_at": "2025-09-09T00:56:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/8bc5533d04fcd97a5136759f825f6507aafe2809", "html_url": "https://github.com/Tautulli/Tautulli/commit/8bc5533d04fcd97a5136759f825f6507aafe2809", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/8bc5533d04fcd97a5136759f825f6507aafe2809/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "74e4991535d540e3031e0d1f79179220ea024a61", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/74e4991535d540e3031e0d1f79179220ea024a61", "html_url": "https://github.com/Tautulli/Tautulli/commit/74e4991535d540e3031e0d1f79179220ea024a61"}]}, {"sha": "55b4d0b8f68cea187c4d0e7f7277d2a044b2d433", "node_id": "C_kwDOAducuNoAKDU1YjRkMGI4ZjY4Y2VhMTg3YzRkMGU3ZjcyNzdkMmEwNDRiMmQ0MzM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-08-03T17:31:42Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-05T05:51:14Z"}, "message": "Update requests==2.32.4", "tree": {"sha": "161681344b451ac02ec80f83e613994e9e261bd9", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/161681344b451ac02ec80f83e613994e9e261bd9"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/55b4d0b8f68cea187c4d0e7f7277d2a044b2d433", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmi6elMACgkQsfH5gHGE\naXpLIA//ZvBjO3isV0ZzVjhbhskAng3KLV2Uo+musR2zLmzuZPtxzID3CQJ1SBbZ\nmiUDXBBsyl5ZXJeFSxYyJNLyfOuK15DPY9oLM++JL3IiqmKt3e3EvsfqQg5X8SJF\nbTaVT5SXB0HQzP/CwHViJ+FFRUxug1am8xqZZa8zhxgilvT4sV8NbVGggkt9fqOy\nRJZDk17UWn5INgy7n9jwHHYV8fWMn3RCk4NVs0cy3JeaIQ+Y25gDPGprg/EGc/pd\nOqiewHs4/6VLT8xrQkI0F2Kl+7Bz/vxUKBBtx54AHeqA4X8WUA+c85yiTtATXr0Q\nwEmTUNz6JWoxbAp7kK3wVuW9ieiN7yNevOQeoPbrQZwbZ2QBFNd7eF4x1icYMJ9z\nK3Dg9v3OzDAInArNHh/G9EJpFEpomz1q5AxW+JFfG0TzM3bkf2cNIzjYQEhHvFEa\nd9jeE246XuyPqMEZF0sWBhBnRbPZympbq1hlx2DiguQf9PdedyI4TDqfg/sXBSGl\n1idgIEQLNBX4LIm2MLugg5rcXH3WrcbSs533DdIrh1jQF7fgHbEPqTQhcTgxTLgE\n11ceUy8tSCeSaIKvxydslpFgxkF5CBAncFwXT38Ea6tbdgTfSuDHnqlrgB7JuWIJ\nLUQPiVEW/v4oKV+OmyK8rGrWAk7rsAkdtJF9X+CQqF1rspOdCcI=\n=9X+n\n-----END PGP SIGNATURE-----", "payload": "tree 161681344b451ac02ec80f83e613994e9e261bd9\nparent 8bc5533d04fcd97a5136759f825f6507aafe2809\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1754242302 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1757051474 -0700\n\nUpdate requests==2.32.4\n", "verified_at": "2025-09-09T00:56:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/55b4d0b8f68cea187c4d0e7f7277d2a044b2d433", "html_url": "https://github.com/Tautulli/Tautulli/commit/55b4d0b8f68cea187c4d0e7f7277d2a044b2d433", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/55b4d0b8f68cea187c4d0e7f7277d2a044b2d433/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "8bc5533d04fcd97a5136759f825f6507aafe2809", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/8bc5533d04fcd97a5136759f825f6507aafe2809", "html_url": "https://github.com/Tautulli/Tautulli/commit/8bc5533d04fcd97a5136759f825f6507aafe2809"}]}, {"sha": "bf62ce4b105cad2a22d2a10c355bab665e43207d", "node_id": "C_kwDOAducuNoAKGJmNjJjZTRiMTA1Y2FkMmEyMmQyYTEwYzM1NWJhYjY2NWU0MzIwN2Q", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-08-03T17:32:01Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-05T05:51:15Z"}, "message": "Update certifi==2025.8.3", "tree": {"sha": "74d4bb838689e9cefc73549aa18f94d1f8edea36", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/74d4bb838689e9cefc73549aa18f94d1f8edea36"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/bf62ce4b105cad2a22d2a10c355bab665e43207d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmi6elQACgkQsfH5gHGE\naXoHQQ/+KL7P3lpxz5hN/BnM0U6enqSF1iKwYDNxhUV7Sih/Gf5H2hbOe/yzekwY\nQT3W86gIwYmmRMPskZ9+eZGsN6+6pN6FGYkkbf08EDvTuTJchy0bXyrS/w9C+MVQ\nDSD2lDahcRBUESmn+vMcIQ4kqvSaCo+L5LCt0syLuGAMpPqk7IV8q73BTJ5DTJ12\n3FgDwmQGnyc2wHAw/8GbOChG09XU3bHhRU01muXLoa40FvE8xe7souBh4rItaL42\njnGldc05zlCzDqb1VD2eXyc/PktQ+sPZwhvG1Qj9TzxazHVxY9ax3qWMWwtW6VuM\n/MvyLg5vChlRWBAiFpiFXFUSecAr8TjrOi8xmtXcMG49+9dCm0YPc6dydXsAUWd0\nZcQ4NFwHkYZm/y4ewGpWyLMA5piJx1FvGzaJSBGhf98GHmda//McaLvU4VBGtvI+\no75W7gYYLq1cBgKlIashfzB68Lv08U76C/8tj03JmD6CrM2gw6i+w0TLOZ446KTI\n6Xg5lpMUoEBLDbbDII13XF4c7x02xSlaEJVPdxJuk78EQ8Ik5Pc+9VmdFh5xM+4C\n3qoaTr+r2/izfCEIStz/ipyssZfBdq+sXkgsIKK/ELjEObncL7RtFVINSX+4MmGB\ngs232wTieXxtybsrd8oOh7HORNkZKge7yXLA3FSSB8h44p4fPBo=\n=lVhW\n-----END PGP SIGNATURE-----", "payload": "tree 74d4bb838689e9cefc73549aa18f94d1f8edea36\nparent 55b4d0b8f68cea187c4d0e7f7277d2a044b2d433\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1754242321 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1757051475 -0700\n\nUpdate certifi==2025.8.3\n", "verified_at": "2025-09-09T00:56:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/bf62ce4b105cad2a22d2a10c355bab665e43207d", "html_url": "https://github.com/Tautulli/Tautulli/commit/bf62ce4b105cad2a22d2a10c355bab665e43207d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/bf62ce4b105cad2a22d2a10c355bab665e43207d/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "55b4d0b8f68cea187c4d0e7f7277d2a044b2d433", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/55b4d0b8f68cea187c4d0e7f7277d2a044b2d433", "html_url": "https://github.com/Tautulli/Tautulli/commit/55b4d0b8f68cea187c4d0e7f7277d2a044b2d433"}]}, {"sha": "451156cf619aa6faae8f124ae22b40166904bafd", "node_id": "C_kwDOAducuNoAKDQ1MTE1NmNmNjE5YWE2ZmFhZThmMTI0YWUyMmI0MDE2NjkwNGJhZmQ", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-09T00:33:45Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-09T00:33:45Z"}, "message": "Merge remote-tracking branch 'ghsa-85wq-r3c5-p945/master' into nightly", "tree": {"sha": "0729970eb19f6e8edc3a88879596844816a7ae5c", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/0729970eb19f6e8edc3a88879596844816a7ae5c"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/451156cf619aa6faae8f124ae22b40166904bafd", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmi/de8ACgkQsfH5gHGE\naXoL5A/+L9MOljnHT+rYOCBzsqs97gb1ZQpq2HHg4AKmW9wgjL3QNV9+X90ee/Mi\n9yHC7lUZNIQXKN986mBL5UIbs3gYdw76F91dH4xqv3AGf5G4eTDrT3IPnBTQiWqf\nM6dazJlzxpd3mcrOFxztpgpa3UOZyljGClg2cJMfqci33WOYwHGjYLvZoJHMa535\nODhDPK+oDsjK4jlpsEQ65I5bPebO4/a6aZRGfZTso3drJoqzvdBvOT26KNKXRLWn\nBn01iTr+A20mr6eGyEUeMe5VOOm4tC4+vKr2aUy/lD1XIZrmcgig3pUy+BdRAmh3\nLyBTCmdDCfroLU6QgwUMj8lfmgfYnqEzn4SyRfqhTW2QZ3AUgn65yZQg12dQn4da\niOoMHHUfGvuqjsxIDNU3WzVWHEd0v2iqOq8DkZveBjUAY48BgRJLkyDJBCctPUXJ\nlAQnorSm0Ozk7ssuuTsl5zEQZkEkmMHWVCB4x3qF25yrZgs7fKb6zwNboHgy+LUT\ny8BQ+/Xo0F4MvPVVoiW1OjSuYw4o/89poQ22E7BcyPK48NEhfNMheBmviSP76G9s\nDkIColFNDepX7NZyIBQrF+oYjtxldD++C44H36MIGICcccfs0vjzeyzHg5hFHWJr\nk2iMwl4L5eZjGox7xbJJUeCWs7CR9IgSV1map8BSp0XcgDiqavY=\n=XbsK\n-----END PGP SIGNATURE-----", "payload": "tree 0729970eb19f6e8edc3a88879596844816a7ae5c\nparent bf62ce4b105cad2a22d2a10c355bab665e43207d\nparent 5a484252becdd7b098988805625bc614cf39a281\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1757378025 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1757378025 -0700\n\nMerge remote-tracking branch 'ghsa-85wq-r3c5-p945/master' into nightly\n", "verified_at": "2025-09-09T00:56:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/451156cf619aa6faae8f124ae22b40166904bafd", "html_url": "https://github.com/Tautulli/Tautulli/commit/451156cf619aa6faae8f124ae22b40166904bafd", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/451156cf619aa6faae8f124ae22b40166904bafd/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "bf62ce4b105cad2a22d2a10c355bab665e43207d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/bf62ce4b105cad2a22d2a10c355bab665e43207d", "html_url": "https://github.com/Tautulli/Tautulli/commit/bf62ce4b105cad2a22d2a10c355bab665e43207d"}, {"sha": "5a484252becdd7b098988805625bc614cf39a281", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5a484252becdd7b098988805625bc614cf39a281", "html_url": "https://github.com/Tautulli/Tautulli/commit/5a484252becdd7b098988805625bc614cf39a281"}]}, {"sha": "cc0150a1b2b745980467003a73aa1be51c1e426a", "node_id": "C_kwDOAducuNoAKGNjMDE1MGExYjJiNzQ1OTgwNDY3MDAzYTczYWExYmU1MWMxZTQyNmE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-09T00:56:39Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-09T00:56:39Z"}, "message": "v2.16.0", "tree": {"sha": "7cda6b5647ebc4e91d74b261b1f42f1f65aa58f1", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/7cda6b5647ebc4e91d74b261b1f42f1f65aa58f1"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/cc0150a1b2b745980467003a73aa1be51c1e426a", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmi/e0gACgkQsfH5gHGE\naXpYVw//UKNFDP68CNMJpEpR45T2ku8bCtcXlSXIEZulLO1V2PfM4dMHL1lFcr+S\nHav0wD7nCtOdBqfrtD46zUVViQckLe7UlAXdylGfAUU6DpuafNSBxqu8DSz8T+mB\nRH973TsDVBiihDWqqQgwjD+qa/38zFV0Wk8rnVp9SONXQ7CFJUZw0aGwhwInYAvZ\nTx9DcffoAVnJWwkjEF1nmGw6b3Cy9MIn/E4iY4shK2wKRAT7fXg0XKxj1GCal/Jp\n9gwLB6zroZQFsDWFyyd8mZk5cHWXeXJIhvbwvEyIX6RV0J5yuGLr500sL+KiP2Aj\nafPD/yhsmHtMbnTRxZa/yops5LUd+lnyfU80sipNR/97imQWdaafNB0RQyYHQQyX\nvDC/PyDDb8CSmKUEt+RAkk3wMDbGQsyxNoHI5c1DSw7aCZgJPBdr+npBezh2elv4\n3tuT8cnfYT6lhaYFFpquK++es68Hzx1mzl0XcUW2qMHqalTZ0TkUxHjwX40v9v9z\nNfoUEGJBvMD36r7/HvMewwIRgE2Dt40v6fXjcFZ7jrxA9pyeZ2JQNXrTo0xr/DJb\nurH/d3tOeCiTelgtaHurdYW8iWtZbxD2HzyCbBkWyIOCvZT2bJd7prgZ6agMKb/P\nJYjF2PY8N+hO8GAhB+uKzNSNeCmJgXfPaiq1N6wnpOMSxmfXc/0=\n=6SL4\n-----END PGP SIGNATURE-----", "payload": "tree 7cda6b5647ebc4e91d74b261b1f42f1f65aa58f1\nparent 451156cf619aa6faae8f124ae22b40166904bafd\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1757379399 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1757379399 -0700\n\nv2.16.0\n", "verified_at": "2025-09-09T00:56:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cc0150a1b2b745980467003a73aa1be51c1e426a", "html_url": "https://github.com/Tautulli/Tautulli/commit/cc0150a1b2b745980467003a73aa1be51c1e426a", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cc0150a1b2b745980467003a73aa1be51c1e426a/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "451156cf619aa6faae8f124ae22b40166904bafd", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/451156cf619aa6faae8f124ae22b40166904bafd", "html_url": "https://github.com/Tautulli/Tautulli/commit/451156cf619aa6faae8f124ae22b40166904bafd"}]}, {"sha": "de3132382b1a1a91d209aacb59cf76bde61583f3", "node_id": "C_kwDOAducuNoAKGRlMzEzMjM4MmIxYTFhOTFkMjA5YWFjYjU5Y2Y3NmJkZTYxNTgzZjM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-17T17:02:00Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-17T17:02:00Z"}, "message": "Update Plex OAuth polling\n\nChange Plex OAuth to polling with max retries instead of using popup detection due to `Cross-Origin-Opener-Policy: same-origin` set on the Plex login page.\n\nRef.: https://forums.plex.tv/t/plex-oauth-authenticate-with-plex-broken-after-plex-web-update-v4-152-0/931098/24", "tree": {"sha": "a6f132a3eca2e60730cc26c84b6dc5e656997062", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/a6f132a3eca2e60730cc26c84b6dc5e656997062"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/de3132382b1a1a91d209aacb59cf76bde61583f3", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmjK6ZAACgkQsfH5gHGE\naXpKsA/+P3oipmmo6y37ECCYLfFjLEY3Fsmo8Ca27mHebcau+fq3xV9ETCoPosgC\nny1Xg87WpHapH9muD5hBWymEULyu09vKzxLvYHtfJbJ8OAIME1e8sYc2RWf04UIG\nCnaCqrmPAw4eJcCLl3TNH8SXFuukIsNRiFzFTnzc3PY0P6kXJ4xP8Rx+P79ktJ+B\nRCrH9ajdhTEsw0kb8FnvlQyEB24gE4vOPg1cxEyrh0z91WwAPrOPzsfUoTOFfkU5\nh5hA8ItlVjbL2Anisr7D0BYEML5dox0+tkyhTxndtiOmkuH6EgHl5kQLaKD+TiKz\naV1zFz+zPsPzpXrnM54doYoSEXbDSyGjePVvS8TX6M84v408eBxTK5XoDJaDlljY\nN0mdNpNIlrk18Qlhbb6UP2yqLgwvRyV0A04hQ322ZNoe5zWv07WzCo/1CIJlOi3G\nI/Sng4BqgJh8orY/0kkBSJV0rUUAowyqYcwqC76p/ZTnmjQCvm1E6Xppi3xhb+Be\nGEdK419rJekQtQJojBZjqvCSE3H1F97SybTK8bz2a9tXeoEyAD2SkO9mQp3DVxt2\nZiyr+hy7w3GmhUHKwSi2nCdtvmqYsA7/wCbedDfQkb6PM1mQWvE8z8+yQyWmJfwg\njslJkt8amfDGTmUqBd8n45HNufZoDEEfD3H4fD3pfDa+WZmPEbo=\n=o7yn\n-----END PGP SIGNATURE-----", "payload": "tree a6f132a3eca2e60730cc26c84b6dc5e656997062\nparent cc0150a1b2b745980467003a73aa1be51c1e426a\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1758128520 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1758128520 -0700\n\nUpdate Plex OAuth polling\n\nChange Plex OAuth to polling with max retries instead of using popup detection due to `Cross-Origin-Opener-Policy: same-origin` set on the Plex login page.\n\nRef.: https://forums.plex.tv/t/plex-oauth-authenticate-with-plex-broken-after-plex-web-update-v4-152-0/931098/24\n", "verified_at": "2025-09-17T17:02:55Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/de3132382b1a1a91d209aacb59cf76bde61583f3", "html_url": "https://github.com/Tautulli/Tautulli/commit/de3132382b1a1a91d209aacb59cf76bde61583f3", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/de3132382b1a1a91d209aacb59cf76bde61583f3/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "cc0150a1b2b745980467003a73aa1be51c1e426a", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cc0150a1b2b745980467003a73aa1be51c1e426a", "html_url": "https://github.com/Tautulli/Tautulli/commit/cc0150a1b2b745980467003a73aa1be51c1e426a"}]}, {"sha": "6cccdc1022042c6c4c57d31326e8430d90770717", "node_id": "C_kwDOAducuNoAKDZjY2NkYzEwMjIwNDJjNmM0YzU3ZDMxMzI2ZTg0MzBkOTA3NzA3MTc", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2025-09-17T17:12:07Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-09-17T17:12:07Z"}, "message": "Bump joncloud/makensis-action from 4.1 to 5.0 (#2594)\n\nBumps [joncloud/makensis-action](https://github.com/joncloud/makensis-action) from 4.1 to 5.0.\n- [Release notes](https://github.com/joncloud/makensis-action/releases)\n- [Commits](https://github.com/joncloud/makensis-action/compare/v4.1...v5.0)\n\n---\nupdated-dependencies:\n- dependency-name: joncloud/makensis-action\n  dependency-version: '5.0'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "tree": {"sha": "d0d5011f31fa2ad491f18760adc6164b069c87ed", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/d0d5011f31fa2ad491f18760adc6164b069c87ed"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/6cccdc1022042c6c4c57d31326e8430d90770717", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJoyuvnCRC1aQ7uu5UhlAAA3/MQAER/LMrJs5cHsIp1zJLgKiFy\nWydCGx7TS8wG/ZrYspQWuNUH0AzZs32rULTeeL8d3WdVjU62AhRSVKXXfTfxM3/T\nbMfqJBdoNiFCNoZZO03wAId/UJyboypr6zK721zepafgnSsnU6dPCUX1XwvciBFw\niKP8lAC7lN9YsGVK1KakgvevXtuQJh4qTMqafoqk9HEG8MOj6M+5XjNwgtmUprSl\n1766xYaz7tBraSzJJxA/dui0+SFhN2Kcn4jWuNuKR3plGzTVLSsnvne+WFklJyvU\nwbOI8gpw2heai1k54ZNY9C6VAEK6TbwggOhDx1HoMiV/u6Td29fFdlLHzEBfVgwO\nyZSVk07cWaD8UC4uiybUWBxel54Df8Gq7Me2k41JrSGlGiNNikjUI0WMAYMdR4EZ\nHUDX3lq++PheD3uR/YacvEZwo/JfmFzNGSxWmz9V+KikILH7ylUxADTJMnOHrdKK\naamJu8z9rrlAsDxCIVm9cPv/EJTY/lVrsbJDiv70wMfO34BnFV7oS+XrcsTOTefM\nHg0YI8ybJrcCFOYrD2qpaLG/zywc/qUbLFJBUp81keouoNNW6k6yEOyFBxVxMTDu\nh4L8W2ILq3NCp2kVROgtVVCm0bNQkui3hTDNalPBXFQEwbRqxxPSuIHyxDz2JHs+\nsTVLu5dqm1XkA1NsWqOJ\n=Xysn\n-----END PGP SIGNATURE-----\n", "payload": "tree d0d5011f31fa2ad491f18760adc6164b069c87ed\nparent de3132382b1a1a91d209aacb59cf76bde61583f3\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1758129127 -0700\ncommitter GitHub <noreply@github.com> 1758129127 -0700\n\nBump joncloud/makensis-action from 4.1 to 5.0 (#2594)\n\nBumps [joncloud/makensis-action](https://github.com/joncloud/makensis-action) from 4.1 to 5.0.\n- [Release notes](https://github.com/joncloud/makensis-action/releases)\n- [Commits](https://github.com/joncloud/makensis-action/compare/v4.1...v5.0)\n\n---\nupdated-dependencies:\n- dependency-name: joncloud/makensis-action\n  dependency-version: '5.0'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "verified_at": "2025-09-17T17:12:08Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6cccdc1022042c6c4c57d31326e8430d90770717", "html_url": "https://github.com/Tautulli/Tautulli/commit/6cccdc1022042c6c4c57d31326e8430d90770717", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6cccdc1022042c6c4c57d31326e8430d90770717/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "de3132382b1a1a91d209aacb59cf76bde61583f3", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/de3132382b1a1a91d209aacb59cf76bde61583f3", "html_url": "https://github.com/Tautulli/Tautulli/commit/de3132382b1a1a91d209aacb59cf76bde61583f3"}]}, {"sha": "93e159a0f4137c17c98bd7a16131e6824166d955", "node_id": "C_kwDOAducuNoAKDkzZTE1OWEwZjQxMzdjMTdjOThiZDdhMTYxMzFlNjgyNDE2NmQ5NTU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-17T17:24:40Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-09-17T17:24:40Z"}, "message": "Add install nsis to Publish Installers workflow", "tree": {"sha": "d1bd316038521304648e1dba58676ff9b5654894", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/d1bd316038521304648e1dba58676ff9b5654894"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/93e159a0f4137c17c98bd7a16131e6824166d955", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEEFPKZYCM7sDX3KZzmsfH5gHGEaXoFAmjK7tkACgkQsfH5gHGE\naXrQERAAiirmij0SkRKcvLqlkSGupSCdCsRNkvzW2sQquGaWKCd7XCtln/JEumPo\nXx3Ma09WqJhZbn2cygl2Qrl6P4JVY8/81cSHL1U+tX/lLmUoNrmJZDIWpLVEKHw2\nLNIodYnGQgZAMMBu+IofI1/zL/iIakRNsvVjxrYc/e5cskgzBj8OoixUmcvWKdcY\nSswcRxDlj37DxSvdjIblUSqY4fb7txT8OJq/TYvFYF7llIai29yJe1+pyxZPfY3p\n2ABp92IX69/QT6lrQU11C+P/vqmnGjPu/P3P6AlnXgwb72I4GCWBfdiTgF/vfxUj\n+avSWZrGIybziMSGzPJ2uqK9CMQQa5pcGF4D3VY8BlhgxYXe4bZW2eRFQfV0GArL\n8HqOiz5FBYyPQr23KhX/AJeII8B1Zo7cezl90xuEGtVLxxxtPYsK2XkPFh0M/VVJ\nRP4WXENPkaKbdZeHf7E4VE0jUblR9+AVOXoQ3fBQ7xAnVHjit55xLP2w6ohqkcnP\nFHOK92UOdiHH9MzDGI+FD68f+W4j9ikYcYLF7Wm+W0PSz/zLhwSxPysPNUEsuGqM\npy/7Z04La8gFwY81Q2S/BesW2uSHCtbq+I5pDaoCCin+2NdtOeW2bgeKTx1IWuwp\n3lkfDE3O1RJWyPbB35WfX/mQNIsQB/30r8+p0LVidsTYByAIJ/I=\n=ag4m\n-----END PGP SIGNATURE-----", "payload": "tree d1bd316038521304648e1dba58676ff9b5654894\nparent 6cccdc1022042c6c4c57d31326e8430d90770717\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1758129880 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1758129880 -0700\n\nAdd install nsis to Publish Installers workflow\n", "verified_at": "2025-09-17T17:24:46Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/93e159a0f4137c17c98bd7a16131e6824166d955", "html_url": "https://github.com/Tautulli/Tautulli/commit/93e159a0f4137c17c98bd7a16131e6824166d955", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/93e159a0f4137c17c98bd7a16131e6824166d955/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "6cccdc1022042c6c4c57d31326e8430d90770717", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6cccdc1022042c6c4c57d31326e8430d90770717", "html_url": "https://github.com/Tautulli/Tautulli/commit/6cccdc1022042c6c4c57d31326e8430d90770717"}]}, {"sha": "8e265f81c27c6ef5e67c4cdcedd77e18eb11470d", "node_id": "C_kwDOAducuNoAKDhlMjY1ZjgxYzI3YzZlZjVlNjdjNGNkY2VkZDc3ZTE4ZWIxMTQ3MGQ", "commit": {"author": {"name": "Stephen Mc Gowan", "email": "mcclown@gmail.com", "date": "2025-10-12T23:43:06Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-10-12T23:43:06Z"}, "message": "Add Ace editor for syntax highlighting and code formatting for newsletter message text (#2585)\n\n* Initial HTML syntax highlighting support\n\n* Ace and html mode javascript\n\n* Fixed formatting\n\n* Changing theme to merbivore_soft\n\n* Created tautulli specific theme\n\n* Cleaning up gutter styling\n\n* Add clearer editor container area\n\n* Minor layout fixes\n\n* Missed css\n\n* Cleaning up JS and some CSS\n\n* Fixing up html formatting functionality\n\n* Removing unneeded code\n\n* Updating recently added templates to handle html messages properly\n\n* Upgrading to latest version of ace editor, removing unused themes\n\n* Actually removing unused themes\n\n* Apply CSS changes from @JonnyWong16\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Cleanup whitespace changes from @JonnyWong16\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Apply suggestion from @JonnyWong16\n\nCleanup CSS and hide editor controls\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Changing to using full packaged ace editor js files, intead of just cherrypicked files, as this was causing errors.\n\n* Removing old ace editor files\n\n* Filtering out HTML annotations\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* init editor when tab is shown\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Add id to tab, for editor init\n\n* Cleaning up required file for ace editor. Moving custom theme to ace folder\n\n* Removing snippets directory from ace editor, as we're not using snippets currently.\n\n* Updating id tag for newsletter text\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Typo on to one\r\n\r\nIf this was a test, I failed. Good catch!\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Remove unnecessary reinitialize Ace editor\n\n---------\n\nCo-authored-by: Stephen Mc Gowan <smcgowan@myemergencydr.com.au>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "tree": {"sha": "90a41b4885bf5cff8dc816a567367486a96ab67e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/90a41b4885bf5cff8dc816a567367486a96ab67e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/8e265f81c27c6ef5e67c4cdcedd77e18eb11470d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJo7D0KCRC1aQ7uu5UhlAAAHCMQAEmMQlk8Wy7OXNk7B6Qsdh0/\niWpqe7Exa6zpfuLzaEfNzA6ekvNkYXoSy6Bu+syoB3fd4xQ+AwJeKj9uVXPeG9c1\nT06I+OJTN/t0PF5KicoYwMPF06Pb+Y21oqhq78SAqKLMQm39hsgOfkPDHhkVn4MI\nzggWpoYI2LsOVYrSKrQK8rhm8lH4OUVJ3wtOEyg4Xu1EDZCRwXAZnmNwl92u8+aq\ng6TmHIBcNT7u52YsE3p2K6GW3jUtQWJo/V8HWpEfqELab5MisX71FZ7QAn6Rp1Re\nKbcMCKVf1AkY+YKRrkTCXAXHbDm8cowBfjeYnQePwke+rA5aQoBw4UFGaxsRtCJy\nlgHFSBxAPAII77vizwDTc10qbJKUqli+8J/LmPkhEQne6MSUyCQGCsoHDuJl/wzM\nNLVKXqNC8x7DoXaiLPBOf7cpiN2R8aoOuoMlFb0JHuYogWXQEMuX/jKo8RgwLvCj\nB1qrQ++6ntVSjOSzc1hdr2KygFT5hRgqlJXvGpDhr3ca00GXub59CLzR6KnYtltK\nW5YZx6jRtiuP9Qd+Xk9YVJtuMJG1Cu+f+mz+mM/0i79TwyFXLCzEAWgYk6iIA07p\n3mg6cOzw493smfuPLlj8v8nugsoqvQAec4GtCaNiNWjHGcEU4my26ZphFdznrhD7\n335xtUO8JYsYo4Sq8VzW\n=+LFu\n-----END PGP SIGNATURE-----\n", "payload": "tree 90a41b4885bf5cff8dc816a567367486a96ab67e\nparent 93e159a0f4137c17c98bd7a16131e6824166d955\nauthor Stephen Mc Gowan <mcclown@gmail.com> 1760312586 +1100\ncommitter GitHub <noreply@github.com> 1760312586 -0700\n\nAdd Ace editor for syntax highlighting and code formatting for newsletter message text (#2585)\n\n* Initial HTML syntax highlighting support\n\n* Ace and html mode javascript\n\n* Fixed formatting\n\n* Changing theme to merbivore_soft\n\n* Created tautulli specific theme\n\n* Cleaning up gutter styling\n\n* Add clearer editor container area\n\n* Minor layout fixes\n\n* Missed css\n\n* Cleaning up JS and some CSS\n\n* Fixing up html formatting functionality\n\n* Removing unneeded code\n\n* Updating recently added templates to handle html messages properly\n\n* Upgrading to latest version of ace editor, removing unused themes\n\n* Actually removing unused themes\n\n* Apply CSS changes from @JonnyWong16\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Cleanup whitespace changes from @JonnyWong16\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Apply suggestion from @JonnyWong16\n\nCleanup CSS and hide editor controls\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Changing to using full packaged ace editor js files, intead of just cherrypicked files, as this was causing errors.\n\n* Removing old ace editor files\n\n* Filtering out HTML annotations\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* init editor when tab is shown\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Add id to tab, for editor init\n\n* Cleaning up required file for ace editor. Moving custom theme to ace folder\n\n* Removing snippets directory from ace editor, as we're not using snippets currently.\n\n* Updating id tag for newsletter text\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Typo on to one\r\n\r\nIf this was a test, I failed. Good catch!\n\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n* Remove unnecessary reinitialize Ace editor\n\n---------\n\nCo-authored-by: Stephen Mc Gowan <smcgowan@myemergencydr.com.au>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "verified_at": "2025-10-12T23:43:06Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/8e265f81c27c6ef5e67c4cdcedd77e18eb11470d", "html_url": "https://github.com/Tautulli/Tautulli/commit/8e265f81c27c6ef5e67c4cdcedd77e18eb11470d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/8e265f81c27c6ef5e67c4cdcedd77e18eb11470d/comments", "author": {"login": "mcclown", "id": 1173840, "node_id": "MDQ6VXNlcjExNzM4NDA=", "avatar_url": "https://avatars.githubusercontent.com/u/1173840?v=4", "gravatar_id": "", "url": "https://api.github.com/users/mcclown", "html_url": "https://github.com/mcclown", "followers_url": "https://api.github.com/users/mcclown/followers", "following_url": "https://api.github.com/users/mcclown/following{/other_user}", "gists_url": "https://api.github.com/users/mcclown/gists{/gist_id}", "starred_url": "https://api.github.com/users/mcclown/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/mcclown/subscriptions", "organizations_url": "https://api.github.com/users/mcclown/orgs", "repos_url": "https://api.github.com/users/mcclown/repos", "events_url": "https://api.github.com/users/mcclown/events{/privacy}", "received_events_url": "https://api.github.com/users/mcclown/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "93e159a0f4137c17c98bd7a16131e6824166d955", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/93e159a0f4137c17c98bd7a16131e6824166d955", "html_url": "https://github.com/Tautulli/Tautulli/commit/93e159a0f4137c17c98bd7a16131e6824166d955"}]}, {"sha": "aea02a902240a90afd900bb065e8a672607b751d", "node_id": "C_kwDOAducuNoAKGFlYTAyYTkwMjI0MGE5MGFmZDkwMGJiMDY1ZThhNjcyNjA3Yjc1MWQ", "commit": {"author": {"name": "Sean Watson", "email": "seanwatson@users.noreply.github.com", "date": "2025-10-30T14:39:33Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2025-10-30T14:39:33Z"}, "message": "Add __init__.py file to jaraco to fix import errors (#2599)\n\njaraco modules are split across multiple repos using namespace packages.\nThat doesn't seem to work with the dynamic sys.path configuration used\nin Tautulli.py. By adding an __init__.py file here the python\ninterpreter will properly look for modules in the jaraco subdirectories.\n\nCo-authored-by: Sean Watson <1328841+seanwatson@users.noreply.github.com>", "tree": {"sha": "bdbbeb10a927cd8c27e13ddcb73a38106cb5b4f3", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/bdbbeb10a927cd8c27e13ddcb73a38106cb5b4f3"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/aea02a902240a90afd900bb065e8a672607b751d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpA3ilCRC1aQ7uu5UhlAAAiBwQAIwNnTnzaKt10hLSxVPhJEgM\nK+HpRxy5u7RWj38UIjZwhQAhXL+BL5rd273XEIWgrcvcZ8UAlnMrT6JfQ4Op1snC\nR/ZCWn8T2buWdJrjK2WjXpmV3paYDmjnVrjx+QfPWnBjhNTCQFDGhOMCN5Qx0YuM\n80+nYZNzBUscxoI5+lPI/aGkm2/OtuRoBLIbPfIu7sif2jTt4RvHgyFbOpSqn2AE\n9/0FJjwGj4FRE5HH/D9/jeyOSbtQzDQIxTLIbyFGwkrxIAcujO4xwGvIEBA/12Ct\nifwdyi3CzeixR2NCpQEcGst4waAaXe1vBhwIlKaaaXvsZ21LTHWnSlkjclTKOYDm\nZgB15HqvXJVMSou4j84naNB3sRtJaw3lSdMxeivSEMlfgb2aQyfDkl+LMHrQzR+R\nWHJ1hyaTjA/KE77pyaErHyiVGyt09CuevTq0yDZGVOPEYb6eHTvVbCNB8pI3epjb\n28utDhxkbyZLNgR9ioswiQ3xBPFx4I0/icGkU5eQBBdPHuDoCv9BBVheKgVLxd01\nP9SDVi/sUUhSA+AmCnl2NWcb97EgtNK0M/Z862zv1R7hwoGO1co4UL5iIJ0mNDf6\nbXy9wZnNBBSNMnb0wENHyOOXBlQuJcaIalWTNODufB4RmHB9JAPYhT1i4BzITk8z\n43dkSubpu8P7XyCHKq+A\n=Pd6Y\n-----END PGP SIGNATURE-----\n", "payload": "tree bdbbeb10a927cd8c27e13ddcb73a38106cb5b4f3\nparent 8e265f81c27c6ef5e67c4cdcedd77e18eb11470d\nauthor Sean Watson <seanwatson@users.noreply.github.com> 1761835173 -0400\ncommitter GitHub <noreply@github.com> 1761835173 -0700\n\nAdd __init__.py file to jaraco to fix import errors (#2599)\n\njaraco modules are split across multiple repos using namespace packages.\nThat doesn't seem to work with the dynamic sys.path configuration used\nin Tautulli.py. By adding an __init__.py file here the python\ninterpreter will properly look for modules in the jaraco subdirectories.\n\nCo-authored-by: Sean Watson <1328841+seanwatson@users.noreply.github.com>", "verified_at": "2025-10-30T14:39:34Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/aea02a902240a90afd900bb065e8a672607b751d", "html_url": "https://github.com/Tautulli/Tautulli/commit/aea02a902240a90afd900bb065e8a672607b751d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/aea02a902240a90afd900bb065e8a672607b751d/comments", "author": {"login": "seanwatson", "id": 1328841, "node_id": "MDQ6VXNlcjEzMjg4NDE=", "avatar_url": "https://avatars.githubusercontent.com/u/1328841?v=4", "gravatar_id": "", "url": "https://api.github.com/users/seanwatson", "html_url": "https://github.com/seanwatson", "followers_url": "https://api.github.com/users/seanwatson/followers", "following_url": "https://api.github.com/users/seanwatson/following{/other_user}", "gists_url": "https://api.github.com/users/seanwatson/gists{/gist_id}", "starred_url": "https://api.github.com/users/seanwatson/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/seanwatson/subscriptions", "organizations_url": "https://api.github.com/users/seanwatson/orgs", "repos_url": "https://api.github.com/users/seanwatson/repos", "events_url": "https://api.github.com/users/seanwatson/events{/privacy}", "received_events_url": "https://api.github.com/users/seanwatson/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "8e265f81c27c6ef5e67c4cdcedd77e18eb11470d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/8e265f81c27c6ef5e67c4cdcedd77e18eb11470d", "html_url": "https://github.com/Tautulli/Tautulli/commit/8e265f81c27c6ef5e67c4cdcedd77e18eb11470d"}]}, {"sha": "830559fb7262cdfe2467a255eb2d12dd8cb96078", "node_id": "C_kwDOAducuNoAKDgzMDU1OWZiNzI2MmNkZmUyNDY3YTI1NWViMmQxMmRkOGNiOTYwNzg", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-12-23T19:34:44Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-12-23T19:34:44Z"}, "message": "Add DD:HH:MM to home stats", "tree": {"sha": "bfd5a3e32c6a6c3f029c97ccd7664275f8d8cc52", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/bfd5a3e32c6a6c3f029c97ccd7664275f8d8cc52"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/830559fb7262cdfe2467a255eb2d12dd8cb96078", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEECL5lGPhhs984Mkn/92qLwoN3kBQFAmlK7tgACgkQ92qLwoN3\nkBRyyQ/+KM7brE89zHAUP0tpBnVQAvtbP0GUwu3u2F2pplk8d5JlS8XdyA7tOZl4\nQ5PdMEGqdWfyQBH2r7w5UpTO84tZ/jqxzLeCfpwjHkyYXTsdLR7XPbtmv0fynwpr\ni958C+7p373TVcZgWd+7WRAJ938HrLYent2fVb5/vMrIimEYza4ZtkeLixMIZUso\nriHKCtyCsmw5D8hAbkLrQdQPg/s+EvGD1BtWjUMuU/xY6O4isFmFgOJmmzXqK3Aj\nZ7qhlE1/lp93s41U5FLqTke/hEZkWv4su2utIJ4f84OTw/E9xDgS+KvzIyEwbxdy\ndlyn7RN+IGD7tI++BR5FdHUOxHFATAqwr76aeBdAnFX1cgJQ+QBnSpiucG66y2S9\nJfMJWly1c501ZIhfgdofCq6nPpudVgNWNDrkvmzasDsflmF5qiWSUfrJaDnG/Z68\nsk4aQ82yszAJxyY3Uv1mSX9Fj2IjZ1QsAJGIxHShM1oc9Kz4fIrSkhqFzoHk4bhh\nPpT20tEInELiML0GdE8JXHsZgmV6aDyqavZljSihII/XHG2gOjnptB4Uqn+4B4iE\nqmmDdzkJtleOUgv2SP0YcuJEuRRXl/aIbT0XEZnUhDnA9wWqjCdG7pMdAy6lNyki\ncHp7yuIjWa/E7TgWxCNIRGMoPhcscS9YxXjfSsefwm3tdKz3/GA=\n=xUkv\n-----END PGP SIGNATURE-----", "payload": "tree bfd5a3e32c6a6c3f029c97ccd7664275f8d8cc52\nparent aea02a902240a90afd900bb065e8a672607b751d\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1766518484 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1766518484 -0800\n\nAdd DD:HH:MM to home stats\n", "verified_at": "2025-12-23T23:12:44Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/830559fb7262cdfe2467a255eb2d12dd8cb96078", "html_url": "https://github.com/Tautulli/Tautulli/commit/830559fb7262cdfe2467a255eb2d12dd8cb96078", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/830559fb7262cdfe2467a255eb2d12dd8cb96078/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "aea02a902240a90afd900bb065e8a672607b751d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/aea02a902240a90afd900bb065e8a672607b751d", "html_url": "https://github.com/Tautulli/Tautulli/commit/aea02a902240a90afd900bb065e8a672607b751d"}]}, {"sha": "afe4c98767516e55908e81843c094199d3e077d2", "node_id": "C_kwDOAducuNoAKGFmZTRjOTg3Njc1MTZlNTU5MDhlODE4NDNjMDk0MTk5ZDNlMDc3ZDI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-12-23T20:06:33Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-12-23T20:06:33Z"}, "message": "zip backup files\n\nCloses #2614", "tree": {"sha": "a9a58aaf15d5a4c881b3028cab24fef75248a838", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/a9a58aaf15d5a4c881b3028cab24fef75248a838"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/afe4c98767516e55908e81843c094199d3e077d2", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEECL5lGPhhs984Mkn/92qLwoN3kBQFAmlK9koACgkQ92qLwoN3\nkBRMng/+Jfv68o1wD1JVc7BBkHrCdcmW4/kO+I2mF3NH1zrO5mPszjKiq3fDi/zu\nKsZRX4M6MjI/ZcmFCilpUeDBgO/lRxPCS84ZbdTHYdCmp2+v/5ih41oWRAq/EqZ+\nCgaPpWembuNCnNo6OPiAd31d25FLyLacePvlHnK1YPr77uaXygOJ3VLVKxghqiVe\nT6l2RlqTIdCDwWrwv/vFxoeidSwpC+IxGgHQx77Tyrud1PUhmXL1Awgwng3ijqew\nR2CsMW1n8wgIo6ClFJ29pkURkt3fJjE9LZdarmkfG9fQhukHB/QN6Qhkp7MYJKjC\nCwwhQ811LjmCl2A3//jO5kMEySJAEm4noU5tZ9hIZB2R9+MQ/mbzZwtRocYKG0tv\nYTdPDUWwM4oMTYFqvs13xjZpwH0ytSxN/C+lGp2g0U+EWzzXd653v7EkIbG/7DCx\n1qnk5yFEUzISdbMjI0/BBRl5xTIuvYdqTran9I01G6tu232yxCRaoMkppG7jW5z6\nlPWBq49c9Ts0ay0suO7l10sMTb3+kJWnKQ2TDEE/sKXA0loC0DVsQMMuamUL876n\nzXDZHlMfIRqPO1wwhnOCd65SCgHIBke+kUWsqLt6WWfntOveTWjJ11slMLSRG40j\nFq99TLbEgnkXK4878rGX2SYoURSTUkgRoV1bKxEje//8t7V5yyA=\n=fZOp\n-----END PGP SIGNATURE-----", "payload": "tree a9a58aaf15d5a4c881b3028cab24fef75248a838\nparent 830559fb7262cdfe2467a255eb2d12dd8cb96078\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1766520393 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1766520393 -0800\n\nzip backup files\n\nCloses #2614\n", "verified_at": "2025-12-23T23:12:44Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/afe4c98767516e55908e81843c094199d3e077d2", "html_url": "https://github.com/Tautulli/Tautulli/commit/afe4c98767516e55908e81843c094199d3e077d2", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/afe4c98767516e55908e81843c094199d3e077d2/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "830559fb7262cdfe2467a255eb2d12dd8cb96078", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/830559fb7262cdfe2467a255eb2d12dd8cb96078", "html_url": "https://github.com/Tautulli/Tautulli/commit/830559fb7262cdfe2467a255eb2d12dd8cb96078"}]}, {"sha": "2ca729612078759500672fa91a794d12c672c5e1", "node_id": "C_kwDOAducuNoAKDJjYTcyOTYxMjA3ODc1OTUwMDY3MmZhOTFhNzk0ZDEyYzY3MmM1ZTE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-12-23T22:17:40Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-12-23T22:17:40Z"}, "message": "Add Tautulli Plex token expired notification\n\nCloses #2514", "tree": {"sha": "462625e922bfc759c8566ee01e2b4a739a26149a", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/462625e922bfc759c8566ee01e2b4a739a26149a"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/2ca729612078759500672fa91a794d12c672c5e1", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEECL5lGPhhs984Mkn/92qLwoN3kBQFAmlLFQUACgkQ92qLwoN3\nkBQN+A//UFNSlMoVceW+M27pS9D6URyGqaAxi/GZksPG/Q4dlLXlOngs46ox1U9S\nZpVdFqughnfk8kNlSvIm061OhhRcAp60mZRwUvbpsZsm58awuSQWmi+4+K+qeJpO\ngaYvNkhTIYH8KDjX1/6j1WeoWJfhQTVGl2Si/6ti9YVsNsBEdyUaTOfNpfNDVtAp\n0eY7fMqVM2q7HxRvWCbtPJxZv6mFtdwKeLptsl+fq9eHhB5D47dUI54bNkBG+3VQ\nx9zAUEP4CQWOCjKjbcWsg1wj10iq7H6ZL4dWxPdXHDibLENu7J09LAo0vbBVrWN9\nPisGKbi3xsjTpmD/1EPddlV6TdLBtfs/q6X+0+UXdBNvV07PMbY27zQRODuKstvl\n0C/5gLar8rWm8gO+91RhI5aWGdsMIMdy0VfIJmJHRn1GvGKeNKVqyLDuybVv396g\nsUIeyoBBGuCFGAor6qwMKMgbSgpM0FNkeO/J9R0minhhVvP2ts0Yc//lS6KtxawL\nhJMebyQ+LKVMbK30eDRWrH5g2x9N/WDSa1koLTDDceqx7LKhGyweo6aJfJ5R9f1l\nFJQC4arAFQGluh5L7MaUTu6+ofo+POyu8FtvMFK+cYZZMXdipl62aNgSh/mopwPy\nQHUTrSN6uuHPjB+8YyXVmXEcfD2ZXaNNK2d94NGdZt+77kZmEHo=\n=gsQG\n-----END PGP SIGNATURE-----", "payload": "tree 462625e922bfc759c8566ee01e2b4a739a26149a\nparent afe4c98767516e55908e81843c094199d3e077d2\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1766528260 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1766528260 -0800\n\nAdd Tautulli Plex token expired notification\n\nCloses #2514\n", "verified_at": "2025-12-23T23:12:44Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2ca729612078759500672fa91a794d12c672c5e1", "html_url": "https://github.com/Tautulli/Tautulli/commit/2ca729612078759500672fa91a794d12c672c5e1", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2ca729612078759500672fa91a794d12c672c5e1/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "afe4c98767516e55908e81843c094199d3e077d2", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/afe4c98767516e55908e81843c094199d3e077d2", "html_url": "https://github.com/Tautulli/Tautulli/commit/afe4c98767516e55908e81843c094199d3e077d2"}]}, {"sha": "b5a9fb631840762e6139087835f16ffb16d33cf7", "node_id": "C_kwDOAducuNoAKGI1YTlmYjYzMTg0MDc2MmU2MTM5MDg3ODM1ZjE2ZmZiMTZkMzNjZjc", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-12-23T23:09:15Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2025-12-23T23:09:15Z"}, "message": "Add HH:MM:SS to activity cards\n\nCloses #2592\nCloses #2603", "tree": {"sha": "f49d475666dda4cfe4dd3acab5be2555bea63b89", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/f49d475666dda4cfe4dd3acab5be2555bea63b89"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/b5a9fb631840762e6139087835f16ffb16d33cf7", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEECL5lGPhhs984Mkn/92qLwoN3kBQFAmlLISEACgkQ92qLwoN3\nkBR/QxAApDW+P/pk5EW8scEfgTDEzTMXAIPjvh6mqujDAVaXjHhtXXksh+i9Cili\nymgRg+zgnerecpGh4O1YRDThICJb5SxYKJqhnT1MZE3g8gEYgLzrKCpTk24e3pdw\nVOrlCySBuU3grmi+y1p5ssZDMXq+7YRz6Zf6UTuU4+9k2/3HOpRBXixKbuO+gKmK\nPTRYJsZNYPnYmxz1VS7UgYyoBmvzkgfBBD1HvZ29NFJfNIGcdb8FM1sPUl0zqqi+\npi80oc/9D3Q2/COb5pDQDYeFRJe6T4oTvEHdnNAwjABRgYt/D5Is2CkaD30e2LbM\nwdoxMkOE3nrqRTzyTYhuNWfk22fpro6aQ+SjIan7436vwBTeBoNTw3xyrgxwa8RY\nSGZJbX497TQibvqHLUYYwCeVEatLNZsnj6YNjfOaAXuCD9CBTwQe7vqs9cPmhDvR\nUoPDuDoBMwEJTrBnBbdDniu70v3UzWISaSOazX4QuYmprCYNkPEtQf3CN2yUDbRz\nWHtdMawhnV1q/FJitN3vC5GwpCwDG3/rJHU3GtjrgLuTe1uIOClomhv4pGOkSGEI\nGfHv4ugyRIcxCPQfejtj/hzFW/VPOf0oGswQ1WABYjg2F46z19f1qdDUO/eHBKsF\n8azghz0A89iHAuCFoUcvYp23k1aTI10qVWLUyJeTbOS0seuaI0Q=\n=RLhw\n-----END PGP SIGNATURE-----", "payload": "tree f49d475666dda4cfe4dd3acab5be2555bea63b89\nparent 2ca729612078759500672fa91a794d12c672c5e1\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1766531355 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1766531355 -0800\n\nAdd HH:MM:SS to activity cards\n\nCloses #2592\nCloses #2603\n", "verified_at": "2025-12-23T23:12:43Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b5a9fb631840762e6139087835f16ffb16d33cf7", "html_url": "https://github.com/Tautulli/Tautulli/commit/b5a9fb631840762e6139087835f16ffb16d33cf7", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b5a9fb631840762e6139087835f16ffb16d33cf7/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "2ca729612078759500672fa91a794d12c672c5e1", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2ca729612078759500672fa91a794d12c672c5e1", "html_url": "https://github.com/Tautulli/Tautulli/commit/2ca729612078759500672fa91a794d12c672c5e1"}]}, {"sha": "d7293a2253367bd00fe0905474a3dde6dde9897f", "node_id": "C_kwDOAducuNoAKGQ3MjkzYTIyNTMzNjdiZDAwZmUwOTA1NDc0YTNkZGU2ZGRlOTg5N2Y", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-01-11T01:54:35Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-01-11T01:54:35Z"}, "message": "Remove timezone from IP address modal", "tree": {"sha": "0a024c1cd837597ab83b9a205b3d034f372f6121", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/0a024c1cd837597ab83b9a205b3d034f372f6121"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/d7293a2253367bd00fe0905474a3dde6dde9897f", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmljAwUACgkQm/TvTexB\nFjpVIxAArYhE+ricTkDt4pFN3nEVDHuZZMhFiq1nBj0xfrN13MtvDMb2yOqF1+xZ\nS4ArKpe5Ga/Pi7TKkFVFpexV1YItN2+1ZZm/XoYtQXGCPmgkr5GKa8bFNqW5HMk/\n+LQGXTg8CgjjdwFteRMMzsaGfMVZDI+b9Qxlzdv7ZfCfjrTvEe1JWfVXjakrgAAZ\nR+q54dmOctIL/BPrnlqxoJUm6a5U2JbGVnwhx69Oeb634oFypvWwQjs6H+ER9cMF\nDPBbyV2FLwq/8m4o/57BeR88oHMEi+iFKRB2sII9FpbUZEY5JP5NkRKjFk53xNxu\n/Tng/GVFYBnPlv3v6Y19qcKqKP0TxRcEJoww2/phMyCFNYm4tAkt6ZCbzfFyrqSD\nPwCE02XIwcsyCWMzNi061LOrZ7TyIJFeYiiSu7Lz0g7Bb/SUgSvRHJ1G5GmksnMb\niT/upHQ6T7vmCetDwXg8CxRRdISDoy/DwaHNBiOeMOmPAGDxGmZDoPmYnnfozP58\n1ysrM7JZb2Fdi1gOnUMskVVLrZGg42+wQSl2jDIOznbLfKC5sM0bFQ6SekLjM6XJ\n1vdmQGcXREkUOsuQr9VmR+uJrmVy5G0eosQwfQjzkcSQEtwgN+Us5QhWXzCdS9FE\nqkaEbqqc1dfiNLA9rZfMXwviZMuYA0kSUPHVLE7aq7OiwnVdvXk=\n=WHiv\n-----END PGP SIGNATURE-----", "payload": "tree 0a024c1cd837597ab83b9a205b3d034f372f6121\nparent b5a9fb631840762e6139087835f16ffb16d33cf7\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1768096475 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1768096475 -0800\n\nRemove timezone from IP address modal\n", "verified_at": "2026-01-17T18:34:30Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d7293a2253367bd00fe0905474a3dde6dde9897f", "html_url": "https://github.com/Tautulli/Tautulli/commit/d7293a2253367bd00fe0905474a3dde6dde9897f", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d7293a2253367bd00fe0905474a3dde6dde9897f/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "b5a9fb631840762e6139087835f16ffb16d33cf7", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b5a9fb631840762e6139087835f16ffb16d33cf7", "html_url": "https://github.com/Tautulli/Tautulli/commit/b5a9fb631840762e6139087835f16ffb16d33cf7"}]}, {"sha": "584fc5b5200fd40feec3495beaa9a65ddfaa7815", "node_id": "C_kwDOAducuNoAKDU4NGZjNWI1MjAwZmQ0MGZlZWMzNDk1YmVhYTlhNjVkZGZhYTc4MTU", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-02-15T20:12:07Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-02-15T20:12:07Z"}, "message": "Bump actions/setup-python from 5 to 6 (#2590)\n\nBumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.\n- [Release notes](https://github.com/actions/setup-python/releases)\n- [Commits](https://github.com/actions/setup-python/compare/v5...v6)\n\n---\nupdated-dependencies:\n- dependency-name: actions/setup-python\n  dependency-version: '6'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "ee9baef2eaf2a4fbded5cb0ef38ce692c1991250", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/ee9baef2eaf2a4fbded5cb0ef38ce692c1991250"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/584fc5b5200fd40feec3495beaa9a65ddfaa7815", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpkiiXCRC1aQ7uu5UhlAAA+vkQAGKjQPI6Inz2R0XSPTIOPIYR\nVFtx/+a9ua34iz7PGe3mW+sO+sgfSvrw1NBgNAMzWq0sGvGAknzh82wSuuvKudEa\nxh1vjx5+k63ts2nqlIPA7u6g5W5yu6VIOpCizrSVCQqkq2I0+k3y7EcDKGVx+wiK\nQlxSB3Gyx8DhAxnZ2xbJXEmj6CG6o4JLEmPVw2zOITwN7bKgUrIAoqFQNWX3XlLP\nT7xhrq2rRDmjt+IJd5Tw9zEfCVZKOTWBLMH0SCMvorXBu+E97hjiCCfhwEh9GwAO\nSk0ZSfb/xtXTdAEUeBD70MMzCYAX19K9KyKhdpQ2A2LTFfWVhzx15RdqiI7IBKuF\nz+hEK1UwVXj04rtmKG+LWy9l3fI42WRG355tdnEzblM1qxhtDf7JcadESQEj6nSe\nugpDCihrzibzOyo3/ArwgEOoIAl/iT54d4Bqv06UAd9xU7ogeTr0HK9RIlDSbPgF\nd/iUWP2daVPh6FpplaYgADVfKGfFKoOdCD0bm8JdLl5O72DOT/xgI16xD4WKCP3Z\nF6Ok7DFNKTkVv+fV5bx/grQEYMwAm0qdA7+DadPTJahp3mfKRzNGLZICkxSQFinG\nxBQyGKl7iNT4jscyAkTNrHQwRH5yQ+YaOo04ZG5x2FIJ/WrPYsntcWBq2javS9lU\nsmLP4nTe2ogfgjd6mLW/\n=5jLi\n-----END PGP SIGNATURE-----\n", "payload": "tree ee9baef2eaf2a4fbded5cb0ef38ce692c1991250\nparent d7293a2253367bd00fe0905474a3dde6dde9897f\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1771186327 -0800\ncommitter GitHub <noreply@github.com> 1771186327 -0800\n\nBump actions/setup-python from 5 to 6 (#2590)\n\nBumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.\n- [Release notes](https://github.com/actions/setup-python/releases)\n- [Commits](https://github.com/actions/setup-python/compare/v5...v6)\n\n---\nupdated-dependencies:\n- dependency-name: actions/setup-python\n  dependency-version: '6'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-02-15T20:12:08Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/584fc5b5200fd40feec3495beaa9a65ddfaa7815", "html_url": "https://github.com/Tautulli/Tautulli/commit/584fc5b5200fd40feec3495beaa9a65ddfaa7815", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/584fc5b5200fd40feec3495beaa9a65ddfaa7815/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "d7293a2253367bd00fe0905474a3dde6dde9897f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d7293a2253367bd00fe0905474a3dde6dde9897f", "html_url": "https://github.com/Tautulli/Tautulli/commit/d7293a2253367bd00fe0905474a3dde6dde9897f"}]}, {"sha": "c23aa63b570316454f5b1dc3d1638b6256208437", "node_id": "C_kwDOAducuNoAKGMyM2FhNjNiNTcwMzE2NDU0ZjViMWRjM2QxNjM4YjYyNTYyMDg0Mzc", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-02-15T20:12:21Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-02-15T20:12:21Z"}, "message": "Bump actions/stale from 9 to 10 (#2591)\n\nBumps [actions/stale](https://github.com/actions/stale) from 9 to 10.\n- [Release notes](https://github.com/actions/stale/releases)\n- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/actions/stale/compare/v9...v10)\n\n---\nupdated-dependencies:\n- dependency-name: actions/stale\n  dependency-version: '10'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "5fece6f101524b74ce2532e633ab42dddbccfd2e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/5fece6f101524b74ce2532e633ab42dddbccfd2e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/c23aa63b570316454f5b1dc3d1638b6256208437", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpkiilCRC1aQ7uu5UhlAAAb50QABNVYEaU+bMDxmyLohZr78Ar\nr49p+aMPDsQioXUrEsEkbz2LUC6b+s+sdkGbewmAf4HJhcueafJn7J5adBTHgXVU\nlm9UsfwD/y3MPBBYX4URZtilqQzsL9l8Z5w0rG8nM/zTrzleTXQvvSI5PnE1L5lc\nRTqhIH7/U74+EaayLO3ylzQUDBVR7xTz+65m9U6ZXOa6L3Do7oCgC6bgQZdfIZF5\n/wglZNZ/mSAanU6y75CDmZVudUUS1R5rtth+7tdca6z0LCOE9AuKmUMuAPtmKPEs\nEU3OMqnzz0C1K+TkinRjoDGm0oY65iv8GD0KWT31SQcFIndH9DxfRnxyJ+jkG6Nk\nIeW4hbjwlm4MIiuY2L6cOfUFcwgCcDXj3PrgKND4PieCKTAeDaF9yXqSzhfvkMyz\nslVYr/LV5VY9pdLs/jm9F4iAqN9phCUJo3AZdG1NGfe2sR+KdUGrRzte4pKWXgaC\nGzTdjq5ArXRs3Lr3dr2GNZ4F+46nMu1agSLn4qrE73QtF0zFrL2W6vgA15Lu9MeG\nKKoG39IfpT1s0whDxTjNxvwIRH74Z3iVxr9ghW5qwixyrqLEBJTUeuFTFcW48GQZ\nqHSAWZn6XV6XlaILlOauil1Kx4rYuBSxLwNCFpneDmtkDNFPIyy5f6wTqfLfkgkL\npsEjmNMyyouB/yfR4ooi\n=bxHZ\n-----END PGP SIGNATURE-----\n", "payload": "tree 5fece6f101524b74ce2532e633ab42dddbccfd2e\nparent 584fc5b5200fd40feec3495beaa9a65ddfaa7815\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1771186341 -0800\ncommitter GitHub <noreply@github.com> 1771186341 -0800\n\nBump actions/stale from 9 to 10 (#2591)\n\nBumps [actions/stale](https://github.com/actions/stale) from 9 to 10.\n- [Release notes](https://github.com/actions/stale/releases)\n- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/actions/stale/compare/v9...v10)\n\n---\nupdated-dependencies:\n- dependency-name: actions/stale\n  dependency-version: '10'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-02-15T20:12:21Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c23aa63b570316454f5b1dc3d1638b6256208437", "html_url": "https://github.com/Tautulli/Tautulli/commit/c23aa63b570316454f5b1dc3d1638b6256208437", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c23aa63b570316454f5b1dc3d1638b6256208437/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "584fc5b5200fd40feec3495beaa9a65ddfaa7815", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/584fc5b5200fd40feec3495beaa9a65ddfaa7815", "html_url": "https://github.com/Tautulli/Tautulli/commit/584fc5b5200fd40feec3495beaa9a65ddfaa7815"}]}, {"sha": "892e6d70700e06fe96ae3d1e73e30561f36be34d", "node_id": "C_kwDOAducuNoAKDg5MmU2ZDcwNzAwZTA2ZmU5NmFlM2QxZTczZTMwNTYxZjM2YmUzNGQ", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-02-15T20:13:23Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-02-15T20:13:23Z"}, "message": "Bump github/codeql-action from 3 to 4 (#2604)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/github/codeql-action/compare/v3...v4)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: '4'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "28324f63607f7ea37c24097fb00c0e5bbef9ace8", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/28324f63607f7ea37c24097fb00c0e5bbef9ace8"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/892e6d70700e06fe96ae3d1e73e30561f36be34d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpkijjCRC1aQ7uu5UhlAAA1XUQAFjE7pmInYgt1gmSuRpLFJOc\nCnhPigRwqrhP00EujKHC+zf0DS2Gsi2SWWcqYpv/usHspIqP9/SStgKS+bW+J2cj\nKyBSNJxb6cHCYiRKxUuac8UorcI/cyiywlqCTWKZahZafKU/b4oLvM7XhfpzE1xW\nscWrYzS+AZzXRsVu01BS9cuQN/OcbO3bINdXNGCgcTw0Q2OvUPGNtBTBGFdTVnAZ\nvtyYVxabMo1/mwZzvEIHyoPfQG9iJeXU+QiX4uoqoaOHJPX7LZWnRUlfh1APXwKd\nadCdGvchyayZWh47VZWGYRcAWoQCVc9SsBG+iiFpeFQ0D9m9KvY16xNbaYngvmk5\nlmsJ6yzm9pz5e1jEcKqD3lS8yhbdBkdMGI2vcMXGl06bT8KV5ZrpB/8TjgDTOvff\nRdW1WdiELd1dZ6hXiaUDRpYvCb4R5yEWakXsYe1YlxBEPTgggMpbY1fva2TZtnwP\nOO60UTbbyaBUICLwSvsoCS6y9sPTh0syR22hPfcbSOPSOKXknhVgyaI6JUw/30vA\n+yaGMFIJt2E0kf1TISTfxgc13ZUq6neZsEv6rIm1fpwqQoeLdSiEcF3a6nHP/IX6\nA3vnvyAKRZ13zF9bOhLXzV71dQ8DtHTVx1wzIh/f6UIujeu4+qAYXyyJadJ6aWNg\nL4CYlshKGMn0pnJ+Wdfg\n=1SWy\n-----END PGP SIGNATURE-----\n", "payload": "tree 28324f63607f7ea37c24097fb00c0e5bbef9ace8\nparent c23aa63b570316454f5b1dc3d1638b6256208437\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1771186403 -0800\ncommitter GitHub <noreply@github.com> 1771186403 -0800\n\nBump github/codeql-action from 3 to 4 (#2604)\n\nBumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.\n- [Release notes](https://github.com/github/codeql-action/releases)\n- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/github/codeql-action/compare/v3...v4)\n\n---\nupdated-dependencies:\n- dependency-name: github/codeql-action\n  dependency-version: '4'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-02-15T20:13:24Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/892e6d70700e06fe96ae3d1e73e30561f36be34d", "html_url": "https://github.com/Tautulli/Tautulli/commit/892e6d70700e06fe96ae3d1e73e30561f36be34d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/892e6d70700e06fe96ae3d1e73e30561f36be34d/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "c23aa63b570316454f5b1dc3d1638b6256208437", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c23aa63b570316454f5b1dc3d1638b6256208437", "html_url": "https://github.com/Tautulli/Tautulli/commit/c23aa63b570316454f5b1dc3d1638b6256208437"}]}, {"sha": "0a9d9ec766ad514c352123dd34077dab94e40ea0", "node_id": "C_kwDOAducuNoAKDBhOWQ5ZWM3NjZhZDUxNGMzNTIxMjNkZDM0MDc3ZGFiOTRlNDBlYTA", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-02-15T20:13:35Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-02-15T20:13:35Z"}, "message": "Bump actions/download-artifact from 4 to 6 (#2610)\n\nBumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 6.\n- [Release notes](https://github.com/actions/download-artifact/releases)\n- [Commits](https://github.com/actions/download-artifact/compare/v4...v6)\n\n---\nupdated-dependencies:\n- dependency-name: actions/download-artifact\n  dependency-version: '6'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "c2a2f48dd515f06a032cd365fb1e644a74111ec4", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/c2a2f48dd515f06a032cd365fb1e644a74111ec4"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/0a9d9ec766ad514c352123dd34077dab94e40ea0", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpkijvCRC1aQ7uu5UhlAAA45gQALD8itafF2ytEHaG5cXA2iw7\nIbocB6bteVCFBI/J7ExUEWe6ccPO3Zf20wtAo632e5fjm4E9GBMdhl42SfpFJTEK\naVv4gsg8SsjO4RdHg6paLwqudzVPrPD+4s3t+pTyHvODnY5Fm7HvCQE/GzZs/K3k\nl37l22gmsN/sBCXlmR/01WZjb2uFktW6Fyb7qJ1U6hAk5ZNxaQGp9MWwb3004YCq\nNPe3HttuviZPKx1x8WdLri0LbSD3bRmmJO/OOEdOmowhhQ7yl369ZyrIYRwpGGWe\nB8IsgGdmR2YVpxmm3ncVRlQ2nP/K8kukGlfTfnXbQYviUeuOIsCoxeP+LMX6baMW\nUABwQyIBJi3nUOeu3nkt+mWLkrbWVikd300XHogkRNwMy/6K9evTaC+skg6cVFNA\nHTvlo6kYGbTSrN2lxps3r6Y7qi9Dtqq0HcIgDgoDKT1cLtNfPcg89cWWkJ5A7OTE\nEhXLsamikfvnvzK16i9rTy+LSqVq+UDC0PJRULxa5E2J36zhDmLcM5OPNrjCbqMx\nHPtS5zrsdoSbuXN6LH1BEHPO4evBbqPtfBdLRNoHuxZEBQNS3zZBriYDAZVYSBqL\nGM09KNZ2WQ4NnNSbHacVukj2fso7us45LGZJ/Ln+NC3UyFGQpGVeK+qIiYwalr1s\nQBkvQhUv+EZVuYbQ9Yp9\n=XHNz\n-----END PGP SIGNATURE-----\n", "payload": "tree c2a2f48dd515f06a032cd365fb1e644a74111ec4\nparent 892e6d70700e06fe96ae3d1e73e30561f36be34d\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1771186415 -0800\ncommitter GitHub <noreply@github.com> 1771186415 -0800\n\nBump actions/download-artifact from 4 to 6 (#2610)\n\nBumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 6.\n- [Release notes](https://github.com/actions/download-artifact/releases)\n- [Commits](https://github.com/actions/download-artifact/compare/v4...v6)\n\n---\nupdated-dependencies:\n- dependency-name: actions/download-artifact\n  dependency-version: '6'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-02-15T20:13:36Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0a9d9ec766ad514c352123dd34077dab94e40ea0", "html_url": "https://github.com/Tautulli/Tautulli/commit/0a9d9ec766ad514c352123dd34077dab94e40ea0", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0a9d9ec766ad514c352123dd34077dab94e40ea0/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "892e6d70700e06fe96ae3d1e73e30561f36be34d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/892e6d70700e06fe96ae3d1e73e30561f36be34d", "html_url": "https://github.com/Tautulli/Tautulli/commit/892e6d70700e06fe96ae3d1e73e30561f36be34d"}]}, {"sha": "f278b1d8051f84c4ac6c3d4e0cde55d733ec6fb1", "node_id": "C_kwDOAducuNoAKGYyNzhiMWQ4MDUxZjg0YzRhYzZjM2Q0ZTBjZGU1NWQ3MzNlYzZmYjE", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-02-15T20:13:47Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-02-15T20:13:47Z"}, "message": "Bump actions/upload-artifact from 4 to 5 (#2611)\n\nBumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.\n- [Release notes](https://github.com/actions/upload-artifact/releases)\n- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)\n\n---\nupdated-dependencies:\n- dependency-name: actions/upload-artifact\n  dependency-version: '5'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "245f2708b0a972f4f992d17c522acdf68365204e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/245f2708b0a972f4f992d17c522acdf68365204e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/f278b1d8051f84c4ac6c3d4e0cde55d733ec6fb1", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpkij7CRC1aQ7uu5UhlAAAgAwQADFYod7iqCPRi3fVckQqlufE\nH9x55wwaLpCcOkDJzsGc6QdKdAOeN6ozTBioI8PLDYrRFKhZ7hg3CeoU/x3OjXbW\nnT2Vivmv77x70f9NMTcSGJlfmKuh1lM/IQsEWO7JsRJn5CCAfJgJSJ3LEV4YhAFF\nJ5mfRh4yCbTEDdkFr0dlNgipldUpUB9UZ5uNcyEygXlnjukVOY+Wvm87M/1yvn1j\nG5caxme2wtDV/jWGejj3DC7vAmdQDgYkEMaK6Cm5DAjH6rEyCgvryuOIRSNY2Dpd\nXwkBCh1sJAvRD6//QhDjJbrIkNMJPLbWasCGmqckfsaBmfXuvjR749yYO6fkPhZe\nwS6WNwwdTKgIrknJ3eYVHgRI396jyd4xU3s6DX2WoZ9i/6g7yQk1HoIjVmHKTZX3\nhKB2t/T181oVUE0H5oZjtC3oYJe3gt2QHZZ/CqNDYCpFFSOuEN6T6dz1rgIk6WIV\nCDN+z7KY/RvhY1sCsWIEJtk5lG52ZG14B1e9c0DX0ZGZPTUSBo1CYPX/JNyvW/Z0\n25u78Wd+pxN2v2DA30B4XGgkDwwz627RgtrSzF0jIQ7QJnrfNdcCQccMym9tcON1\nJo8ORtUCQTAjIAP0P5O+7BaKzhdHELOtmwcoPas55HxFiUR+WfVoh4cRizDzOr68\nxaq7Jn8sjqhg1/2g39t0\n=qPcC\n-----END PGP SIGNATURE-----\n", "payload": "tree 245f2708b0a972f4f992d17c522acdf68365204e\nparent 0a9d9ec766ad514c352123dd34077dab94e40ea0\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1771186427 -0800\ncommitter GitHub <noreply@github.com> 1771186427 -0800\n\nBump actions/upload-artifact from 4 to 5 (#2611)\n\nBumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.\n- [Release notes](https://github.com/actions/upload-artifact/releases)\n- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)\n\n---\nupdated-dependencies:\n- dependency-name: actions/upload-artifact\n  dependency-version: '5'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-02-15T20:13:47Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f278b1d8051f84c4ac6c3d4e0cde55d733ec6fb1", "html_url": "https://github.com/Tautulli/Tautulli/commit/f278b1d8051f84c4ac6c3d4e0cde55d733ec6fb1", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f278b1d8051f84c4ac6c3d4e0cde55d733ec6fb1/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "0a9d9ec766ad514c352123dd34077dab94e40ea0", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0a9d9ec766ad514c352123dd34077dab94e40ea0", "html_url": "https://github.com/Tautulli/Tautulli/commit/0a9d9ec766ad514c352123dd34077dab94e40ea0"}]}, {"sha": "7e5008fd5ebdd4f63387eefd8b55b7f525719391", "node_id": "C_kwDOAducuNoAKDdlNTAwOGZkNWViZGQ0ZjYzMzg3ZWVmZDhiNTViN2Y1MjU3MTkzOTE", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-02-15T20:13:57Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-02-15T20:13:57Z"}, "message": "Bump actions/checkout from 4 to 6 (#2615)\n\nBumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.\n- [Release notes](https://github.com/actions/checkout/releases)\n- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/actions/checkout/compare/v4...v6)\n\n---\nupdated-dependencies:\n- dependency-name: actions/checkout\n  dependency-version: '6'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "aab96e6c72dee96a90bfb2b8244ceb8f69ac4813", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/aab96e6c72dee96a90bfb2b8244ceb8f69ac4813"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/7e5008fd5ebdd4f63387eefd8b55b7f525719391", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpkikFCRC1aQ7uu5UhlAAAUhAQAFqoDo9zbzCAdBJqunQCr4L5\nzmtE3xrESU1XyYfTxCOIgmwVNrfZf9TMvnGrMagF3Bj/3itZj7jzR41RgcrdHVWt\nsAfrHfdESmURDgFgzgvDEWz/S3YkpEAUx2D94nyUtVEQVFcIDdIkKpFUaGVUkjsI\nmkHUSYin+vWtYck1y9WGiWEuDTb+QAceiyi/ZomMT0hm4ISQTOhf/tCUxk07qyaH\nDnvWbEMS7D94oICCdpAtjAIMrYhYrHS4xvb9FiGjbeHeRy/df22klk+ybvemJ6Xe\n08jioKtDji/hBygllk1dmqvnEFIJxbRBD1X8F5HdAAdIeXS9RsI5bX+ISUR47wKK\nslmHstPjPPgjrzXGs6CnBY0/2+UmEdu5dey7i9Rgrzjyp+ou7Te9Dv67ZTJ8FnCw\nVqsBz/+7CrjgKCz7PJfLLYeZwiWBc1gTzfJQiaSGFM00doN7I7CuIAKl9HS0O6h1\nrVVdUYB5flKUyHG8hh+YgDKOxxRQCkAZTq0qTlvZXPqdp6XlwS+VwCq6cLF1E7UN\nLxnF1fWNruJQD0HReZYcUvhsHX6kTslGPBv8oRmz9qwGP/8VUT8qfQcuzMT1LGw6\nrnXLToeLQAIr0x8VGPIf5qucqsaDwQx5VcOmpVRqsa0q4fZW97neFD9ESjwWkE5L\nUgzLE6T0kfI0xVVVYqr9\n=vYtA\n-----END PGP SIGNATURE-----\n", "payload": "tree aab96e6c72dee96a90bfb2b8244ceb8f69ac4813\nparent f278b1d8051f84c4ac6c3d4e0cde55d733ec6fb1\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1771186437 -0800\ncommitter GitHub <noreply@github.com> 1771186437 -0800\n\nBump actions/checkout from 4 to 6 (#2615)\n\nBumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.\n- [Release notes](https://github.com/actions/checkout/releases)\n- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/actions/checkout/compare/v4...v6)\n\n---\nupdated-dependencies:\n- dependency-name: actions/checkout\n  dependency-version: '6'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-02-15T20:13:58Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7e5008fd5ebdd4f63387eefd8b55b7f525719391", "html_url": "https://github.com/Tautulli/Tautulli/commit/7e5008fd5ebdd4f63387eefd8b55b7f525719391", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7e5008fd5ebdd4f63387eefd8b55b7f525719391/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "f278b1d8051f84c4ac6c3d4e0cde55d733ec6fb1", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f278b1d8051f84c4ac6c3d4e0cde55d733ec6fb1", "html_url": "https://github.com/Tautulli/Tautulli/commit/f278b1d8051f84c4ac6c3d4e0cde55d733ec6fb1"}]}, {"sha": "d57ea6c4609ec4aeac762eada135f61e77dd142d", "node_id": "C_kwDOAducuNoAKGQ1N2VhNmM0NjA5ZWM0YWVhYzc2MmVhZGExMzVmNjFlNzdkZDE0MmQ", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-02-13T18:17:59Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-02-15T20:19:19Z"}, "message": "Restrict graphs to guest user", "tree": {"sha": "1752ac02cf1cf747b001c594494441de1cc84c5b", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/1752ac02cf1cf747b001c594494441de1cc84c5b"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/d57ea6c4609ec4aeac762eada135f61e77dd142d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmSKkgACgkQm/TvTexB\nFjrJDA/+P8WgZxEbxta7Va/r9WiyKQUK8dFo55t+2TxZQuqyp+UFGzOZ4V42VzqN\n4rasIRxIFQ3w5HRSOMWfl02c3CxQ0i0Du8G5Oz0glCzWfMJhjqpONXMB976iTGfv\nLGJSoxMWZ6pbEeWKpNZ6+XrXUGt19TxLy/mmkALQ27N2ZbhwMl4SPGPi11ESSRmc\nolnCSAU/MUei5a4SDexyAPcn5b+KhoWh61uQTjc+uQihNkuZT+0Ydm2lqWONfQjr\nz4cwfzFOPX+2w9Ywbv9xpe6k8iQ6rXv7XvzUtsTSgEA/jpjho4LJO8wUQlqjh73e\nRd0zWjZ1dxJdNWGYMbm2ZKFmIZBFx0FP1Td4SBYVOSIO2lJzFpQaHTN2vGOSS6WX\n5ojD5cg0EbzbMDfZiVONu4QiH0GK2g2Djo7R0VzS8FKYfyjLDblhsrHbJVParySB\nx6UCX7ITccicYyum6dFyZoQQw0H9gPcgQM+M43rFiyaM+LwYXZcy97/MM2zg9/FL\nTdLKy2RzSfPSMjNcH0k/aAR7E1KusAOMBmZ5QVsCNZFwLt3OWUsWhe/qk5hf1YdO\nM+esukpQrcGkoCNX4w7//kbpXIUtVA9R1Y5TS8Jku8tMKPONouxslSbzm54nW6JF\nkWnbEGmTiR3pWVuNLXxMz0dWwtWotszcRqO0lRIQ+nH8U0sOBFE=\n=uXlL\n-----END PGP SIGNATURE-----", "payload": "tree 1752ac02cf1cf747b001c594494441de1cc84c5b\nparent 7e5008fd5ebdd4f63387eefd8b55b7f525719391\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1771006679 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1771186759 -0800\n\nRestrict graphs to guest user\n", "verified_at": "2026-02-15T20:19:51Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d57ea6c4609ec4aeac762eada135f61e77dd142d", "html_url": "https://github.com/Tautulli/Tautulli/commit/d57ea6c4609ec4aeac762eada135f61e77dd142d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d57ea6c4609ec4aeac762eada135f61e77dd142d/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "7e5008fd5ebdd4f63387eefd8b55b7f525719391", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7e5008fd5ebdd4f63387eefd8b55b7f525719391", "html_url": "https://github.com/Tautulli/Tautulli/commit/7e5008fd5ebdd4f63387eefd8b55b7f525719391"}]}, {"sha": "0a837049c1a62124087cca27f82d96603b75eede", "node_id": "C_kwDOAducuNoAKDBhODM3MDQ5YzFhNjIxMjQwODdjY2EyN2Y4MmQ5NjYwM2I3NWVlZGU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-02-15T20:30:03Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-02-15T20:30:03Z"}, "message": "v2.16.1", "tree": {"sha": "29906c05ae66188b09a715f305de4df80553029b", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/29906c05ae66188b09a715f305de4df80553029b"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/0a837049c1a62124087cca27f82d96603b75eede", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmSLMsACgkQm/TvTexB\nFjrf7BAAiJBpvOiXkjIRM9C4yrwb4tXrOM0MGeXNc20f9/6DafdbI/WLSustVgXd\nYZdb6k3/VjfVQpAQ+IqmTCGQJ2OvVvV4WeJgmppQkJUWJQ6UncOfI7WR1mULHFuY\nSOCqOsoRhz/85HyTjY77gjSeMqECd3TEFslgqB+SPzDIi7BjC0naeA6k57NMra+l\nqeCttaxQv1YHWZ6azD4iYM9kY0vxEYuVhj3N0FnFmM2vv8km5sIjciC7q15QdN6d\nEMmhfGI2t60/4oAPi4xEgnznDypBf5WDjZyFBPIPgHXklhdHc2JmDGPim2PEUH1W\nNtPqs6WRgdCl524meFbxllY2ZQYtPnffY2km9naiJM1ypj3VVlMfmNpbE1T7bmfO\nDGChkpnB4eXNCsQBeCFbqH8TWTp2QTmTRLSRp7phWU22WagNjx/YeN4i39br3HK5\nMeoV5OjCxrt/yfpfd7YUApHnU4UDV6rfJ4flQSEqbH/Xw0lZPTtxgxuXUQX9bc/c\n2zlq1oU1g/Q4liLMojqLzCEZFPRf4W/lHxV45bcudkRtsP2I5CcmCoKwTNW3JHau\nKmiVoi2BOcjvUjRT+7gSPXoLsJnffdoawp49JxhVIvwzUJGRqW8+H0WZfPYJMZIO\n2yPrV0guST4GqzdeR6rtX3OTbWyjhgcC5Dfdh7x8luV58nqy1bQ=\n=sNWg\n-----END PGP SIGNATURE-----", "payload": "tree 29906c05ae66188b09a715f305de4df80553029b\nparent d57ea6c4609ec4aeac762eada135f61e77dd142d\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1771187403 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1771187403 -0800\n\nv2.16.1\n", "verified_at": "2026-02-15T20:30:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0a837049c1a62124087cca27f82d96603b75eede", "html_url": "https://github.com/Tautulli/Tautulli/commit/0a837049c1a62124087cca27f82d96603b75eede", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0a837049c1a62124087cca27f82d96603b75eede/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "d57ea6c4609ec4aeac762eada135f61e77dd142d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d57ea6c4609ec4aeac762eada135f61e77dd142d", "html_url": "https://github.com/Tautulli/Tautulli/commit/d57ea6c4609ec4aeac762eada135f61e77dd142d"}]}, {"sha": "fe245b111d01deaf164c0c0eb3c3097c970a821d", "node_id": "C_kwDOAducuNoAKGZlMjQ1YjExMWQwMWRlYWYxNjRjMGMwZWIzYzMwOTdjOTcwYTgyMWQ", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-02-18T21:47:42Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-02-18T21:47:52Z"}, "message": "Validate log file is a subdirectory of Plex log directory\n\nFixes #2632", "tree": {"sha": "e2b3eecefe98504e489e99bf0128ff722396c426", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/e2b3eecefe98504e489e99bf0128ff722396c426"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/fe245b111d01deaf164c0c0eb3c3097c970a821d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmWM4gACgkQm/TvTexB\nFjr1mhAAqJNl6NPNbbepdr0mqGdUcQvDnWzJzstiySiX7zRYrsRMWgmc/IpZ4IZN\nY8T++hWXcKCN0b92NWlh+T0pTUDjFIq9UN8gmFwndrRfmk/zgBdTKVIKw35ngkNT\nW3+zmDo/Pu1j7/V3xiPOPzBMiPZmlOZi/sxl7//4zmZG6YxRrmEz5gSTxvTwkBuC\nCJb+erv5UVAnrIsHcKlo64+k1lnPQH9iDYV81F/S9lRmGK3Jguf7VnlgyNz/DGs5\nU4avrzdJ0Itz8f9/DTl2deNFhOw1uMOedGVMnvYAPXJvr6dQn0/tLcIgMCvb4P80\n6o6CgJH0O6HDfigWnZ+lcFT0e3vpfvSjZtDFr9WnIVqV/DVGXOrb441qm7SBYqMK\n8PrDnA1rSMNDaJFFMOeUjjbtDEyulCK0OZvv/Y/+s00pB7CaqCwG/CtLVJoX6Kbw\na8U3uJsPebhixgUfxfZDlRm7YptAOu5eVf2pSsO9jca24v5OoaBDbybp18WQpzwz\ni7I8YVPAxbFEN1/Zz2Y5CHUqFhFThm+gdSHXk1j96bCgCWcWQT1eF/7iqW+ueHfc\nciAdFbht1xpGi3RMENaArBOZGTIczD4HmqQlX/aI+3VqQGSng0DesYWq80o1u7Be\nfzOpHhs4rRH88rIpmo3EfMQAFFwAVTQZzDx0PbXq0lxnYKKgDhE=\n=9Ok2\n-----END PGP SIGNATURE-----", "payload": "tree e2b3eecefe98504e489e99bf0128ff722396c426\nparent 0a837049c1a62124087cca27f82d96603b75eede\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1771451262 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1771451272 -0800\n\nValidate log file is a subdirectory of Plex log directory\n\nFixes #2632\n", "verified_at": "2026-02-18T21:48:52Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/fe245b111d01deaf164c0c0eb3c3097c970a821d", "html_url": "https://github.com/Tautulli/Tautulli/commit/fe245b111d01deaf164c0c0eb3c3097c970a821d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/fe245b111d01deaf164c0c0eb3c3097c970a821d/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "0a837049c1a62124087cca27f82d96603b75eede", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0a837049c1a62124087cca27f82d96603b75eede", "html_url": "https://github.com/Tautulli/Tautulli/commit/0a837049c1a62124087cca27f82d96603b75eede"}]}, {"sha": "5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "node_id": "C_kwDOAducuNoAKDVjYWZkZTQ1NzI3ZmZjMjFlNGI4MGU3OGFkNTVjNThlZTc3OTdiMmU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-02-28T01:52:08Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-02-28T02:07:27Z"}, "message": "Prevent RCE in notification text eval", "tree": {"sha": "a2eedbdcfd171a3993ac9766b0eb860f2621dbcc", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/a2eedbdcfd171a3993ac9766b0eb860f2621dbcc"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmiTd8ACgkQm/TvTexB\nFjq7LxAA0HXmo2jS9ntPkftt2JKZZV1wPz62Lqm7a3d4micMIHtSdUdgXxQWVxdW\nhsl5aFnlmkRKEXLTJwkKHzftr4wkctHQmTxMN8JrufBPeLMB1n2PYgUayQ5R5QVJ\nmfEXT5S8ZQQiZOiOUOnPdpEqc048ij/ifErV9s8siqXuYPj63QJVjnjt4bGglume\n/xdrr/d/3HR40Izrj0B4/xrrllSeZOPp2kMAC4RkdOrjPxV9q8RkqpWtNwB0LR+3\nbzcEiM3r6vAUGyDY0twY8Hlj561ENKyekgvRS39aYkK+eZdVeH4XllG1olpGXUoP\nrC1kZOz229IrmtfVbILngEv+IXsKNvnRKwoAeYdIUMGu3E/asXxH6p8YFPVo9pUt\npSez16KYcqICucixtU5aOXUpdtjoEWiNf6NGvyPcMPaNJNhlpLeqs93PkD7DXhWq\nfNFyhNkmEBhtV22qCI8eXYl+kGS2aMUAvwNgF0/+0FsoyWcFs+Snr2TGyywxZ67o\n9Mh5b1t8OqOIKv4FiU4EBm7VciyqEVq2sT9aCXZSYsa8eM3xF7ruFDQlVT31guZw\n46VGUO3IRVmTEdsr1Aqf+BFjMAemzgt/iJ0N3ucavRLQmRtk82fNPNdY6eCtF/A2\ntDqyMtQsHygLXQrl5F7goc5gK7F8MnZfshJAuA9++5LO4fCxEL0=\n=heMi\n-----END PGP SIGNATURE-----", "payload": "tree a2eedbdcfd171a3993ac9766b0eb860f2621dbcc\nparent 0a837049c1a62124087cca27f82d96603b75eede\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772243528 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772244447 -0800\n\nPrevent RCE in notification text eval\n", "verified_at": "2026-03-01T02:02:58Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5cafde45727ffc21e4b80e78ad55c58ee7797b2e/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "0a837049c1a62124087cca27f82d96603b75eede", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0a837049c1a62124087cca27f82d96603b75eede", "html_url": "https://github.com/Tautulli/Tautulli/commit/0a837049c1a62124087cca27f82d96603b75eede"}]}, {"sha": "5aa2ff85ff4ed31049937d8051465563f4c40631", "node_id": "C_kwDOAducuNoAKDVhYTJmZjg1ZmY0ZWQzMTA0OTkzN2Q4MDUxNDY1NTYzZjRjNDA2MzE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-01T02:00:17Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-01T02:00:17Z"}, "message": "Merge remote-tracking branch 'Tautulli-ghsa-m62j-gwm9-7p8m/master' into nightly", "tree": {"sha": "a27c73f8cbd3cf89dee8130b4be0ca7d5887109a", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/a27c73f8cbd3cf89dee8130b4be0ca7d5887109a"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/5aa2ff85ff4ed31049937d8051465563f4c40631", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmjncEACgkQm/TvTexB\nFjoCdA/9EYYYtrRwpICPiB8iwBwC+lXUKTdNdxqjTYmudg2dRE0X6Ny7MQg3hF3e\nQMdjbvjs3rIFcUDcc8egGHHNzNq6yCeS33pdS2L1hUk5WlvFOB13K917lURmKbL4\nc4i09Z6B7DnEVk0uRhG4lgl9U7wFZb/4msgGGF2Y2hsJuNzAJhcuHHnbyLIImcsf\n/FypWSNTfpMESB45l2/JkzNJX0bO4kbwadxXoQ96mhOoTwXOMq++/bY2w4xHmugu\nUmsKcwizuOCpqZbzFdi7PPcDII65rMjGs5vIOGi2v5VWayd6x/2ig3mLDl6xAUdR\nZNi7rb1RVWrP8df+RxE/CUR/qsaIQrSC9gC7t/PA859V7Qol+poZQk/DwBmJfaUi\nGM6XfbVu1XuDt0ttgWSjDQ6NwyvpdL6wpMm0sPtS39M4hzXLHlVtjouedcEgsUQH\nNIC7ZeuFl8jkwxZzgjMx8IUTl2ICC+4T696FiLpIMnUbxR99K2cUQ8Kh/74Y98C9\nRTa7QAxmMs6yCooUlOvDGgJHdoO4CTOSs5nOS9UNNx50Irbm4mbGWgXhKs+iEQUs\ngbEFezucEBIh7vZ4skj3aDKksJhLqyUM83N5wA+cTrStkSmJMfTyAgo0rOZ2qXMl\nSMsCVHDdIEzUO1xSJe1pSDQRDvH0lm9xNwFzHD0ZhYeW2zEC8gA=\n=LGOR\n-----END PGP SIGNATURE-----", "payload": "tree a27c73f8cbd3cf89dee8130b4be0ca7d5887109a\nparent fe245b111d01deaf164c0c0eb3c3097c970a821d\nparent 5cafde45727ffc21e4b80e78ad55c58ee7797b2e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772330417 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772330417 -0800\n\nMerge remote-tracking branch 'Tautulli-ghsa-m62j-gwm9-7p8m/master' into nightly\n", "verified_at": "2026-03-01T02:02:58Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5aa2ff85ff4ed31049937d8051465563f4c40631", "html_url": "https://github.com/Tautulli/Tautulli/commit/5aa2ff85ff4ed31049937d8051465563f4c40631", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5aa2ff85ff4ed31049937d8051465563f4c40631/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "fe245b111d01deaf164c0c0eb3c3097c970a821d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/fe245b111d01deaf164c0c0eb3c3097c970a821d", "html_url": "https://github.com/Tautulli/Tautulli/commit/fe245b111d01deaf164c0c0eb3c3097c970a821d"}, {"sha": "5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/5cafde45727ffc21e4b80e78ad55c58ee7797b2e"}]}, {"sha": "6f85479c7ba267d7d3b0fc1116b6f302837dab82", "node_id": "C_kwDOAducuNoAKDZmODU0NzljN2JhMjY3ZDdkM2IwZmMxMTE2YjZmMzAyODM3ZGFiODI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-03T23:00:09Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-03T23:00:09Z"}, "message": "Check for error before returning HTTP response", "tree": {"sha": "82cda435852f79762ba8c82d0b3a2455634c1fc5", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/82cda435852f79762ba8c82d0b3a2455634c1fc5"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/6f85479c7ba267d7d3b0fc1116b6f302837dab82", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmnZ/oACgkQm/TvTexB\nFjpWOw/+KnmoFjNf+jZ7X15gL4doZlJgS4pZtLjnG0frP9B9oRxkqaG8/T12ovAB\nWs5W5RAyIyfwdvFtLcANyWOoSP7e+2+S9GBLQOoModZl4Yv88H3s0t6e6oDjqIAM\n+ES+Uep4RZ56xpXuklK90sHlq7yKq5cKK/HOnWoyeM0ibx9cHdMF1cSX1JOuYtSh\nYcooGBUzBPbnvrg2dEU9Tp9tNzr6C7j9S0NQWe5CJodm4u6HXyR76SG2YO3DmGmr\ngoct9DNAJxH3DIcUCvUh7/wdIN6QWqvgUmycq3w2ltZDU/B/FcY2DJYIr3OQgZQi\n0o2FZrEgFVTQ3ZfZlpqG8RlzBG6iFjmkH+1qsRtOAhOvcELcQ0fUPe+/U8WBZYKh\nyIeE6ehm8XmvmfPuYKjEyRV5vEM5IoRp08FkD2xetDGfRJHUudrwD2UaEaywtizL\nExLFyB9sIrwXkNtID/4O6qHu6lCcNyeuAEW1/L80b1nZ6Zhr1nqz9KDwzCnkK6AU\n42/XxwAMXg20ytQ/lPW47TUs2ZFe//DVee9KzDvz1XpZ2oJ7sl1Xt/AP3QdGVr/B\npRr1y/8gPwvR+P2+7EKtIrfKz4mn3NtTCP4FEjQIbf/MnpefdeZiD8G/cuNe/dd/\nxyHNqSwM9PE3aM/HCQ9cRnYGm8H2IjHwhQc3aNS26v6mEF4Z6rY=\n=SZFE\n-----END PGP SIGNATURE-----", "payload": "tree 82cda435852f79762ba8c82d0b3a2455634c1fc5\nparent 5aa2ff85ff4ed31049937d8051465563f4c40631\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772578809 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772578809 -0800\n\nCheck for error before returning HTTP response\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6f85479c7ba267d7d3b0fc1116b6f302837dab82", "html_url": "https://github.com/Tautulli/Tautulli/commit/6f85479c7ba267d7d3b0fc1116b6f302837dab82", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6f85479c7ba267d7d3b0fc1116b6f302837dab82/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "5aa2ff85ff4ed31049937d8051465563f4c40631", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5aa2ff85ff4ed31049937d8051465563f4c40631", "html_url": "https://github.com/Tautulli/Tautulli/commit/5aa2ff85ff4ed31049937d8051465563f4c40631"}]}, {"sha": "4899367b52f5c9c3f8a3a4b84e4820aed8d773e5", "node_id": "C_kwDOAducuNoAKDQ4OTkzNjdiNTJmNWM5YzNmOGEzYTRiODRlNDgyMGFlZDhkNzczZTU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-04T03:12:54Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-04T03:12:54Z"}, "message": "Fix newsletters for modern Plex Personal Media agent", "tree": {"sha": "e77783e0a14904af703687c94c6bc81c30fd8464", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/e77783e0a14904af703687c94c6bc81c30fd8464"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/4899367b52f5c9c3f8a3a4b84e4820aed8d773e5", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmnozYACgkQm/TvTexB\nFjoFFQ/+ImAEIcftYo0pTf9PppvpPDFJMvLlElaUmDhjIXoNk0051gA1lzuamsq7\n69Em38nn+QlebNg7PJwqsF+2CaUuEgK2SoEPRfvnfT8DS5bgTeDdvtG4PfmTlmd6\nhByKDUQYUC/nFl48vjPHZ678rCRSJaShBxeyYsUbHDIxhEX6YYe1scz9/p9jyZRg\npgI7PgulQL/U5WMK7J+yGsvHbH4/+WbH6exzEkbBmoQdOv7cyU53wRd00Bi0NEsF\n0jCn3DigstBCrOyKROj/3jKt/VKHEizjSVCjaCBoQWQSG79XVhw7XvuxvWBIjRkQ\nJJioVL2aq8gBrGHQQxDXgJNdH8ccOuEZgwoNiKPFYBlNXzIOaO1aKHqcH0hZtpG5\nKGINHliUJ4Jh+3j2vQYWIpAHvS9H4U16MML4+mrNg9SDk49+UGUPvtrBEz2Uo8dn\nIJPyPdyoV5ubsqYJzo/qK79hgSG81JVuIWo5rEY78PlTq0juBfneWDYAI+NR1kP9\n7ykMoiXa+GdoNuyss/gmNH4EwJLbGGGmVW4SiQKAXTqtKRARLZWXDJwpijH1tTwy\nQYA8+ZgteSmcGPrBjUCZdvrBmbBQkTzjCjXOYXSeKl5GLqfg7e7/1VeZ+sc7+T69\nSIsqAyBhx2N5pl/tU8DF1T8I3a/P0W9qGFqT06UEXUhoH0zKfB0=\n=Bo4c\n-----END PGP SIGNATURE-----", "payload": "tree e77783e0a14904af703687c94c6bc81c30fd8464\nparent 6f85479c7ba267d7d3b0fc1116b6f302837dab82\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772593974 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772593974 -0800\n\nFix newsletters for modern Plex Personal Media agent\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4899367b52f5c9c3f8a3a4b84e4820aed8d773e5", "html_url": "https://github.com/Tautulli/Tautulli/commit/4899367b52f5c9c3f8a3a4b84e4820aed8d773e5", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4899367b52f5c9c3f8a3a4b84e4820aed8d773e5/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "6f85479c7ba267d7d3b0fc1116b6f302837dab82", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6f85479c7ba267d7d3b0fc1116b6f302837dab82", "html_url": "https://github.com/Tautulli/Tautulli/commit/6f85479c7ba267d7d3b0fc1116b6f302837dab82"}]}, {"sha": "fdb7a035f66b41e916fdf0755448e4ac77351ed3", "node_id": "C_kwDOAducuNoAKGZkYjdhMDM1ZjY2YjQxZTkxNmZkZjA3NTU0NDhlNGFjNzczNTFlZDM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-05T00:07:27Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-05T00:07:27Z"}, "message": "Fix opening graph history modal", "tree": {"sha": "541502c3ac7839119379e83f4b44b7369a3c05e6", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/541502c3ac7839119379e83f4b44b7369a3c05e6"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/fdb7a035f66b41e916fdf0755448e4ac77351ed3", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmoyT8ACgkQm/TvTexB\nFjp9IxAAnD6eHgkBf38utANfnViHm2jTzNuTX6HR1XGhChzUdMkowJfV4i0w1U9u\naUpM5FPn74xDmqzj8npLiKDEue3p91p7iByv2LnXT24/vceRLAqF55KTLVp12Mye\n/Hkhmm3zGKQDKr3jVSgu9WC2co2v5GNvbPh+b4rrp7iIh1Ap116/9U6Ut63BkFEi\n8Hc+94LUSf8bI24MMXekp9SapQ5QGnX8sQ8eySQYmnzUeJz7V2bT7uoMlTsPMHmw\nxH7eAr/ZosDL3APjKGSiUIF/d8jAgR80HlXCZjCuJbkZK906pvmB6DfDj3YCe/hV\nEAwbODtWFJSDgb3VU3XPi3ZAvmv7qqr7d6U5g9HZkmviMLW1+Bq8AAZj7ycdSZMI\nIyw/02CkKNdS6h3Iq5zJgIGSx88lxNA0uF71Ru9DJ/uFuqmh+xnJA3SKUAaBVHeH\nW8QJv5KjadJtujuJnFTfeM6bHEt8c+nTphDf6vukJ3H+1FisOFRF1ShhinjcD8Qm\nyi92tsSVrNVjMiIPRZ8Zk1A2d+c+fl4VNonZ9IxqmsQu6M2Bv1T5zd3B4CH9bxqk\nfF/NSwPnaXGK6g5Nu9YZxN5zjxGe/KD8I+QNFxKBoSAwMm2zhHxzyHG90MEAWKJN\nayuM6p46tbNKwevKAkOxE0eHNtFeM0+H7pKW+2D5gM+bBSy482o=\n=RyXk\n-----END PGP SIGNATURE-----", "payload": "tree 541502c3ac7839119379e83f4b44b7369a3c05e6\nparent 4899367b52f5c9c3f8a3a4b84e4820aed8d773e5\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772669247 -0800\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1772669247 -0800\n\nFix opening graph history modal\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/fdb7a035f66b41e916fdf0755448e4ac77351ed3", "html_url": "https://github.com/Tautulli/Tautulli/commit/fdb7a035f66b41e916fdf0755448e4ac77351ed3", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/fdb7a035f66b41e916fdf0755448e4ac77351ed3/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "4899367b52f5c9c3f8a3a4b84e4820aed8d773e5", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4899367b52f5c9c3f8a3a4b84e4820aed8d773e5", "html_url": "https://github.com/Tautulli/Tautulli/commit/4899367b52f5c9c3f8a3a4b84e4820aed8d773e5"}]}, {"sha": "d13533d0ed9daa642dd7f86b24fe6bae0eef4229", "node_id": "C_kwDOAducuNoAKGQxMzUzM2QwZWQ5ZGFhNjQyZGQ3Zjg2YjI0ZmU2YmFlMGVlZjQyMjk", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-08T23:35:56Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-08T23:35:56Z"}, "message": "Fix unauthenticated path traversal in `/newsletter/image/images` endpoint", "tree": {"sha": "61d6c6d9d1a69e0846e33cd9bbff83bc79134f1a", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/61d6c6d9d1a69e0846e33cd9bbff83bc79134f1a"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/d13533d0ed9daa642dd7f86b24fe6bae0eef4229", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmuB9wACgkQm/TvTexB\nFjotbw//ZuJy4DqTvyw1cmSc+ldWAUoJZZkd5MmKzRxJxTX/s3zRqXFkRNVpVvky\nQzDBQIuYNmjvBNrlVvk+vxp5ykHb5NZbzpo/sFfjR1mKiha04LWIuwYXDfTvPcV0\nSfCmGzvNaA5hVshd+ifO/Y11Upi/lJSb9gQ9XH802ACKScdGznb7WxH+bD09JTl2\n0jLw1oPQ0uUQZ4oD2Pp1qJRiTMZLc0HZ/eQfV/tqQGUdPCGOB2QxMhh85e3vcZzt\nUjkLWU3xix7ogLwFoZYGYR33i9SbP2y7uYqX2G+MsGlDW6sr79mEttdgQIdL7thp\nghrH2fxiVUvMSFdqLmvP0Jm55QiOORePxHIRp2AiOe0zw/iovIwdy0ALDL1j3tnl\nUQNvqkLe7QhSShFBimxWK7a4H/qfaRnEKCOLV5mQrcVHUugeEETLoHpqFfETogtF\nou0VnGPW73yq3pZRk7PKPlDq1beUKXOI7SfCxcFqP8Rr/SyOi8Rj1LakpqA8kqlT\nF6xZyOMW9uyN9Iflhi9EMUiMlOAyHaHs8Y34LvBcS8bJ8RzvaPd5yAt4cx4iEGYc\nnaSf0hG8wvNsgSy/S2I95El0OMSQlE9kNxnqRwZRbO67YZwP0fjbYg+e+01j8oKg\n4HL4gYma4oMp8N++Y5xsnhkOmqQQT/VhFaE4Nwo/lFrIsM1YdgQ=\n=bxyj\n-----END PGP SIGNATURE-----", "payload": "tree 61d6c6d9d1a69e0846e33cd9bbff83bc79134f1a\nparent 5cafde45727ffc21e4b80e78ad55c58ee7797b2e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773012956 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773012956 -0700\n\nFix unauthenticated path traversal in `/newsletter/image/images` endpoint\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d13533d0ed9daa642dd7f86b24fe6bae0eef4229", "html_url": "https://github.com/Tautulli/Tautulli/commit/d13533d0ed9daa642dd7f86b24fe6bae0eef4229", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d13533d0ed9daa642dd7f86b24fe6bae0eef4229/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/5cafde45727ffc21e4b80e78ad55c58ee7797b2e"}]}, {"sha": "ae4daba2b30bb641b4f9143a80ce0e81f690ce23", "node_id": "C_kwDOAducuNoAKGFlNGRhYmEyYjMwYmI2NDFiNGY5MTQzYTgwY2UwZTgxZjY5MGNlMjM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-08T23:49:57Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-08T23:49:57Z"}, "message": "Fix SQL injection in get_home_stats", "tree": {"sha": "25bb26799b38dfbf84077fc8a39de3ce91d7ef17", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/25bb26799b38dfbf84077fc8a39de3ce91d7ef17"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/ae4daba2b30bb641b4f9143a80ce0e81f690ce23", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmuCyUACgkQm/TvTexB\nFjo5sg/+Ltp5JVEBXdm3xVBPx+8J4A9Je04i64njLvJVMliwtt1bB1zXZiwntbOE\nn/Ae695/CKR7dD0XBvqUwSTmXoYlRGM4I68GJJQm/V674fj6rPep9SFeDqbkzLyd\niBSOT3ND3mmX8Jo6tFj+2NopSbtMwlKn9hZnbs9NnJNxqElAsSFaFUvs3XQlN8Ci\nOhxthYGKQPfs7w1eo0yQ3VKPOZk88AdAoyWatD4dN3TU12WIC5CWkmxotu1CAhbU\nwAxx5Vboh0P05kZuu2ShgjqsfX9/U1yRE10HpHr8rmsGFleFeG+D9X0hbl2eEMEV\nXKV5AEOstk9RABJ900gaUYa4PmujWXpxNXp+i8r0EOD+MZYFLeEvhGnlL1Bxc3rT\nj+yvOMUKC1R/e14FmjxlVbxEiFQF+jmmcXL4Se7Drute05wxoU+v+9JN8DdHyX8I\nLbynflrQkfW1hnlmAzxY8tCpnc2QMltTAbOKc8aS4gzIN7nxrgUs87IpQ/yLyIpb\nspmGm11I4H1bNuoDFJ4pWlXdUGRjLYCwPNB5UPYFHoA2M4zaRq9SXU3Rz4Q5+KFf\nOHFwXyUr2OMbhkSMlpCPX3p8Lu2Nrel63UlpZNK9pmqjEQuxz9iKWdojXVGCwpHm\nIQzg5f8vNG9UmA3gt/VTxPvAUyx2vaqL3wkZ0zxPoGcezUNXCT0=\n=M5Gc\n-----END PGP SIGNATURE-----", "payload": "tree 25bb26799b38dfbf84077fc8a39de3ce91d7ef17\nparent 5cafde45727ffc21e4b80e78ad55c58ee7797b2e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773013797 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773013797 -0700\n\nFix SQL injection in get_home_stats\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ae4daba2b30bb641b4f9143a80ce0e81f690ce23", "html_url": "https://github.com/Tautulli/Tautulli/commit/ae4daba2b30bb641b4f9143a80ce0e81f690ce23", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ae4daba2b30bb641b4f9143a80ce0e81f690ce23/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/5cafde45727ffc21e4b80e78ad55c58ee7797b2e"}]}, {"sha": "59906efcc172abe2cc3d7f259239ed21fed86f2e", "node_id": "C_kwDOAducuNoAKDU5OTA2ZWZjYzE3MmFiZTJjYzNkN2YyNTkyMzllZDIxZmVkODZmMmU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T00:01:18Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T00:01:18Z"}, "message": "Remove get_apikey command", "tree": {"sha": "be95597efad8df0344e29cbb307812284baa1b9e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/be95597efad8df0344e29cbb307812284baa1b9e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/59906efcc172abe2cc3d7f259239ed21fed86f2e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmuDc4ACgkQm/TvTexB\nFjrotRAAsDbHWrrmbgK2ZDKf9euhxtPemEFem6J6ysRdyg47FJwo5GwogWKApsc4\nUtvBHJ0inE4at3x9IpeJZfcFLmrPLkIUqiXvncNhMUPtstilEGufA4ROoAsN3LmG\n56KglsHMQwBKmlRIUtEb4lxBH01TS5E46P8tJwB3re5IjMkWykFw+Xg3KojixMp+\nIRS91WPhVg/+GQJlcxAN+brvxUYTdhQp5T+YCLkCCnWUuNy6JQM7D1A21XHuUcC2\noG0cjV7Pyo0LMCllPIESqBwUrncSmzMvAt0X5efKqM62hmE8r9tdRXb8IkcFdXFQ\nZPd2vvU7EupxGn4ms2jBXjvR08KzaWH2ZraxnXyqSlouvVMpatBAVRVovE5HE+VJ\n2KoEwtimynyxmnPWuAZ7kP+IMKTLEY3/9T0u6K87qM7yd45XdQRb4cfPNT5dCaBl\nqJzYV4rJ1i6gWE8aWbZ2VNIdkrKJJQlKLzHyXxjNzPp4We5HN/YLA2OwkfF6V0Kq\nhF7CfI3cQtnKSVzYPnvcC0wHAfu43l7kOda1wAzJhJUD59uBAZqlIqHIkXPE5FuF\nG2w510D2X/uTFOuF7o1YMd0EhiOxy7vFSfjU9PaO8XGFOhGJ2OH1YqQk2+34CquA\ngtrv8asqofSSnh6B0bbG6S/UW4MQxGfTyx+fjXbTLAWF53lUfu0=\n=J5oN\n-----END PGP SIGNATURE-----", "payload": "tree be95597efad8df0344e29cbb307812284baa1b9e\nparent 5cafde45727ffc21e4b80e78ad55c58ee7797b2e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773014478 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773014478 -0700\n\nRemove get_apikey command\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/59906efcc172abe2cc3d7f259239ed21fed86f2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/59906efcc172abe2cc3d7f259239ed21fed86f2e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/59906efcc172abe2cc3d7f259239ed21fed86f2e/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/5cafde45727ffc21e4b80e78ad55c58ee7797b2e"}]}, {"sha": "70a71b275a2edd76873f75e86f502605ef745c68", "node_id": "C_kwDOAducuNoAKDcwYTcxYjI3NWEyZWRkNzY4NzNmNzVlODZmNTAyNjA1ZWY3NDVjNjg", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T00:01:51Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T00:01:51Z"}, "message": "Add regex check for JSONP callback", "tree": {"sha": "8ea4e45d6065489f87e7f90aa308e0c313ad15f3", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/8ea4e45d6065489f87e7f90aa308e0c313ad15f3"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/70a71b275a2edd76873f75e86f502605ef745c68", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmuDe8ACgkQm/TvTexB\nFjplrQ//czgVu23cOhWKDQJcoX+3T7Wu4dRoxuji/KA1u+M0CEsDD3ph2STxfTf2\nYJraSJn/xtKPWiHYlr8dTq2U7kXXNpLOrl2uCebHlHjfHNzv9V5xfpItloSfm6m/\nCnRS95wKtmCa/VyNkBBYIULE4VcReZUGaK4pH/fpisrZKHyReRAN6USfAJ7pOSPI\nQtfC47ljkGVETxJ5I92lIewYqRF3LRK6wRrTtamDiKX27Oil6HmUnFs85MC+YQM3\nE7Iv+3rI3AbllJTggyfEfZnEen/3MRsbTtIjTA0ZvtHM9OnzpdDbaMPb9baQM8vQ\nra1WKfwNNFuGMU3en6pwY207tG6M3ph77qIG9KM3pe/pHhW3ar6TmwpcYgeFP4rR\ntTnISJ7XM4RwZHOEdhO3Ek5VgMhmoprFpUMGOn0LgcpjnbXX/IzkQ2L2m5D36uth\nIhKoZY0TjaYb7Z2Jj+ACdVvD8hHrQsI6O1nWZW1jex8cr0J/abJGY38OpBr1VgFC\nVGuPETuAiZ71W6R0/veiyMn+n8amgTbfvxafhURrD+ddly/NjiOsTMn+v6J4m5x8\nxdrdoZrT4q1mocv+68E3wPRnSrmYchhTFYf8WQ+69Nbr+4wF/zRPT2mThvIAwFL7\npIrjyGbOGGBUQ3I3bVvW/RqoL7t6A6H6IgeXtJ2hccBlqbmbso8=\n=f7Mm\n-----END PGP SIGNATURE-----", "payload": "tree 8ea4e45d6065489f87e7f90aa308e0c313ad15f3\nparent 59906efcc172abe2cc3d7f259239ed21fed86f2e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773014511 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773014511 -0700\n\nAdd regex check for JSONP callback\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/70a71b275a2edd76873f75e86f502605ef745c68", "html_url": "https://github.com/Tautulli/Tautulli/commit/70a71b275a2edd76873f75e86f502605ef745c68", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/70a71b275a2edd76873f75e86f502605ef745c68/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "59906efcc172abe2cc3d7f259239ed21fed86f2e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/59906efcc172abe2cc3d7f259239ed21fed86f2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/59906efcc172abe2cc3d7f259239ed21fed86f2e"}]}, {"sha": "46381de3f1c47301dbb8236c6d74df34ef7f4d99", "node_id": "C_kwDOAducuNoAKDQ2MzgxZGUzZjFjNDczMDFkYmI4MjM2YzZkNzRkZjM0ZWY3ZjRkOTk", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T00:53:51Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T00:53:51Z"}, "message": "Fix authentication to pms_image_proxy", "tree": {"sha": "d5a0922d6e81b879556816cae5b5c9920d3b24ea", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/d5a0922d6e81b879556816cae5b5c9920d3b24ea"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/46381de3f1c47301dbb8236c6d74df34ef7f4d99", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmuGh8ACgkQm/TvTexB\nFjoaoRAAoj6Q6JCJ5CHNFq5ZDTSNiDr2ZgesPPd/CqmaGC/WOTeZdub6vd+4hxtg\nub2oLSdaeG0vj3U6i8j9dLp/TIu01I7xIu8Bi0TuLMXtrtPAvUOReeFwrmKQd8Cn\ncD2SA8bI7IEg4q1g7CySVN5bZJiTRMyGRV1V21/sLzYvQ5Adzeuyi5jI5aaRfGb/\noNVXFpECDvAEWhnRCh+BPYw5/gBX1ymph1I2eE8J0GDCcVon2M8GBuS8ckJf1tFq\n2OtQCxNkwi5HJS4udYdwF+mTmATGvmClRDliBG+OAXorQEXGHmmQpjceQl7CBSt8\nH59TiZHPLNfUpE2l9MksL8FqYiUQ7p1K9OSGhdL4VdhvxUNR11VATR8YItE/S+Xu\nc04Mo7Uiw+k64/3u9qsTi9WzIiCrniQrHrZ3iXRq7guFsfdk7ibf+kTL8A/QlG2i\nJ1ayNMZyge7kly9paE2uFaawCGswlNlZjqcsrSLq41OKKPE5SLK7msekFDNsdYom\n2eeeDbyrUhjBbBYSPlVV9almocsj3kzTggcwEyIHzaMw2lpEZLhDKTnXPH4wwgFc\nJASV+6oMGPLfynbMtgNyjZCiM19qcF2FKugQ4fQhS7cqTY4WeGOQQjGZDHmiZP3W\n+jHAByydOft5x3eNC/h3wA9bkpCdKFfII0iDNKkDP/8imrFvLmM=\n=d0Pm\n-----END PGP SIGNATURE-----", "payload": "tree d5a0922d6e81b879556816cae5b5c9920d3b24ea\nparent 5cafde45727ffc21e4b80e78ad55c58ee7797b2e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773017631 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773017631 -0700\n\nFix authentication to pms_image_proxy\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/46381de3f1c47301dbb8236c6d74df34ef7f4d99", "html_url": "https://github.com/Tautulli/Tautulli/commit/46381de3f1c47301dbb8236c6d74df34ef7f4d99", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/46381de3f1c47301dbb8236c6d74df34ef7f4d99/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5cafde45727ffc21e4b80e78ad55c58ee7797b2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/5cafde45727ffc21e4b80e78ad55c58ee7797b2e"}]}, {"sha": "db7d9ed8503b0fde76101de786693ea52c5e9bdc", "node_id": "C_kwDOAducuNoAKGRiN2Q5ZWQ4NTAzYjBmZGU3NjEwMWRlNzg2NjkzZWE1MmM1ZTliZGM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:47:08Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:47:08Z"}, "message": "Use pathlib to check for relative path", "tree": {"sha": "601c6731fdb3937a3e0175dd03770fa8e564743b", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/601c6731fdb3937a3e0175dd03770fa8e564743b"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/db7d9ed8503b0fde76101de786693ea52c5e9bdc", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmu+YwACgkQm/TvTexB\nFjrU2g/+OIsazcK+oxAubgl7PIr44cWOh2vbis4Qukg8+kNlOnY/yIi/eVWt/Dmr\ngyQl1fY1RXdTs+suP8ofkf0dSsqUBJpixU1I7AAQ1YAGmRlWUiVSDBMMhvnGXUUG\n88LUux3NMuvKTSyWDJCSkCZK0ztT9CipvfFVfOblAErQmykc6ZJND803h+VVloKY\nLXLNld4WBfLHq/FvO2fzmu9wbFt2sOhzk5gF9ecbeR3ghz+Mg/aduYZQ2RBwKQFg\n7OmKF2qQ3fxAg2m+Ju/69EX1O/34Dvb81GIw1enHSdWL/ZAJ6OEHJ2ZBqs1UNJCm\nY9fv29dLrel3TArzRDBESqmogCBl1Q0nPUrkTjLYXTzXzO3tpj+1Bff7vggDLIiq\nd9AYRWoFDT44nH2AF7izOnRUSTH1UIIwVKdGh5XxGWHgbpD/5EKVuKTtPKRm0jD2\nwekFys9voSvtyTGwuG5zK4blmYKUI3q2C6sdUro4Ff9s4f4lablTh17dCjdRG/5Z\nL5gUq66+YRKD/BMfms+nj1oFJeP9NxOA0/RnhJkEizQZJ2uTASR3PpMsrTUG23Eu\nYnze87/rIs9KPva/X6BO3XkHQ4Ko7HXz5HVbzxNPNpZ7+EZOMWm3gTJj/Wqutgrh\nQjxv+elAS2wsRnA4QEYbmODUdHlbuR5JhObkEOyXs66C9FhRj30=\n=fio7\n-----END PGP SIGNATURE-----", "payload": "tree 601c6731fdb3937a3e0175dd03770fa8e564743b\nparent d13533d0ed9daa642dd7f86b24fe6bae0eef4229\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773074828 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773074828 -0700\n\nUse pathlib to check for relative path\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/db7d9ed8503b0fde76101de786693ea52c5e9bdc", "html_url": "https://github.com/Tautulli/Tautulli/commit/db7d9ed8503b0fde76101de786693ea52c5e9bdc", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/db7d9ed8503b0fde76101de786693ea52c5e9bdc/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "d13533d0ed9daa642dd7f86b24fe6bae0eef4229", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d13533d0ed9daa642dd7f86b24fe6bae0eef4229", "html_url": "https://github.com/Tautulli/Tautulli/commit/d13533d0ed9daa642dd7f86b24fe6bae0eef4229"}]}, {"sha": "21525156040cae1cb6fba7047d1ae6631a09bfde", "node_id": "C_kwDOAducuNoAKDIxNTI1MTU2MDQwY2FlMWNiNmZiYTcwNDdkMWFlNjYzMWEwOWJmZGU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:53:47Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:53:47Z"}, "message": "Merge remote-tracking branch 'Tautulli-ghsa-95mg-wpqw-9qxh/fix/api_jsonp' into nightly", "tree": {"sha": "120c5cdc2b496d03e6ef0bda513b6554ffac7be1", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/120c5cdc2b496d03e6ef0bda513b6554ffac7be1"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/21525156040cae1cb6fba7047d1ae6631a09bfde", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmu+yUACgkQm/TvTexB\nFjo3tw/+IgTYig3UTrI6EK2+397jY0JdXv730pWy8bYZbY+CHYe3h0oIaxD1ZNMI\nzMbCIFKx3hXKAFqp9SF9DueM3WFPEBdsE2bYOKX0eOn16bROjaIwo4rVKySJHC2H\nInQegv8UdHn7lwmk8vUdozS++GZ4H/maWNNJMRvrZLbn4U8vI3eEq5xRJphotYrF\nlp1JNu/ah5AKcLqQ64HtqrsFvzBb0fvHyZgnGqgmvTsvaPHS6bK3lPOJTMRh6QbD\nizWl4EwoYyaeKojj15jIsfYZ7p0kbAdxPCupzPUgLWpBfKs6iwyu1x7EuRYPwWOV\n/yJgtzw2M8VALjJKIGp4vCZh7kkJGJ1n1VvJWsrpU+JgLuw/7wUnDfEJv4OPC+YK\nkDF4dXvW2LPNGuEGpmKrJxc8lJv3VMo+EJ/YTibRfCRxk3Ux/MJRvf2cJnM75Vui\nwuVR3tz84TB7FWCxEa1ToNfhI/J5LwqCkBDsv61R0m8lPvR5pRsLFYqyVrhNxho0\nJp5B4WovCJ7bXGrl7LcKL2ecYCdssLxQTFk8vbMwJZjxVYOSL4o43VcTuGLMBxJt\nyHfJzweTy97Cmv0oNNXC4E/vb+M5xN/GWHPmg4HB5XW66uC0ln8fmzUwd4XDMBWW\nvzVeBF46HC52ow43F4FY/ILD5lCekssGQ23GuyL5MGoq89OQDio=\n=WoU8\n-----END PGP SIGNATURE-----", "payload": "tree 120c5cdc2b496d03e6ef0bda513b6554ffac7be1\nparent fdb7a035f66b41e916fdf0755448e4ac77351ed3\nparent 70a71b275a2edd76873f75e86f502605ef745c68\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773075227 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773075227 -0700\n\nMerge remote-tracking branch 'Tautulli-ghsa-95mg-wpqw-9qxh/fix/api_jsonp' into nightly\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/21525156040cae1cb6fba7047d1ae6631a09bfde", "html_url": "https://github.com/Tautulli/Tautulli/commit/21525156040cae1cb6fba7047d1ae6631a09bfde", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/21525156040cae1cb6fba7047d1ae6631a09bfde/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "fdb7a035f66b41e916fdf0755448e4ac77351ed3", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/fdb7a035f66b41e916fdf0755448e4ac77351ed3", "html_url": "https://github.com/Tautulli/Tautulli/commit/fdb7a035f66b41e916fdf0755448e4ac77351ed3"}, {"sha": "70a71b275a2edd76873f75e86f502605ef745c68", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/70a71b275a2edd76873f75e86f502605ef745c68", "html_url": "https://github.com/Tautulli/Tautulli/commit/70a71b275a2edd76873f75e86f502605ef745c68"}]}, {"sha": "df673fdcaf0d106ead56298f2332f6597b926d40", "node_id": "C_kwDOAducuNoAKGRmNjczZmRjYWYwZDEwNmVhZDU2Mjk4ZjIzMzJmNjU5N2I5MjZkNDA", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:54:31Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:54:31Z"}, "message": "Merge remote-tracking branch 'Tautulli-ghsa-qj2f-4c4p-wv97/fix/pms_image_proxy' into nightly", "tree": {"sha": "665c9884d927b9560b9fdfc5977fe080aaf0e9ca", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/665c9884d927b9560b9fdfc5977fe080aaf0e9ca"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/df673fdcaf0d106ead56298f2332f6597b926d40", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmu+0oACgkQm/TvTexB\nFjqqWhAAglnMfIO6lMODV5TSciGKoOhrjdiNb6KP8rOhLqR9x1ifni/zJtjU+igw\n2BQYSn6dIZBbeUeAReRbbLJroyZ1y9nDysE/aUekCfbeZWxBMOldMvBGiRFL+yCY\n7JwDAAzajnGnIQIgl2KskcrhGT/PvEU+m4A/gxOXXRD+2iWHBlPwP7X+RDSxxXPW\ndW8e/qKfj3JobURgufXJGMO41PYYl3cx4dsupEDumu1zxobqN0McuY/95CFknhOQ\nav8YPjj99+5IrGJUX3/NdZBSKt16DI2qghIsKDeC/CFeRObuf+s6P4RwEOwO8QDW\n0cszBjqNtm4UOKDJEoLHe3LL6nHok/NoLKPMCay7t5r+bE8W0+6REDHQ/U6MmmIY\nJR0w2du6YodcX2J4+vaZPkI+T48a4mgQTjNZa44d1HFWMfStQiabjTsCFfU/Xxmt\nqg61vdAVElx2PZbLHN8nJz01ZMdoOuyFc1G2aD83DqCZ+GIhRRmSXjaJTHcx/WHA\nBcqKebplGVKo7jO4SMVneZgrBb4ibGEHs09/q5N78ueUkfMXFpwezsS63QiWBFgH\nPsfSsXpCHuRFK4CUDhVjKU3yYy92L4l9W7YGUvFCtd8jqsDDaqC6vQipC0mfWqMt\ngei0fWQ7s8R0LNtiPxDbfxdR9pL7hGusrgugo1YTzR3t69Dt2MY=\n=tjKn\n-----END PGP SIGNATURE-----", "payload": "tree 665c9884d927b9560b9fdfc5977fe080aaf0e9ca\nparent 21525156040cae1cb6fba7047d1ae6631a09bfde\nparent 46381de3f1c47301dbb8236c6d74df34ef7f4d99\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773075271 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773075271 -0700\n\nMerge remote-tracking branch 'Tautulli-ghsa-qj2f-4c4p-wv97/fix/pms_image_proxy' into nightly\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/df673fdcaf0d106ead56298f2332f6597b926d40", "html_url": "https://github.com/Tautulli/Tautulli/commit/df673fdcaf0d106ead56298f2332f6597b926d40", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/df673fdcaf0d106ead56298f2332f6597b926d40/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "21525156040cae1cb6fba7047d1ae6631a09bfde", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/21525156040cae1cb6fba7047d1ae6631a09bfde", "html_url": "https://github.com/Tautulli/Tautulli/commit/21525156040cae1cb6fba7047d1ae6631a09bfde"}, {"sha": "46381de3f1c47301dbb8236c6d74df34ef7f4d99", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/46381de3f1c47301dbb8236c6d74df34ef7f4d99", "html_url": "https://github.com/Tautulli/Tautulli/commit/46381de3f1c47301dbb8236c6d74df34ef7f4d99"}]}, {"sha": "7651095698ce85c099b56b97d6340ce53714e022", "node_id": "C_kwDOAducuNoAKDc2NTEwOTU2OThjZTg1YzA5OWI1NmI5N2Q2MzQwY2U1MzcxNGUwMjI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:55:07Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:55:07Z"}, "message": "Merge remote-tracking branch 'Tautulli-ghsa-g47q-8j8w-m63q/fix/home_stats_sql' into nightly", "tree": {"sha": "330099000645b3aecc427464c63787b368cc054f", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/330099000645b3aecc427464c63787b368cc054f"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/7651095698ce85c099b56b97d6340ce53714e022", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmu+20ACgkQm/TvTexB\nFjrGRQ/7BJpy90LA+CAPlQxfwforxn5fnKwtrmt7Y4kDr6D/9yo3BQvDtaLiy+sW\nuX7h3mfI8wyzs3ThFGLa10cYHZYYGulaoZhQ9bv9QxMDc2ATtqtGJjpeY7EGMYcs\nQVh+SIgqKlUDMSzOjVjwiCr5redd1zJr0iOc/lT/CZJVg2Np8ryoFpzTVfO6rDBS\nuFQfWjiFoajx1xT5+bPa9bcLbvNglXLJ74lBNqN0DJsp1Wjo24aEz3TyYMjg4RkD\nQU3TKXKgKRbwJ+zmKqLcQRF29mqNdLRC1HLlgb+rXUDqFRaRk2QPtxMERaR0qcNY\nZlF6RDZrKrAHFDFrjhsbgpOdfzOSIZC1/SnRR0ynSiwKlFGR4GRxFUVYaV3adiHw\nHsuKVAUUCuv0RxtUQdGh/5z+0ZAfuN2MPfP/vDvuKrO2wnXnmbR9+Twc/HKuHusf\nGBhj351LQm1QFEniMoZVRDhGzFxN3nK8+baZeHrI3hQLRIuMgo0RbIQQo7QhhlUS\nuZiaOdDYLLVcsvFxOoadNmkehM0ANbhWFdMr3G9EnEbde/B//6hQ2I/xCuCb+GK3\nOvMYiWeGnByjo8MdYnHpJmGFNl/R2FDG4RO4365nqEVKzZ5fKYZdoKMVD3xTiMod\nFjPzFFA+vHDahTdO/aJpfuhEQTbLovdoy3OrDcSD6+eiimojET0=\n=g3MF\n-----END PGP SIGNATURE-----", "payload": "tree 330099000645b3aecc427464c63787b368cc054f\nparent df673fdcaf0d106ead56298f2332f6597b926d40\nparent ae4daba2b30bb641b4f9143a80ce0e81f690ce23\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773075307 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773075307 -0700\n\nMerge remote-tracking branch 'Tautulli-ghsa-g47q-8j8w-m63q/fix/home_stats_sql' into nightly\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7651095698ce85c099b56b97d6340ce53714e022", "html_url": "https://github.com/Tautulli/Tautulli/commit/7651095698ce85c099b56b97d6340ce53714e022", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7651095698ce85c099b56b97d6340ce53714e022/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "df673fdcaf0d106ead56298f2332f6597b926d40", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/df673fdcaf0d106ead56298f2332f6597b926d40", "html_url": "https://github.com/Tautulli/Tautulli/commit/df673fdcaf0d106ead56298f2332f6597b926d40"}, {"sha": "ae4daba2b30bb641b4f9143a80ce0e81f690ce23", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ae4daba2b30bb641b4f9143a80ce0e81f690ce23", "html_url": "https://github.com/Tautulli/Tautulli/commit/ae4daba2b30bb641b4f9143a80ce0e81f690ce23"}]}, {"sha": "fa0ef7fc616b036c5159cae16bde3d38815653ef", "node_id": "C_kwDOAducuNoAKGZhMGVmN2ZjNjE2YjAzNmM1MTU5Y2FlMTZiZGUzZDM4ODE1NjUzZWY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:55:30Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-09T16:55:30Z"}, "message": "Merge remote-tracking branch 'Tautulli-ghsa-xp55-2pf4-fv8m/fix/newsletter_image_path' into nightly", "tree": {"sha": "9ba7e9e78d6ca05a4709506a9a9a621b4d34dce8", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/9ba7e9e78d6ca05a4709506a9a9a621b4d34dce8"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/fa0ef7fc616b036c5159cae16bde3d38815653ef", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmmu+4QACgkQm/TvTexB\nFjom3Q//XSx7FI49MIrzjBqB2sVM+C8onTNu89PzjSDJPdyPxNJpEYhZwN2jyx8U\n+GIscB+y3/BNJ4QdDlspEkgfXRM3mKDNnSKLpSqWGDivxO359RoIJMEotKf5alIr\nG06L3QgGU9I0dTS2XG1dOCHDbjaksQeRFFFstXtDOU2t4twf+vi2qKgA7+5oPM99\nN711W6GliKSBq1XoyCTeNwKBmTGax5J3ZPWPh/QmslT9FHJI25BX5Y///urnJJam\nbyduGykaOVelhpE9TIiveIAiZ51c8WbtMc1Q7u3F3xuMuD/6ZcxF+GQNgcn7fES0\nnP/eiDknItsQlFLD2VYT7CsZ7WEvLdOTq8cjQCAYI8LzXOESpYWtIREBmkQWYUEa\nrQZYElgq24oHbnRhRq6NKqXePh99j8nB+ep9SkkRQ73CklZIYVTvka2CSg55bM80\n0MJnMpE9VXrl/UO8u5pH00DEhUOIXB++qxASWfS8NtTrG9yZ0u1hzdfHK5RfSzfU\nIyMmfyPTgSXcDJs3k0Ae+HEIqj+zUxa1ICWhY2zyf27rDaPh75nV9Z91u/Z2GK44\nE2zYwwATRkJ/BjAOvs+pWKm0msszD/NIJatZkpoi9KC67+RdJCgVWRDIGGdViRjk\n2DPPZi6vlrNfn0hjCGvP7Lmgl6x7h59Eyhfy3agCatMBpTemhVo=\n=7T1M\n-----END PGP SIGNATURE-----", "payload": "tree 9ba7e9e78d6ca05a4709506a9a9a621b4d34dce8\nparent 7651095698ce85c099b56b97d6340ce53714e022\nparent db7d9ed8503b0fde76101de786693ea52c5e9bdc\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773075330 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1773075330 -0700\n\nMerge remote-tracking branch 'Tautulli-ghsa-xp55-2pf4-fv8m/fix/newsletter_image_path' into nightly\n", "verified_at": "2026-03-09T16:58:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/fa0ef7fc616b036c5159cae16bde3d38815653ef", "html_url": "https://github.com/Tautulli/Tautulli/commit/fa0ef7fc616b036c5159cae16bde3d38815653ef", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/fa0ef7fc616b036c5159cae16bde3d38815653ef/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "7651095698ce85c099b56b97d6340ce53714e022", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7651095698ce85c099b56b97d6340ce53714e022", "html_url": "https://github.com/Tautulli/Tautulli/commit/7651095698ce85c099b56b97d6340ce53714e022"}, {"sha": "db7d9ed8503b0fde76101de786693ea52c5e9bdc", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/db7d9ed8503b0fde76101de786693ea52c5e9bdc", "html_url": "https://github.com/Tautulli/Tautulli/commit/db7d9ed8503b0fde76101de786693ea52c5e9bdc"}]}, {"sha": "4092035d855f50a90c385c9a080cfdb946cbe388", "node_id": "C_kwDOAducuNoAKDQwOTIwMzVkODU1ZjUwYTkwYzM4NWM5YTA4MGNmZGI5NDZjYmUzODg", "commit": {"author": {"name": "Anthony S", "email": "anthony@swierkosz.dev", "date": "2026-03-09T18:44:40Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-09T18:44:40Z"}, "message": "Update third party logos (#2646)\n\n* Update donation provider logos\n\n- Replace old PNG donation buttons with SVG logos.\n- Consolidate inline CSS styles into reusable base and modifier classes.\n\n* Update Google Play badge\n\n* Mark GitHub heart octicon as decorative\n\nCo-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>\n\n---------\n\nCo-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>", "tree": {"sha": "ed6b7ef0d2327d2488bd8285beaf3d284e773480", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/ed6b7ef0d2327d2488bd8285beaf3d284e773480"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/4092035d855f50a90c385c9a080cfdb946cbe388", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJprxUYCRC1aQ7uu5UhlAAAFUYQAGgY6xZ8bcGe3aZVBcRnj6O8\nO09k3jawXbxRBdu6tYGFJ2XU02peQFRJQrjl8wIDyg+gMDjjg3bSf52Sg36t1GMU\nleMo7PUbQ/SzBw0qBO8hD+8XKeVt5pCrGP9ImKb6H13zIlvBLjHj2p6KChuea1sd\nXU9Us96OqwgwjQLPeCCOnjdDYWpr+HdxDuVQ5ZIjro9KA11yYdXgMRRhTXB300fg\nYp6kJR7p/2M/tDuTP1fnX14+j+Epa04xKtZ+RYcKtE3eNKtqaxvjTvlkWWsBDTdM\niKA8z1v1Crx34TBjwvFui45Yx59hO497VN6I1u9P9vVqrc4ZdIlfsfux0bjJJ4qN\nrIoPzeth/C+6wNTB2s9RJA+v+jZ61KDeRhBkWNIn9hEpK1wm5rG2w9kDhLIsHt8P\nf/qVeu1fKl26wWvlkTLgingJxfXX81KW7+EDnSTOb7UGa7Rt8IcmcwNm20yvQhUl\ndjiHtLfaM3e1o3F5wWM5voICWbxzPOG+MhkfJKNh/jWQg5NuaaY0EpKvq+Ublkdh\ntcDLEvSuAOGCg8nnfhfxV+LSmZJSebOSHMSunNKGOOmNMptt+QenMb2wFDXUHJSw\nEY1pioPS+qPw767Fhn2XNtj8zoToEjEfrBkVx61SRTtZSHPcjU/NCRED5/Tdw6qU\nQ1ND461kkSO+0uKzDV2p\n=AAP2\n-----END PGP SIGNATURE-----\n", "payload": "tree ed6b7ef0d2327d2488bd8285beaf3d284e773480\nparent fa0ef7fc616b036c5159cae16bde3d38815653ef\nauthor Anthony S <anthony@swierkosz.dev> 1773081880 -0400\ncommitter GitHub <noreply@github.com> 1773081880 -0700\n\nUpdate third party logos (#2646)\n\n* Update donation provider logos\n\n- Replace old PNG donation buttons with SVG logos.\n- Consolidate inline CSS styles into reusable base and modifier classes.\n\n* Update Google Play badge\n\n* Mark GitHub heart octicon as decorative\n\nCo-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>\n\n---------\n\nCo-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>", "verified_at": "2026-03-09T18:44:41Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4092035d855f50a90c385c9a080cfdb946cbe388", "html_url": "https://github.com/Tautulli/Tautulli/commit/4092035d855f50a90c385c9a080cfdb946cbe388", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4092035d855f50a90c385c9a080cfdb946cbe388/comments", "author": {"login": "aisgbnok", "id": 10838153, "node_id": "MDQ6VXNlcjEwODM4MTUz", "avatar_url": "https://avatars.githubusercontent.com/u/10838153?v=4", "gravatar_id": "", "url": "https://api.github.com/users/aisgbnok", "html_url": "https://github.com/aisgbnok", "followers_url": "https://api.github.com/users/aisgbnok/followers", "following_url": "https://api.github.com/users/aisgbnok/following{/other_user}", "gists_url": "https://api.github.com/users/aisgbnok/gists{/gist_id}", "starred_url": "https://api.github.com/users/aisgbnok/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/aisgbnok/subscriptions", "organizations_url": "https://api.github.com/users/aisgbnok/orgs", "repos_url": "https://api.github.com/users/aisgbnok/repos", "events_url": "https://api.github.com/users/aisgbnok/events{/privacy}", "received_events_url": "https://api.github.com/users/aisgbnok/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "fa0ef7fc616b036c5159cae16bde3d38815653ef", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/fa0ef7fc616b036c5159cae16bde3d38815653ef", "html_url": "https://github.com/Tautulli/Tautulli/commit/fa0ef7fc616b036c5159cae16bde3d38815653ef"}]}, {"sha": "2546d1de175772bf03da50a0f808a46d8a0e688f", "node_id": "C_kwDOAducuNoAKDI1NDZkMWRlMTc1NzcyYmYwM2RhNTBhMGY4MDhhNDZkOGEwZTY4OGY", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:23:09Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:23:09Z"}, "message": "Bump actions/cache from 4 to 5 (#2628)\n\nBumps [actions/cache](https://github.com/actions/cache) from 4 to 5.\n- [Release notes](https://github.com/actions/cache/releases)\n- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)\n- [Commits](https://github.com/actions/cache/compare/v4...v5)\n\n---\nupdated-dependencies:\n- dependency-name: actions/cache\n  dependency-version: '5'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "93792628feeeb9355f36843f842f2f36d8429d32", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/93792628feeeb9355f36843f842f2f36d8429d32"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/2546d1de175772bf03da50a0f808a46d8a0e688f", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv4tCRC1aQ7uu5UhlAAAhSYQAEPz00YTiMC5PaVf9MTNLKep\nPAGDPNl4l7kQScj41TpjwFxlitJUFWPQz18ltfuFIlXuF1pWUG8P6vSzeCZK5uKp\n6RRN8gDKj6Cdh4VI7IZUBIacHU0ljwSgaQ0r7QvzxZlluLpH+xU0wwRLx5QsgNVa\nOmCa8esYfdAan/vPV8kRwmPNNazC7FdA4QusWnzyojUYgxv/Tkr6lJyvzjif68SX\no4Q/qgoaReFloEY3BoXHugc/wlJ5QOERPGMYfbDdnMph31fJFGuCqznKf7+hopiv\n0ennIXPP028X8jcOli+6Wc5KPAka4UsNnU6oGZpfoPhGKgGc7ObOA8l33Tsxw1K3\noLPpmLNy2YzYwQ3cRBhPZT6/0VrIj7xyFFcabAbkjd3MX/u146nKk/ksxJkLMQYq\nXd9WuDbdC+hAu3Va5s5a46Moyhx3NagVJpufktY6yPzn+e2cUZq3yb+iiR4a9IcV\n2a4q/VvKsRVegCKIfzVn66oZL4V+V50eRDEOsd7J68hWXmnEdYyKWvTyAbFICLzp\nTVuh5+ibvDAFNqmeevEYG2B3fPc/5zKjnfRdKc8hlu8LptvxNvu8pqtCDpCuMkiw\nyNIZ+KRnrBcnVIPkQH+gT/odLhb2REniiNvOb9U3OdqgqKk/gm8T8ZA4NUUHUXMe\nZ7Vzf5rIUbiRBPyfUqfb\n=O5SO\n-----END PGP SIGNATURE-----\n", "payload": "tree 93792628feeeb9355f36843f842f2f36d8429d32\nparent 4092035d855f50a90c385c9a080cfdb946cbe388\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124589 -0700\ncommitter GitHub <noreply@github.com> 1774124589 -0700\n\nBump actions/cache from 4 to 5 (#2628)\n\nBumps [actions/cache](https://github.com/actions/cache) from 4 to 5.\n- [Release notes](https://github.com/actions/cache/releases)\n- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)\n- [Commits](https://github.com/actions/cache/compare/v4...v5)\n\n---\nupdated-dependencies:\n- dependency-name: actions/cache\n  dependency-version: '5'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:23:11Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2546d1de175772bf03da50a0f808a46d8a0e688f", "html_url": "https://github.com/Tautulli/Tautulli/commit/2546d1de175772bf03da50a0f808a46d8a0e688f", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2546d1de175772bf03da50a0f808a46d8a0e688f/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "4092035d855f50a90c385c9a080cfdb946cbe388", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4092035d855f50a90c385c9a080cfdb946cbe388", "html_url": "https://github.com/Tautulli/Tautulli/commit/4092035d855f50a90c385c9a080cfdb946cbe388"}]}, {"sha": "28814840f1d1fc2d2a749c479057c707713cc280", "node_id": "C_kwDOAducuNoAKDI4ODE0ODQwZjFkMWZjMmQyYTc0OWM0NzkwNTdjNzA3NzEzY2MyODA", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:23:30Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:23:30Z"}, "message": "Bump dessant/label-actions from 4 to 5 (#2629)\n\nBumps [dessant/label-actions](https://github.com/dessant/label-actions) from 4 to 5.\n- [Release notes](https://github.com/dessant/label-actions/releases)\n- [Changelog](https://github.com/dessant/label-actions/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/dessant/label-actions/compare/v4...v5)\n\n---\nupdated-dependencies:\n- dependency-name: dessant/label-actions\n  dependency-version: '5'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "1f1a6962ea51e5478685b321b846f1a12fd4dfe4", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/1f1a6962ea51e5478685b321b846f1a12fd4dfe4"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/28814840f1d1fc2d2a749c479057c707713cc280", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv5CCRC1aQ7uu5UhlAAANBQQAEdW1GEmDj2YoQ4pnHd2UfOS\nVuRPmHGXh2vV3keB+Y7cF5c5RxS2Wk3t6x7cbwqcS/KIxbdHU29ekSkaJzABkrNK\nQwSTRN15/w4u00S9lyy+sFr1I3WLTfT8sLi/UhOwnhfO3hvoYxsk4bJkxniXmtkB\nnGoP64XWG2VDonHbeqZ+CUizzM+TFe8wmGlPwbtEeUbkqrk8SZlBmgIMSVjcpX02\n2TgXwrfTrz1q3ujtMffkgFjo+y3DzRiw1liQJXqtcH1ZuSmapAhQOJgBN/7HSf1Q\nVkdp+qESTmtWKREdkFqHj6aolRAnkECM1DAM1mBU+UNEU7UiteUMp256bbPYdEPz\nyDSZ2F/geUZcpofP6EFH7XDSq+jVQLtO7UqT8t+/q6hmQzHmQ2xcqxF6yYiOMUcb\nyfD2CCI/qkVOGNGjnb3hRVfYbGXJKG21R66MD3HsofjbN5yNDNAMAo7ebBw25vQH\nW8AHuENOL0VAZYCF6mczS2cxxdgcyaRjpx79uAXk+np/zFdk3ESLJi8OLx98LuPw\nOhFrsw900ZbeHo2Db+tD3cHiqRiQ/yGtpCa9MQjoFLipIVxRKfz3IRCZ/+6mnkF0\nuxeMTnBX74g7jwVpglgzIK7A1q2Yj1wFeo9eeoOrLpeZQG8Gwgi8oycCkZd2AT93\nB4q9rEuheUnO3MJnh0bW\n=ztqq\n-----END PGP SIGNATURE-----\n", "payload": "tree 1f1a6962ea51e5478685b321b846f1a12fd4dfe4\nparent 2546d1de175772bf03da50a0f808a46d8a0e688f\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124610 -0700\ncommitter GitHub <noreply@github.com> 1774124610 -0700\n\nBump dessant/label-actions from 4 to 5 (#2629)\n\nBumps [dessant/label-actions](https://github.com/dessant/label-actions) from 4 to 5.\n- [Release notes](https://github.com/dessant/label-actions/releases)\n- [Changelog](https://github.com/dessant/label-actions/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/dessant/label-actions/compare/v4...v5)\n\n---\nupdated-dependencies:\n- dependency-name: dessant/label-actions\n  dependency-version: '5'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:23:31Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/28814840f1d1fc2d2a749c479057c707713cc280", "html_url": "https://github.com/Tautulli/Tautulli/commit/28814840f1d1fc2d2a749c479057c707713cc280", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/28814840f1d1fc2d2a749c479057c707713cc280/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "2546d1de175772bf03da50a0f808a46d8a0e688f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2546d1de175772bf03da50a0f808a46d8a0e688f", "html_url": "https://github.com/Tautulli/Tautulli/commit/2546d1de175772bf03da50a0f808a46d8a0e688f"}]}, {"sha": "f9774db5386f5db25f2e6a7b332bebb80adfde2e", "node_id": "C_kwDOAducuNoAKGY5Nzc0ZGI1Mzg2ZjVkYjI1ZjJlNmE3YjMzMmJlYmI4MGFkZmRlMmU", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:23:35Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:23:35Z"}, "message": "Bump actions/upload-artifact from 5 to 7 (#2636)\n\nBumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 7.\n- [Release notes](https://github.com/actions/upload-artifact/releases)\n- [Commits](https://github.com/actions/upload-artifact/compare/v5...v7)\n\n---\nupdated-dependencies:\n- dependency-name: actions/upload-artifact\n  dependency-version: '7'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "b84760f628a678c5aa8c2869e4d1e16263215973", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/b84760f628a678c5aa8c2869e4d1e16263215973"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/f9774db5386f5db25f2e6a7b332bebb80adfde2e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv5HCRC1aQ7uu5UhlAAAWT4QAH+Vhy3QYy+NWLH+UTHCJYda\nwhOoWpkl3rbhh7Oa9c8PGM4Vbi6g1Vaaf/33hgE2uCZhp/30p4iphv8rz82OItYB\n4XlKrIbbHymDWR8kyMHZ4ueO305GTnqpk5J5WNedQeCfRI1Ylbf8yZ7zyat4vyHF\nOagXId42jTKiP3VGGR9oPyozQlhFDTIcq4YDUR7ukE/vo4z8PuA21bKzQSQwmqXu\nyIcv7If+Sacq/NJ2ynZj2Ksy2wd32IkdY38MtzV9b6YvPRI8XjzS4pHxlvxozY49\nH0jzFCR2qHB+PDw2oEoprW5BqD+AC9ugzE1+9v4SUneUt1zmUVl2bVmwfixWW9Hw\n/E+qGO6ABrxFw5O6rD/XaNYNVk+YjCDki1q9NNFg5G8WxZNZfIKNJEm7D44sE01p\npKlSpP2Nc+4+ulowa69CnILwa9JPQGLnhI4fOwHB67EpyBEfPk7W4totWH60C/+C\ntJ6zkRK26Ydth8Y9SvR4gmOUWXjRY9sTEf+cvlp1xXZh94EPpsZu2RaoZXBnm0Z8\nfguBL+dapyPW5ejmCyzUIGBQJJUonu5g3phjVrLEx2e4p9pRIxWaSsNBLJ567tAL\n0ZTElb1uNoyrAoMIdtg78Ch2oiiBebo5OYeIUMXl4OKLcTTXPqIQnUGThFuzWbeX\nZCS0njnX3o9mnUorQKGS\n=LyXl\n-----END PGP SIGNATURE-----\n", "payload": "tree b84760f628a678c5aa8c2869e4d1e16263215973\nparent 28814840f1d1fc2d2a749c479057c707713cc280\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124615 -0700\ncommitter GitHub <noreply@github.com> 1774124615 -0700\n\nBump actions/upload-artifact from 5 to 7 (#2636)\n\nBumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 7.\n- [Release notes](https://github.com/actions/upload-artifact/releases)\n- [Commits](https://github.com/actions/upload-artifact/compare/v5...v7)\n\n---\nupdated-dependencies:\n- dependency-name: actions/upload-artifact\n  dependency-version: '7'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:23:36Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f9774db5386f5db25f2e6a7b332bebb80adfde2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/f9774db5386f5db25f2e6a7b332bebb80adfde2e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f9774db5386f5db25f2e6a7b332bebb80adfde2e/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "28814840f1d1fc2d2a749c479057c707713cc280", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/28814840f1d1fc2d2a749c479057c707713cc280", "html_url": "https://github.com/Tautulli/Tautulli/commit/28814840f1d1fc2d2a749c479057c707713cc280"}]}, {"sha": "5df9890186b5a346fbad1ae0b9bd9739cdc4aed6", "node_id": "C_kwDOAducuNoAKDVkZjk4OTAxODZiNWEzNDZmYmFkMWFlMGI5YmQ5NzM5Y2RjNGFlZDY", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:23:43Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:23:43Z"}, "message": "Bump actions/download-artifact from 6 to 8 (#2637)\n\nBumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 8.\n- [Release notes](https://github.com/actions/download-artifact/releases)\n- [Commits](https://github.com/actions/download-artifact/compare/v6...v8)\n\n---\nupdated-dependencies:\n- dependency-name: actions/download-artifact\n  dependency-version: '8'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "8d956fcb410a2668f54cf865ebb199e622db22d5", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/8d956fcb410a2668f54cf865ebb199e622db22d5"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/5df9890186b5a346fbad1ae0b9bd9739cdc4aed6", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv5PCRC1aQ7uu5UhlAAALKwQAEObiOt5ZFLjiIJWXyKvEXZp\nDnn4ii7Igx3A2mLYjxyTzE99UTmaW9xcQ/EBzG2u4UlIeoo7LIZMS8Ui3EUQcbK3\nVsoOUk9zaw0rDdB+ZFqAsANDDXKveaYw20R0vQAtOZHw6UG0vGXuDjs6iMVaGjkB\nm8Vn5F07jS8ikVWRs0vsr4Y8oM/wo/b4nPKdEpqmZQqY//AHQbi5GIsKSsqjSRk2\nMvDVUZvxwMIyTG7FZ1n+FtPnYOpVL5ADbkbf8QxxAP6pueF2CgV7LZ8hTaDh+Ldx\nHoDV+sjiwoDFPbG87LcX/jlQr1ZcjFAE+1nAmdOgC3Jt8lv8a7Fo/aRDqRvZLt4s\n9dK5qOWmL59oFCX79nFlX2EJaL/S2teVS1No9k5ym5LukPNudAPXeI1auFdL7ZYB\nYOqlkDc2EkanRdKd+pT/bAExby3bR7DOKAzI3zmPUkGKqmE004ll6wBm+WjfFM3Y\nWaZMyWfPb7klPC2MhEuKA+UOzSkKCd9TZiOGEmNCfHYbanEbAi7ELVk18MQbnUl5\nj8tZJRE7IqBDquBKuZQ7StOmo/5CJ4idV5PHO83TZyyVbM6DXCxPX7G34wasBFM4\nX5+/18xd1mwxOIt72NdDgvTQRLID5KaG7T0e52vb+bkCn0FH5lO+8I6mmwJz8Ijz\nOos78XIxEwGw9JQOt943\n=tF2o\n-----END PGP SIGNATURE-----\n", "payload": "tree 8d956fcb410a2668f54cf865ebb199e622db22d5\nparent f9774db5386f5db25f2e6a7b332bebb80adfde2e\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124623 -0700\ncommitter GitHub <noreply@github.com> 1774124623 -0700\n\nBump actions/download-artifact from 6 to 8 (#2637)\n\nBumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 8.\n- [Release notes](https://github.com/actions/download-artifact/releases)\n- [Commits](https://github.com/actions/download-artifact/compare/v6...v8)\n\n---\nupdated-dependencies:\n- dependency-name: actions/download-artifact\n  dependency-version: '8'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:23:44Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5df9890186b5a346fbad1ae0b9bd9739cdc4aed6", "html_url": "https://github.com/Tautulli/Tautulli/commit/5df9890186b5a346fbad1ae0b9bd9739cdc4aed6", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5df9890186b5a346fbad1ae0b9bd9739cdc4aed6/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "f9774db5386f5db25f2e6a7b332bebb80adfde2e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f9774db5386f5db25f2e6a7b332bebb80adfde2e", "html_url": "https://github.com/Tautulli/Tautulli/commit/f9774db5386f5db25f2e6a7b332bebb80adfde2e"}]}, {"sha": "c2c860b2fe1895c6039b487e082d03817e0ff79e", "node_id": "C_kwDOAducuNoAKGMyYzg2MGIyZmUxODk1YzYwMzliNDg3ZTA4MmQwMzgxN2UwZmY3OWU", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:23:48Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:23:48Z"}, "message": "Bump crazy-max/ghaction-virustotal from 4 to 5 (#2638)\n\nBumps [crazy-max/ghaction-virustotal](https://github.com/crazy-max/ghaction-virustotal) from 4 to 5.\n- [Release notes](https://github.com/crazy-max/ghaction-virustotal/releases)\n- [Commits](https://github.com/crazy-max/ghaction-virustotal/compare/v4...v5)\n\n---\nupdated-dependencies:\n- dependency-name: crazy-max/ghaction-virustotal\n  dependency-version: '5'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "8858934c73769794423962a80fdd13742ab53887", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/8858934c73769794423962a80fdd13742ab53887"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/c2c860b2fe1895c6039b487e082d03817e0ff79e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv5UCRC1aQ7uu5UhlAAAEcYQAHp4nxnGPzap+rVAIc4GDEgp\nRFC2HWf7BReyEbxTz9J/M1XL/nMI0FgdYQPBp7BQeSPyytpdcWOiwRqv4a3BeknI\nZktjAzXCnYfELs4Z7tCrl3H+o2YcJ9aDGe3Lf3GSxZo5MTo62G/ctVmbMcUGHJW6\nJQiyxYCIPD8EBWhYaEDCvXq+tqE9VeYRwA/HDWVX1fK60Ueb063tuo4NipqwCeGp\nakVXSHOw0XVRos7UZGXiCXhCiRemblL2fEMoDRbJEDTyzAAAWvKHYkGfUlCDUV9U\nB+ND4fmFuv6Dlbh8jJ/qp8h9bhejdWZvjBJl0Wsl0YsX2S6m+Dp6y+39QlPoqUm8\n3/PC/8VMHeZNU3Z4sukDu6ShLG/MWYEXEg+J9iyYsZKCJenEX3aPqGcHYvt2I9l1\nlygrDmc87ONyRPtyjTbTrZNOaR/HOiUzig+pSQapiNGZx+t6ZzP+ufTK5hUBwSrE\n2e9ge5JE1tROtcF/3BYxjCnf0MopDVgss4K7u+47CSUu/jJiT3Et7CzxadwXdirQ\nbRMAlnEqYQA5UT0FWFjYf30IiMR4iX4+JNXAuVMuFel04NvF1XrjxvPQeZh0IDgv\nT0ypqCC/0L+EODMF5T8LleC5rymN8vAI2IVvNTvtocl4ZndIQxWTqHlkC+QnnWJU\nI15ZHkzcsPz5xPMR9la8\n=5Y4N\n-----END PGP SIGNATURE-----\n", "payload": "tree 8858934c73769794423962a80fdd13742ab53887\nparent 5df9890186b5a346fbad1ae0b9bd9739cdc4aed6\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124628 -0700\ncommitter GitHub <noreply@github.com> 1774124628 -0700\n\nBump crazy-max/ghaction-virustotal from 4 to 5 (#2638)\n\nBumps [crazy-max/ghaction-virustotal](https://github.com/crazy-max/ghaction-virustotal) from 4 to 5.\n- [Release notes](https://github.com/crazy-max/ghaction-virustotal/releases)\n- [Commits](https://github.com/crazy-max/ghaction-virustotal/compare/v4...v5)\n\n---\nupdated-dependencies:\n- dependency-name: crazy-max/ghaction-virustotal\n  dependency-version: '5'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:23:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c2c860b2fe1895c6039b487e082d03817e0ff79e", "html_url": "https://github.com/Tautulli/Tautulli/commit/c2c860b2fe1895c6039b487e082d03817e0ff79e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c2c860b2fe1895c6039b487e082d03817e0ff79e/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "5df9890186b5a346fbad1ae0b9bd9739cdc4aed6", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5df9890186b5a346fbad1ae0b9bd9739cdc4aed6", "html_url": "https://github.com/Tautulli/Tautulli/commit/5df9890186b5a346fbad1ae0b9bd9739cdc4aed6"}]}, {"sha": "ef82e8c2aee9ab2d586de59cfaf445e080e1fd69", "node_id": "C_kwDOAducuNoAKGVmODJlOGMyYWVlOWFiMmQ1ODZkZTU5Y2ZhZjQ0NWUwODBlMWZkNjk", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:23:56Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:23:56Z"}, "message": "Bump docker/login-action from 3 to 4 (#2641)\n\nBumps [docker/login-action](https://github.com/docker/login-action) from 3 to 4.\n- [Release notes](https://github.com/docker/login-action/releases)\n- [Commits](https://github.com/docker/login-action/compare/v3...v4)\n\n---\nupdated-dependencies:\n- dependency-name: docker/login-action\n  dependency-version: '4'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "eda71801aaeb3886601a16c59762aed565130ec5", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/eda71801aaeb3886601a16c59762aed565130ec5"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/ef82e8c2aee9ab2d586de59cfaf445e080e1fd69", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv5cCRC1aQ7uu5UhlAAAvA8QAGQVLxvV3nQIf+Fl0kxSHoEb\nndAcHpYfsKEKOgGy0IQP81p5NI6FFpxWf5igf52XKKmSGwSL052YHKytQilWHdyJ\ncpgiFMTN0o4WoVpIAAsGPd/pWahsPGOSwjqlUAuHAWX+2kuwehMF9To2zhyycRXI\nJ1NEumPdgDM0S6g4U1QlftG15yeVTHODXnOQwqp6YNn0zJ16m6HFD7K35ZMRu0xd\neJc+kEfzrDDc8zmqJ4v6XBTATwyUobSo8H1dh3zwcc69Wg3GyUwK4hAfAC0PVBQc\nqBtAZjkN4UaS6l9daZS3nFIXEBFEoUOYR5KA9yFtkbpiX3nC+vDmM2GXNxirFuai\n106wtuAxm+YsseOyCeW18jEhuATpdKogUcCY339Jax/PUuDnNelx5Bz3xp0rnNq8\nXba+wh5n8QzEQkSlH5VXRA0qmUFfZMKkJQdZQqTZk9puh3ZkIK3aVY3KOX19Wc+X\nxrlA2zqSQQsBTpP74xmcgG9blbOKKecoIFeK6ZSyG6HRayb2EEaAEJml1OUMAtdB\nA369hJi+MKLiN2r/a7ZWyQOaX4JgNetbhyXGsGK+d4yWQ7dplAmDejwcPxpy8hi4\nc+uymLcg4o/7c0iEMShl5UcHo/sLn87Cf2/Y3VVeYh5Jk1+sPq3vUFVM1gO78vjL\nlguAe6lbna1HewzGjZdu\n=kYqJ\n-----END PGP SIGNATURE-----\n", "payload": "tree eda71801aaeb3886601a16c59762aed565130ec5\nparent c2c860b2fe1895c6039b487e082d03817e0ff79e\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124636 -0700\ncommitter GitHub <noreply@github.com> 1774124636 -0700\n\nBump docker/login-action from 3 to 4 (#2641)\n\nBumps [docker/login-action](https://github.com/docker/login-action) from 3 to 4.\n- [Release notes](https://github.com/docker/login-action/releases)\n- [Commits](https://github.com/docker/login-action/compare/v3...v4)\n\n---\nupdated-dependencies:\n- dependency-name: docker/login-action\n  dependency-version: '4'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:23:56Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ef82e8c2aee9ab2d586de59cfaf445e080e1fd69", "html_url": "https://github.com/Tautulli/Tautulli/commit/ef82e8c2aee9ab2d586de59cfaf445e080e1fd69", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ef82e8c2aee9ab2d586de59cfaf445e080e1fd69/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "c2c860b2fe1895c6039b487e082d03817e0ff79e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c2c860b2fe1895c6039b487e082d03817e0ff79e", "html_url": "https://github.com/Tautulli/Tautulli/commit/c2c860b2fe1895c6039b487e082d03817e0ff79e"}]}, {"sha": "9916a8fe306f38443dfec25fe8c42322af5d98c2", "node_id": "C_kwDOAducuNoAKDk5MTZhOGZlMzA2ZjM4NDQzZGZlYzI1ZmU4YzQyMzIyYWY1ZDk4YzI", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:24:02Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:24:02Z"}, "message": "Bump docker/setup-qemu-action from 3 to 4 (#2642)\n\nBumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4.\n- [Release notes](https://github.com/docker/setup-qemu-action/releases)\n- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)\n\n---\nupdated-dependencies:\n- dependency-name: docker/setup-qemu-action\n  dependency-version: '4'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "480164702ed85513a26fd1e16dac087c0ed33b21", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/480164702ed85513a26fd1e16dac087c0ed33b21"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/9916a8fe306f38443dfec25fe8c42322af5d98c2", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv5iCRC1aQ7uu5UhlAAAb/YQAADDBzWRDYenYNB6pcPC1445\nccggo0VcCR+q+DN/frl81bfJ65ELu+TUO426Z9k5l9+h26OUdmRxx+LOyKMgB/qh\n6dIxmz3KDRovHS7VhqxzgciN7p26sFCrtbdXQhJtSIV4ZwggFlDTqQ9zlqqmol0Y\nJHEKyDK1kb0NcFixiqMSFVL3bMglu0CZ0zZwZrS5juox6V9CJtAIbuX/Ucqf8xMX\n38Dt4t8oDET1jeNHBOy+ffwoFTnEmwBphuWiUogFWvrOjLUW1u/FgyQRuLlIEXFJ\n4xpxADYj0NlIsHNpDVX7Eat4c5DebN5VS0WnvrTyFlyMVZfhkrviO/Kn7PTOw1yl\nZ27wXVRA4zwz2AjbnfFU3zCQxnE1eE5r6StNrwggAbNurAQTDc6p4ebgOsx/6coi\nX75dBRkH+JjEQsSz4mfuhGVo6pMU9kNXkvPUMuhzq3KT5VX7wanXb/ueefxJObcl\nHrIwoJ10IeXbEKsGwznuMrEmwAvx+xW9ZFlIzWxmvz20oDfs86tPLrYluZkvVlDU\nHp3xP2NQmjsT7e28DZzKcLn6cxsw7f6WIBXtlxtkeW+zkpeEY/ves69NvkaKxgVd\nZDM4E9OyVvZF2gpoFXoSHlnJbyWaWgj6BQGNhpQV46RUswDBwrdMMtQcrAdZc5Oy\nOLnLlHPIbcJG7nGeVlsI\n=VcvD\n-----END PGP SIGNATURE-----\n", "payload": "tree 480164702ed85513a26fd1e16dac087c0ed33b21\nparent ef82e8c2aee9ab2d586de59cfaf445e080e1fd69\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124642 -0700\ncommitter GitHub <noreply@github.com> 1774124642 -0700\n\nBump docker/setup-qemu-action from 3 to 4 (#2642)\n\nBumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4.\n- [Release notes](https://github.com/docker/setup-qemu-action/releases)\n- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)\n\n---\nupdated-dependencies:\n- dependency-name: docker/setup-qemu-action\n  dependency-version: '4'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:24:03Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9916a8fe306f38443dfec25fe8c42322af5d98c2", "html_url": "https://github.com/Tautulli/Tautulli/commit/9916a8fe306f38443dfec25fe8c42322af5d98c2", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9916a8fe306f38443dfec25fe8c42322af5d98c2/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "ef82e8c2aee9ab2d586de59cfaf445e080e1fd69", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ef82e8c2aee9ab2d586de59cfaf445e080e1fd69", "html_url": "https://github.com/Tautulli/Tautulli/commit/ef82e8c2aee9ab2d586de59cfaf445e080e1fd69"}]}, {"sha": "688f710e3ea02bca0f88b363de1e7cf80f1fa9ef", "node_id": "C_kwDOAducuNoAKDY4OGY3MTBlM2VhMDJiY2EwZjg4YjM2M2RlMWU3Y2Y4MGYxZmE5ZWY", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:24:08Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:24:08Z"}, "message": "Bump docker/setup-buildx-action from 3 to 4 (#2643)\n\nBumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.\n- [Release notes](https://github.com/docker/setup-buildx-action/releases)\n- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)\n\n---\nupdated-dependencies:\n- dependency-name: docker/setup-buildx-action\n  dependency-version: '4'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "2091f9b3fc9c0652dcdc60fcadf0cebefbffd5dc", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/2091f9b3fc9c0652dcdc60fcadf0cebefbffd5dc"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/688f710e3ea02bca0f88b363de1e7cf80f1fa9ef", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv5pCRC1aQ7uu5UhlAAAUCMQAHS0Ek7ZqRdkm4iMNanc3fIr\nk08MrNANWHNDGkhhlUxz3KFs/ox4ZcImt2+/fzifhCKlt+lk1JGO8Qr6DPpQ363X\nVZq3hNulbUlpjmcQzJL8Yf5WTzPQQxjP7ewWx6jQhdy/FciXMpURZu/75HB4efXA\nL63+1rnlOSUAbsZEOFOoxKVK5SHMK4Tjb8+fDh2GYLdTPPc2KdTQC2B4AH4DLDRd\nlGNZ6C5eZHSlLDY4KBsuSdNbWJwQEQ2BZKTw34ZD9uiiYTdXt4iYT+g9tT1P8ZzD\nll5rCg3YPqezSy0Zz6YlfE6PQ9q83i7QM0PMGvLPuyJEQTz7S+uvrrfGvEqG/stb\n/YiraKf5DUZezbzuRZMHaYniP9W4MPyPrKkLPTHWrCSYby5bOC6GpwqS+DcAoAxu\nRw8tGH8AvhaBJmDi0ku3hTo+vObJrwmLbsHvMtOQvEVP1M034UdIerdfsUHylNaE\nQzUURpNgDzxZFUtfK4kKZ77aqNOWblJq3ZtQ0Zdyc+nKuZ9mQs3Zu6AsOP+GnQ3B\nweqPCDK2QbRSwoMaFx0z6R2t3s/Fdj6eO/EJEh0ZZBDYO8a1L5/hh0YCaPw5EGiX\nomaA+Yp6sJzJyu7gCLJhEwXzxEqVn1snQLEDwMQADCNt7EjKFY3ub6xXQfTXZs9r\nq0+5B3uc7FkcC8wrGGOM\n=jmOh\n-----END PGP SIGNATURE-----\n", "payload": "tree 2091f9b3fc9c0652dcdc60fcadf0cebefbffd5dc\nparent 9916a8fe306f38443dfec25fe8c42322af5d98c2\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124648 -0700\ncommitter GitHub <noreply@github.com> 1774124648 -0700\n\nBump docker/setup-buildx-action from 3 to 4 (#2643)\n\nBumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.\n- [Release notes](https://github.com/docker/setup-buildx-action/releases)\n- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)\n\n---\nupdated-dependencies:\n- dependency-name: docker/setup-buildx-action\n  dependency-version: '4'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:24:09Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/688f710e3ea02bca0f88b363de1e7cf80f1fa9ef", "html_url": "https://github.com/Tautulli/Tautulli/commit/688f710e3ea02bca0f88b363de1e7cf80f1fa9ef", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/688f710e3ea02bca0f88b363de1e7cf80f1fa9ef/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "9916a8fe306f38443dfec25fe8c42322af5d98c2", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9916a8fe306f38443dfec25fe8c42322af5d98c2", "html_url": "https://github.com/Tautulli/Tautulli/commit/9916a8fe306f38443dfec25fe8c42322af5d98c2"}]}, {"sha": "4af6f828619086eb690e500984766750ade72a18", "node_id": "C_kwDOAducuNoAKDRhZjZmODI4NjE5MDg2ZWI2OTBlNTAwOTg0NzY2NzUwYWRlNzJhMTg", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:24:17Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:24:17Z"}, "message": "Bump docker/build-push-action from 6 to 7 (#2644)\n\nBumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6 to 7.\n- [Release notes](https://github.com/docker/build-push-action/releases)\n- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)\n\n---\nupdated-dependencies:\n- dependency-name: docker/build-push-action\n  dependency-version: '7'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "63f8777d7807c1fd9f9e583fbc83101952aa6cc6", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/63f8777d7807c1fd9f9e583fbc83101952aa6cc6"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/4af6f828619086eb690e500984766750ade72a18", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv5xCRC1aQ7uu5UhlAAAHpIQADBrY8ahYfvu9rbwe5gT5Qo7\njf+8w0SHWgY/z1sg4x3kO/9hYquqxVcgkKuKrLfGB0+RwRFqRbTFHQYUvuzMY16c\ni8pZgvI6EflA/b7FjJh9FTx3mWi78Zk2uFsgikdCgVqrlWgWyi79AU1cq8XlkIRy\nPBPiHx3NADf2UHFzwWzsUhC8cMEt+l+8GR7+zhwbZrA7DOLc/0MXklTLd9Agewws\nUih43tgRB80d09twkrEGwyYvDkV6gA5wheITaz6HnDmdXEYqv+9RRXhN8Z07G69Y\nzJ77wvXL/gcP+0zNgbPLaBeXRu3S/aZRcbBtmN0P+oMhjkug7cKlIA+krWuDqRaW\nCCqLJG8iX6lxnLsVLGeFdh6wCM02Wpi9elPc7zs97amsSo/jmav1YH8OFwF1wxRs\nfP/7h2PzCJsTfm5xZ2Ze3rHvXuSiGRfEP08GmX4MuXVYetaR/5wW2e022fyYNq3A\nhUj0ZOTkidC9NuyyZNfhADNmfHb2AYXxYD+NUVwC77ZEs0cqJSrnTUOJKK7+jHWa\nRStMkVZhfyZIsG1PT9xiyZTc0qn5EoWKEDmxsIWoEIkAVcN3vVUkGe6HxLcrQbbu\nFZLqA6KYM5jYxDctU9lsCnUOzWk0q3plHTGypycLMv77pg0H1KiA/ZPWmya30xqz\nA8rqi8QXiCtlUJycbBlx\n=YP5A\n-----END PGP SIGNATURE-----\n", "payload": "tree 63f8777d7807c1fd9f9e583fbc83101952aa6cc6\nparent 688f710e3ea02bca0f88b363de1e7cf80f1fa9ef\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124657 -0700\ncommitter GitHub <noreply@github.com> 1774124657 -0700\n\nBump docker/build-push-action from 6 to 7 (#2644)\n\nBumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6 to 7.\n- [Release notes](https://github.com/docker/build-push-action/releases)\n- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)\n\n---\nupdated-dependencies:\n- dependency-name: docker/build-push-action\n  dependency-version: '7'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:24:19Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4af6f828619086eb690e500984766750ade72a18", "html_url": "https://github.com/Tautulli/Tautulli/commit/4af6f828619086eb690e500984766750ade72a18", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4af6f828619086eb690e500984766750ade72a18/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "688f710e3ea02bca0f88b363de1e7cf80f1fa9ef", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/688f710e3ea02bca0f88b363de1e7cf80f1fa9ef", "html_url": "https://github.com/Tautulli/Tautulli/commit/688f710e3ea02bca0f88b363de1e7cf80f1fa9ef"}]}, {"sha": "0c106c63bdb30f6c1f87e33951aa4893e220baf6", "node_id": "C_kwDOAducuNoAKDBjMTA2YzYzYmRiMzBmNmMxZjg3ZTMzOTUxYWE0ODkzZTIyMGJhZjY", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:24:25Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:24:25Z"}, "message": "Bump docker/metadata-action from 5 to 6 (#2645)\n\nBumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5 to 6.\n- [Release notes](https://github.com/docker/metadata-action/releases)\n- [Commits](https://github.com/docker/metadata-action/compare/v5...v6)\n\n---\nupdated-dependencies:\n- dependency-name: docker/metadata-action\n  dependency-version: '6'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "1b4b791bb399518e95631a1c789dc339df64aa4e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/1b4b791bb399518e95631a1c789dc339df64aa4e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/0c106c63bdb30f6c1f87e33951aa4893e220baf6", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv55CRC1aQ7uu5UhlAAAJ5AQAI6MC/mV9Hq/MeHr0wmgElgz\nGOQBAfsCggFuCxemp5R2wYu0ZWTgCeO+2FRAl2dxVBedVvg1eQjHqbNipf1lpgP6\npBoDz626uPxD3vunMkhjFJ4mdun5tgdZN43ctWSIxHCJEN6L07P7cgmu6PCqqUEu\ngVNmHP8PWPOSxeWoqBtTZjIo8w28x1FrmAyxyd5V30YpR7Y7ENdvJOIGxfY9QNEn\n9Nv2Ju5YQMFhbAyxlQyUTTpLLS6tyJEkB+P0zO5H1ew9GYeSkdKTYZV0V9DsDpzs\nQDyC96CNWMjYOlL5Jy55PvHJ66HVcAW26xcecm8hy5sGp7ebXJre0fvFo2DYwcky\noJ6e5nG0/5LDt3u+tY6tRK4N8trDmXdxyHVUsh7wQhECXdgfYwNtQYv6s4GA75rk\nN3f8EtJpu1xCYLEQ3zsIxdpx/byJG38H2xiuux9s8kOLcgZIXtS0FeqSnVM7En6x\nabo8IVKaDKHmFtLCLINJyzFBHMXQQY3f6CL7LxamidT2uAPNHF8c2cqO9yTOg6RN\nCar6APE5BNw6R6N+QgYggGiOtjfDYixMHk3oqFbar8h81BuoZr5nHAhUrzOh37sT\nIRz4T/9QaFyg3AWDoh5KPBzSG1B8MxqtBLB3zReQMvesmNNr6/6IBr7vygiTu0vq\nDHZJ6lRGzb+yMk/2dchK\n=Co3D\n-----END PGP SIGNATURE-----\n", "payload": "tree 1b4b791bb399518e95631a1c789dc339df64aa4e\nparent 4af6f828619086eb690e500984766750ade72a18\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124665 -0700\ncommitter GitHub <noreply@github.com> 1774124665 -0700\n\nBump docker/metadata-action from 5 to 6 (#2645)\n\nBumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5 to 6.\n- [Release notes](https://github.com/docker/metadata-action/releases)\n- [Commits](https://github.com/docker/metadata-action/compare/v5...v6)\n\n---\nupdated-dependencies:\n- dependency-name: docker/metadata-action\n  dependency-version: '6'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:24:26Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0c106c63bdb30f6c1f87e33951aa4893e220baf6", "html_url": "https://github.com/Tautulli/Tautulli/commit/0c106c63bdb30f6c1f87e33951aa4893e220baf6", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0c106c63bdb30f6c1f87e33951aa4893e220baf6/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "4af6f828619086eb690e500984766750ade72a18", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4af6f828619086eb690e500984766750ade72a18", "html_url": "https://github.com/Tautulli/Tautulli/commit/4af6f828619086eb690e500984766750ade72a18"}]}, {"sha": "2f31d79a30b5e7b815dc94186ca3a7cafb8ea8b7", "node_id": "C_kwDOAducuNoAKDJmMzFkNzlhMzBiNWU3YjgxNWRjOTQxODZjYTNhN2NhZmI4ZWE4Yjc", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T20:24:32Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T20:24:32Z"}, "message": "Bump mshick/add-pr-comment from 2 to 3 (#2649)\n\nBumps [mshick/add-pr-comment](https://github.com/mshick/add-pr-comment) from 2 to 3.\n- [Release notes](https://github.com/mshick/add-pr-comment/releases)\n- [Changelog](https://github.com/mshick/add-pr-comment/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/mshick/add-pr-comment/compare/v2...v3)\n\n---\nupdated-dependencies:\n- dependency-name: mshick/add-pr-comment\n  dependency-version: '3'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "04d7671b03304e0f320e326f88358be537874d47", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/04d7671b03304e0f320e326f88358be537874d47"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/2f31d79a30b5e7b815dc94186ca3a7cafb8ea8b7", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvv6ACRC1aQ7uu5UhlAAAdUQQAAIp6S4tZRm+sgk8ytlm/nnA\nF3OytqWvNY4CP3sF5C/5OeGXqjIYRkGAjWSXsTAYyiMDeM8e9kHk6wnhg3Or4ad5\nczehhk2LA2fz7cGizgRIMT0PmGDnNjrRktltnV98eFAvQX2RTOhvjnzXIz8QtsJu\nNYMxJ5x5JhqaQTfCrauctcz0wWDgAmh9iX3VUU/Ww9AuXIw5un4qJEeRrlgUHriB\nU/gObdlIlBDf8g0QzQMhtlUnCssepoFp2RHfskoxuE2ro9Qxy9z5E3GWByFwPz+i\nCYq9otjmjWnV9kLDipt+COgoc3DbFjJdYTGg0EIxgvxba6B0ObzC22PhcVykMBwq\nCfcfOD4qcXn1S2XfnJgCHvNWwf0j++ejTurJ7oUiLTB1Xs25X292gEbtqq7shvTD\nQwzdouh6mIWNtU8jnGGYWXclJver4dFhnTvrGLpL0EzT2m2/JrBOUlHwQYMnWPid\nFbmYEIXGorNciQm7+Uwx0yPKyNEOSs93qai0E1oCWrkU7CcQKLpgJXzsQH9Snw7B\ncS8IMKaxBI/yZYfXCnvLvcgmx7ZrIw2DIuwSeIY6yj3rabTEcxvzCu0YXwQo0NSG\ncP5NVxSAL/+ffe3dlbLAhubr8Tp84l1IO4HZ0zh3JwxQFKaRuKnU6zQUmlt/thH0\nDnx09xXrpLRb9h0+5IiR\n=r8k0\n-----END PGP SIGNATURE-----\n", "payload": "tree 04d7671b03304e0f320e326f88358be537874d47\nparent 0c106c63bdb30f6c1f87e33951aa4893e220baf6\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774124672 -0700\ncommitter GitHub <noreply@github.com> 1774124672 -0700\n\nBump mshick/add-pr-comment from 2 to 3 (#2649)\n\nBumps [mshick/add-pr-comment](https://github.com/mshick/add-pr-comment) from 2 to 3.\n- [Release notes](https://github.com/mshick/add-pr-comment/releases)\n- [Changelog](https://github.com/mshick/add-pr-comment/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/mshick/add-pr-comment/compare/v2...v3)\n\n---\nupdated-dependencies:\n- dependency-name: mshick/add-pr-comment\n  dependency-version: '3'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T20:24:33Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2f31d79a30b5e7b815dc94186ca3a7cafb8ea8b7", "html_url": "https://github.com/Tautulli/Tautulli/commit/2f31d79a30b5e7b815dc94186ca3a7cafb8ea8b7", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2f31d79a30b5e7b815dc94186ca3a7cafb8ea8b7/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "0c106c63bdb30f6c1f87e33951aa4893e220baf6", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0c106c63bdb30f6c1f87e33951aa4893e220baf6", "html_url": "https://github.com/Tautulli/Tautulli/commit/0c106c63bdb30f6c1f87e33951aa4893e220baf6"}]}, {"sha": "c92eeefb5bcf891b22d84376e22d61bbd20f7318", "node_id": "C_kwDOAducuNoAKGM5MmVlZWZiNWJjZjg5MWIyMmQ4NDM3NmUyMmQ2MWJiZDIwZjczMTg", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T20:50:40Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T20:51:59Z"}, "message": "Update Docker CI workflow to use GitHub cache", "tree": {"sha": "0b90869573804f51c1d47053e8647b527738ef1d", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/0b90869573804f51c1d47053e8647b527738ef1d"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/c92eeefb5bcf891b22d84376e22d61bbd20f7318", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/BO8ACgkQm/TvTexB\nFjrJvBAAqIVUYt8Pl8FnUfzAbye/NJv8uWV83n8iYmJBbwpydLRiRoNw/rRRLjfJ\n7KzaG/nZgg6ieEPff3y3yh2XHvHvKSqJ+N+OWme65z4U0DGR+YyLD91a14Kf1N9K\nozktjal3IvOR/YdMCN6awGUamkhIwpVjVUuclkY7aSFUdEe0Wcb30kIBB8j9vVS0\ngxo1/LRTmFJ9gIKVTmOHHW9b1C+5c9V3ntE3C6FbWzieF8rVdMO0hqz/XFm1lT/+\nqyitf08/f5jkSTM1xAQXVpAhe7dDZnHzUA7+IBy/yiDRPOajXskcWw8gnirAEO3X\nb4J+8Zj/e9CQGW3XWmRJn53MwGBmpxdD6EQ4vu1ikBiaDUJuiedUVJVAv3aseQqR\nqtuN3dJBBgW5zVMuuS4QSEmunqwOMH8jnUxyWqECnDZ1NCxFskU67f8l3hItFLSC\nLDj055hfFu4IhgJ3zdgvt10QFJ5rOdGnTSVj304470n98jgZls3DTyEFJO0+1msr\n58496PYSyaQB/fFZ/XkvdQ+qhcqtXwctVrErZ2iQ+LQNDsx87+Sp9guZK7bZer7Y\nljyCg34FEMRrx0J9deTdZxNHgk1qQNFRG94Mte0YOJrpf3YRwZWrndxhm2b1UpyF\nNxQIiLkI1YpvuVtCVJlmY4XKdA08IyKGa/BFN0LEBQeZEvrpIto=\n=Q6th\n-----END PGP SIGNATURE-----", "payload": "tree 0b90869573804f51c1d47053e8647b527738ef1d\nparent 2f31d79a30b5e7b815dc94186ca3a7cafb8ea8b7\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774126240 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774126319 -0700\n\nUpdate Docker CI workflow to use GitHub cache\n", "verified_at": "2026-03-21T20:52:14Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c92eeefb5bcf891b22d84376e22d61bbd20f7318", "html_url": "https://github.com/Tautulli/Tautulli/commit/c92eeefb5bcf891b22d84376e22d61bbd20f7318", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c92eeefb5bcf891b22d84376e22d61bbd20f7318/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "2f31d79a30b5e7b815dc94186ca3a7cafb8ea8b7", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2f31d79a30b5e7b815dc94186ca3a7cafb8ea8b7", "html_url": "https://github.com/Tautulli/Tautulli/commit/2f31d79a30b5e7b815dc94186ca3a7cafb8ea8b7"}]}, {"sha": "31ce763b6a45e5dc82992cfc3a8183c94b34ab61", "node_id": "C_kwDOAducuNoAKDMxY2U3NjNiNmE0NWU1ZGM4Mjk5MmNmYzNhODE4M2M5NGIzNGFiNjE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:12:22Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:18:28Z"}, "message": "Bump apscheduler==3.11.2", "tree": {"sha": "392e2971d5a599d851b493df1de734554fae6b54", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/392e2971d5a599d851b493df1de734554fae6b54"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/31ce763b6a45e5dc82992cfc3a8183c94b34ab61", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/CyQACgkQm/TvTexB\nFjoMhQ//Tf7RBkDIsoGjn8ahzXipXlGpDn9AjMwnkgejeggPMxHmPT1rUH1JO/6Y\nOiJaM5cLhsGAjutUo+thBE6PotUBQsWlkcnLoUuRjaDM1eohQSCNJa28KBkUjkkn\nbGusitv2z20ef/tz8ih/y7d8IoS82UdOUQiOegl6cdoKgL9ju8cbIhkljlg4/U9y\nGlEnPcPlnG2plRVL5xZq3ETCJd+DVafSO6og/J3WcwYNxoBhnC6xcMICjV3ha2GO\nRSHcR7GolsITUNLnVIr70I0w+35MWiLAqCM/A5ZlpjUGjxxnjoZu7By2hiODeouz\nVuGlvmyCXrIVwCqcPufsnHzA+qbci6c2eGdC/5RDRFiYnVu6M6FPlxXfszS1ac7J\naPwEwhJk/HiI17yZL8vdEdhdAVB0FCL7tV5eu0EmD52aENEcMCfwVWBIFPy1oaIq\n0XbGRGNEQnKyUtyGpM/lhYF0tvgBjBE3jOd4lFcMH9iu1I7BNjnnaCko8xC0oyk3\naVMc3Rabl2iMb72pQbI7Dg+kUYAYjsEdu35PyDYm+FGQWSlbpSL92PHIzaRoBvL3\ncKnESpBw2KaezcltsMiyFfCYqHEZbrbwzns9y7XPpTtY4ItAc5vOelZGZECRCewV\ncKF7X+rM0i1gua8udZH4jRaGlVgQJLwAPNg6SItmGq/a5F1yo0Q=\n=Kn1P\n-----END PGP SIGNATURE-----", "payload": "tree 392e2971d5a599d851b493df1de734554fae6b54\nparent c92eeefb5bcf891b22d84376e22d61bbd20f7318\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127542 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127908 -0700\n\nBump apscheduler==3.11.2\n\n", "verified_at": "2026-03-21T21:18:45Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/31ce763b6a45e5dc82992cfc3a8183c94b34ab61", "html_url": "https://github.com/Tautulli/Tautulli/commit/31ce763b6a45e5dc82992cfc3a8183c94b34ab61", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/31ce763b6a45e5dc82992cfc3a8183c94b34ab61/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "c92eeefb5bcf891b22d84376e22d61bbd20f7318", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c92eeefb5bcf891b22d84376e22d61bbd20f7318", "html_url": "https://github.com/Tautulli/Tautulli/commit/c92eeefb5bcf891b22d84376e22d61bbd20f7318"}]}, {"sha": "7f46ea832e24f12a0e4dfe9904f7379c59792d6f", "node_id": "C_kwDOAducuNoAKDdmNDZlYTgzMmUyNGYxMmEwZTRkZmU5OTA0ZjczNzljNTk3OTJkNmY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:12:33Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:18:28Z"}, "message": "Bump urllib3==2.6.3", "tree": {"sha": "44a42913e39d9c86a8c7ba25cf8f03e2e55cae5b", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/44a42913e39d9c86a8c7ba25cf8f03e2e55cae5b"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/7f46ea832e24f12a0e4dfe9904f7379c59792d6f", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/CyQACgkQm/TvTexB\nFjoa9w/8DzGP3CrtxK9Im/Cuv4UkKLjtckaM5x6YHvTUUy/ubRYyg1FwMUFDTk/d\n88JFtBcyMHBkIEV2ngiI/iaRNILDpth1BlG7g+a/SoZQrXO+V9GtmIaRQsXZrxiC\nyPrgb8IaxYwYX1kD6wVTkWLQLystR8Gw4yc0HSMUkLglwYhBHcaCdTyyfgLbs5KI\nfmIlEtDhRCWmAFJ2rczKeKIjhdK8aVOkLNisQ6gDRfsC9JkDhkq9ceNONzqw4Pwg\nhAFN9jM9H0CtthwvdU0uVHVQT8IGH1yYo1eJZoVPwQrzarmktqvJIl5EIZHQz0Nr\n0RCkK/gk2xlRrdDuP+7QTSOYXkZTDvWzfxfJce1hhkkvnrjJQDvjjhYIwwm8BzGz\nafYOJB7AeyIJHROEyGRJRei7ugG11kxlAijort+YBQKhbSL4g9mRoT+Q4pZ95iu3\nxVRraAXjrr1ZlFgcvBJQAWbOA3UbIpYWe7j0KXCNkxmVKyt2VFiuPXyD7NIIZ56q\nBwvXnoNNx5dqJNMg/HkXDCIkz/4V/VrCMmRe/ozX9+k/FBQL2wzsdmCLRif/mQ19\nZ2H3tV3Tj4Cbl4uSiDA+dm5Van10N1V8JDnWP3Xwc51sPjvmULbvnvaVAwBtpFq/\nAJ1Ye0FwsD2wYkjnK3zqL5W4ZSal4XLAvLEn92LLElKtbNo5xR8=\n=Y/wr\n-----END PGP SIGNATURE-----", "payload": "tree 44a42913e39d9c86a8c7ba25cf8f03e2e55cae5b\nparent 31ce763b6a45e5dc82992cfc3a8183c94b34ab61\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127553 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127908 -0700\n\nBump urllib3==2.6.3\n", "verified_at": "2026-03-21T21:18:45Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7f46ea832e24f12a0e4dfe9904f7379c59792d6f", "html_url": "https://github.com/Tautulli/Tautulli/commit/7f46ea832e24f12a0e4dfe9904f7379c59792d6f", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7f46ea832e24f12a0e4dfe9904f7379c59792d6f/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "31ce763b6a45e5dc82992cfc3a8183c94b34ab61", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/31ce763b6a45e5dc82992cfc3a8183c94b34ab61", "html_url": "https://github.com/Tautulli/Tautulli/commit/31ce763b6a45e5dc82992cfc3a8183c94b34ab61"}]}, {"sha": "9adc4bb151fab6c86be799f4aa40fdb53508dcbc", "node_id": "C_kwDOAducuNoAKDlhZGM0YmIxNTFmYWI2Yzg2YmU3OTlmNGFhNDBmZGI1MzUwOGRjYmM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:13:25Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:18:29Z"}, "message": "Bump PyJWT==2.12.1", "tree": {"sha": "a26f4f6ced2a8b6c8c95ec86cb1ff6bb88a2f55c", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/a26f4f6ced2a8b6c8c95ec86cb1ff6bb88a2f55c"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/9adc4bb151fab6c86be799f4aa40fdb53508dcbc", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/CyUACgkQm/TvTexB\nFjogOBAA0B1GCp3WWgBJ5o+wVSNDuvTbWJZBlZzTVi1k3Q6FXfpWUoCWvI2r76kX\nLEUBFzcQrTMVyUMzbXvnkDOs9UrnX5dieeethJ5DT3meNSi9hWxa39fkscYct6jp\nvS+E4ocmf+hCSqvDN0T2aISdsZgibj34rXA6ZLEcXVCz+B8/ISFDZJjMLeqkOze+\nc2GSaGZfzQE8WBCPAF/PZjL5qdcMruBwU3G+/qGfnWdMVh4gw3KJSs4Mf5WtAs+d\nMMh31nQG/k0gtY7jiAkKDoqPFES18FsKhTb8X+mI1+H5h07gIvqFb2Ce29kbUn+Y\ngvvf3KzBbeqlUxn+4daHdGgvmdh+5gT87qFy7pO/ZsG7X9XMnVO28ycfTWeXTvC8\nobv+Imq9E3HCmb8R6eEskp/vDtOZVyYR9ITCubjA0yPKaUMXkQcTYBpQTLWbqy86\n8WQGyCKLNorcCWAoNMy6lxMUG+HpKr8AzdIEO23W8KHH3UfVbExBr0bM04xZ7Lo7\nXK0jPU+BCXLAaHjELqE6nD0TLMsgLDyF3yx1RjHRGdF63xFMnFdEcZowHd02meVU\n8Z97NcxOgeYIlN/RXCs4zVkzcYCN1NZLczZ7U+k8wPn1mQYJOiCshI/fGxyfrA/Q\nZ0mXO/TvnGEtM21413uMAd6D9CiVFaEnMmJRXJrAu4WLscRNzME=\n=YrId\n-----END PGP SIGNATURE-----", "payload": "tree a26f4f6ced2a8b6c8c95ec86cb1ff6bb88a2f55c\nparent 7f46ea832e24f12a0e4dfe9904f7379c59792d6f\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127605 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127909 -0700\n\nBump PyJWT==2.12.1\n", "verified_at": "2026-03-21T21:18:45Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9adc4bb151fab6c86be799f4aa40fdb53508dcbc", "html_url": "https://github.com/Tautulli/Tautulli/commit/9adc4bb151fab6c86be799f4aa40fdb53508dcbc", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9adc4bb151fab6c86be799f4aa40fdb53508dcbc/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "7f46ea832e24f12a0e4dfe9904f7379c59792d6f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7f46ea832e24f12a0e4dfe9904f7379c59792d6f", "html_url": "https://github.com/Tautulli/Tautulli/commit/7f46ea832e24f12a0e4dfe9904f7379c59792d6f"}]}, {"sha": "687f62fb8b881bd28cc8a5a27605fccbf183a747", "node_id": "C_kwDOAducuNoAKDY4N2Y2MmZiOGI4ODFiZDI4Y2M4YTVhMjc2MDVmY2NiZjE4M2E3NDc", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:19:12Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:19:12Z"}, "message": "Bump cryptography==46.0.5 in /package", "tree": {"sha": "e4453e6fdd934b7b8c59b11f5e21262ea105605a", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/e4453e6fdd934b7b8c59b11f5e21262ea105605a"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/687f62fb8b881bd28cc8a5a27605fccbf183a747", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/C1AACgkQm/TvTexB\nFjrjtg/+MEWsYUEIdAQlptodgd9xj8VtpNARCTJTP+COwno9ZTRu9LL24crjjm/o\nVVAuB3HBPedqLbnJ5wuwci+iS2S6b2V+PpeS4q8l6trLPV1De9MQPDuxaiqFWRCV\nPqQTMNJXX0Q6UiXW1ywVGpwRZftWG5KOYt15OJxwGNJ4DFfU398ueY+AlxzO06Iy\nDnJmvDpPfI95IuKgNJumsTUtroLCiMJ8jXXVSJptzZJOk6kxfxIHKP4OjNgYa48L\n/FZOiAIPkGrFFBGN+Xd6vNmecEvYabAwl2JMwx4M13A4I4eXdVwFdt0IYO+bc4Bf\nZHrYlTww94XFJJiaTYg6ENoEyPrwY/2q9hkkYrrS8IRqDCOLg/Y3ipqdH4pKLYks\nfx04hiavBR0HAWokjhShHlhYhSrqPM0M3DyrYZPHCsKbcUSLyqe3lGs/5wgfH1OI\nGRhAZ+4NJbs+Q4Vfc+1RCmbemii6JeExoHhIysfXvZMcUpLgUYe4B6AEiOHcQXmg\nhFSMcyao0PDnn3oQEblr1ffgcRyTE9Uvsgj4HFHhaWKFyBfBk45CnbprExkU2b3a\nO96AbFRsgL2jLi8nnbo4PTwUX3DH4xwD61RXscROVvYAIdgGykdZED/QOWKw9Ukd\n38sHAM55qmvHac5+fxs2Cr49DrjvhYYiTJPo0qE9sXU4dXQhOM4=\n=h0qm\n-----END PGP SIGNATURE-----", "payload": "tree e4453e6fdd934b7b8c59b11f5e21262ea105605a\nparent 9adc4bb151fab6c86be799f4aa40fdb53508dcbc\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127952 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127952 -0700\n\nBump cryptography==46.0.5 in /package\n", "verified_at": "2026-03-21T21:21:16Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/687f62fb8b881bd28cc8a5a27605fccbf183a747", "html_url": "https://github.com/Tautulli/Tautulli/commit/687f62fb8b881bd28cc8a5a27605fccbf183a747", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/687f62fb8b881bd28cc8a5a27605fccbf183a747/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "9adc4bb151fab6c86be799f4aa40fdb53508dcbc", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9adc4bb151fab6c86be799f4aa40fdb53508dcbc", "html_url": "https://github.com/Tautulli/Tautulli/commit/9adc4bb151fab6c86be799f4aa40fdb53508dcbc"}]}, {"sha": "77f8176aaa532690ad1f3cedc3c169e29bde7567", "node_id": "C_kwDOAducuNoAKDc3ZjgxNzZhYWE1MzI2OTBhZDFmM2NlZGMzYzE2OWUyOWJkZTc1Njc", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:19:24Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:19:24Z"}, "message": "Bump pyopenssl==26.0.0.0 in /package", "tree": {"sha": "2bcdc494d1252cd6d9d234bb3393caaf405696d4", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/2bcdc494d1252cd6d9d234bb3393caaf405696d4"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/77f8176aaa532690ad1f3cedc3c169e29bde7567", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/C1wACgkQm/TvTexB\nFjq5YQ/6Amw5WHeAv2aVBGr9u8V/LcchxpVAYSmT0RZAtoueqTfVTN57Y05vY3vg\n+7xRz/l1B061aFyRyhnEtfQj5fcDhcPXWG+tiVnCE0H8HIMwLCddygneAzHEjMbf\nk6t+GVW+u//Tzen4t5KRRs4V64w1scWwMuVOmRnvMmfGnp3AqIDNQG4Hb83HBIe2\n1cayO+SpetYUGhxwNBsQbbEpjSF/XstyOGgq0UGM+8zbTgA7x/tUqWsrNECvU2D8\nnJsVNXistHw/FN1TJudwsN6rKtmWN7YxjlZ4qFvI5OwlThNoMqb97yjbOPXxO7NV\ncSAR29BI8rZ1UvR1Bm+Xa+5AuujuoFVrzyIGDD8oMLykAQPwWhCbhhd6oT/nyoEB\nS73d/6R0xZKc2BFh8vNrkIJ9EnCwRa0HylASpCiG86esl5euE6vkdlTE7fCdo++d\n4yygoGpG8EgQJDDd81m561pM0CjINvCLJe1do4SFCU/AXwvrP0FQZybVtWI0c3IH\nXQFxcmCLDoKAKcyXVdhkHgx9EfKGR7Iwdagz5MuC+4uRLZW1kkBAtTsNPbkYB+dD\nINx2xcL6XZxTsDt9GYaFDnqd4Qk4Oskf6zPNgOauIiDhAoEUnY8ct6diKwICIpNz\noKJm2LqQAtY9vgtAfhQ9ZqwJaM0K4gAf1GBX70GuQcGgmcJkADI=\n=zB2e\n-----END PGP SIGNATURE-----", "payload": "tree 2bcdc494d1252cd6d9d234bb3393caaf405696d4\nparent 687f62fb8b881bd28cc8a5a27605fccbf183a747\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127964 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774127964 -0700\n\nBump pyopenssl==26.0.0.0 in /package\n", "verified_at": "2026-03-21T21:21:16Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/77f8176aaa532690ad1f3cedc3c169e29bde7567", "html_url": "https://github.com/Tautulli/Tautulli/commit/77f8176aaa532690ad1f3cedc3c169e29bde7567", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/77f8176aaa532690ad1f3cedc3c169e29bde7567/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "687f62fb8b881bd28cc8a5a27605fccbf183a747", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/687f62fb8b881bd28cc8a5a27605fccbf183a747", "html_url": "https://github.com/Tautulli/Tautulli/commit/687f62fb8b881bd28cc8a5a27605fccbf183a747"}]}, {"sha": "6e42feeb6132dd4dfca0a44d4ee51aaffd7feccf", "node_id": "C_kwDOAducuNoAKDZlNDJmZWViNjEzMmRkNGRmY2EwYTQ0ZDRlZTUxYWFmZmQ3ZmVjY2Y", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T21:36:44Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T21:36:44Z"}, "message": "Bump pyobjc-core from 10.3.1 to 12.0 (#2608)\n\nBumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 10.3.1 to 12.0.\n- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)\n- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/main/docs/changelog.rst)\n- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v10.3.1...v12.0)\n\n---\nupdated-dependencies:\n- dependency-name: pyobjc-core\n  dependency-version: '12.0'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "6038ae0ec2c9f915daa68bf38fe5a49a750a7b4c", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/6038ae0ec2c9f915daa68bf38fe5a49a750a7b4c"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/6e42feeb6132dd4dfca0a44d4ee51aaffd7feccf", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvw9sCRC1aQ7uu5UhlAAAWM4QAGDoDLvsjFoDg+Cx+oNzdB6t\n8vQYLGlfd3012lHEiLR3v1/jZytpwDUHucprqJyotI3VO+MkkIIIeB958IUfbef5\nq6hOdlGQAtVsvE+CCxEb9n0aZzb4Q+axy/AEkwy769hUSUMZYF+CNbhJDEqJVc8G\nEFnWVZRpK4heLnzQQ9Gsq+P3S8ouSo+A+fuN5EXEVj/gCHhek/mptAkq7K1VtCJv\nDX6/4h3cpotDHdm9bA4L5iw8bFiSdkPowsOpoVI9EAbzyUdtDlIQEHnG8hlr55mG\nIEwP/nAN5owdcdMFoW4l6ynpUOxhi58cKh3E5NTw6+fjK1W6qsexmNKXd5L+A05c\nVYHpLfgQ827cQizflJIxgDcWNHrF5AmeVMSYvgGmUqHzLY9FPG9WyIuct/rQ2ZO/\nP/MMwK2XNpecKoBujFL4kqcWvIWo4ABqpjx/fehOaM6m1lP6sENYlKQ/FtXFQZL8\nCAeXVSA2CWRSnLAxcokB/7wtgOALC/GbehjcRSxBWPnNa/B7zX57Q7dkVWB7mMI4\nZ904/opjpNvzT6ui4D8wfrNqXkgrY2tvzZusbS1SHj6aPbpl9ZfiInbFhcEw8Lbi\nqgvpWVm27RarSqTF35O8CF7dWR/BuchBsasiOCJuKJur9GO0FVdWpb45F5XiLG0F\nQ8EanfZOJzmKy8ViQDNB\n=2UrS\n-----END PGP SIGNATURE-----\n", "payload": "tree 6038ae0ec2c9f915daa68bf38fe5a49a750a7b4c\nparent 77f8176aaa532690ad1f3cedc3c169e29bde7567\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774129004 -0700\ncommitter GitHub <noreply@github.com> 1774129004 -0700\n\nBump pyobjc-core from 10.3.1 to 12.0 (#2608)\n\nBumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 10.3.1 to 12.0.\n- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)\n- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/main/docs/changelog.rst)\n- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v10.3.1...v12.0)\n\n---\nupdated-dependencies:\n- dependency-name: pyobjc-core\n  dependency-version: '12.0'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T21:36:45Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6e42feeb6132dd4dfca0a44d4ee51aaffd7feccf", "html_url": "https://github.com/Tautulli/Tautulli/commit/6e42feeb6132dd4dfca0a44d4ee51aaffd7feccf", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6e42feeb6132dd4dfca0a44d4ee51aaffd7feccf/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "77f8176aaa532690ad1f3cedc3c169e29bde7567", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/77f8176aaa532690ad1f3cedc3c169e29bde7567", "html_url": "https://github.com/Tautulli/Tautulli/commit/77f8176aaa532690ad1f3cedc3c169e29bde7567"}]}, {"sha": "15f1b28d242a89089c99050d51761f53c5670938", "node_id": "C_kwDOAducuNoAKDE1ZjFiMjhkMjQyYTg5MDg5Yzk5MDUwZDUxNzYxZjUzYzU2NzA5Mzg", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T21:37:41Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T21:37:41Z"}, "message": "Bump pyobjc-framework-cocoa from 10.3.1 to 12.0 (#2609)\n\nBumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 10.3.1 to 12.0.\n- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)\n- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/main/docs/changelog.rst)\n- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v10.3.1...v12.0)\n\n---\nupdated-dependencies:\n- dependency-name: pyobjc-framework-cocoa\n  dependency-version: '12.0'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "16c70f8883f703279c646e0f2ac646be169ca914", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/16c70f8883f703279c646e0f2ac646be169ca914"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/15f1b28d242a89089c99050d51761f53c5670938", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvw+lCRC1aQ7uu5UhlAAAEa8QAK2C1v3MHkeLPXk79U7kNMPo\nnGvkEq5XSs+mtwfRCXporfbjMBVMmAw2vxMlzF3TyFDELVHK8SSDSVNNXsG/G1fU\nS4VmNZ3zRZ13yO29UVyyKek61sgSB6/ugtdEHbIjmeGHV14XZN+KmY5lB9FRrcoD\nToRrMN9lIxVPW7gdgFYZgajzT41SlA2oAavrfw3DLn66jlYjDh9WnxZ7g6IBMjXJ\nqbWx/ZnkoHQ3XA7FwsNTpQRRKgUzaXyHQqI1J7jrWbj6M7tKgaLXev1I3Vt05UZv\nSl9iTpLjbuWzaB5PfO1419Ko8FDGKCE5W4oJyi5qjyRbAdTTOjdW3Gs0X24KmstI\njjSdJJMBE2EMDOUQByx8FpfGnqk1XLh+pzhUQ04Y0jJOpLCz2ExsL+KQ7QhVaMUV\nVXfeXsCOoF7B7vc8hgIEWejvm9JDsRu38M+PHXt0+7ZTJbMd35e0L5C3dTA/Ce+V\nuqq926hjP9lymvBa5mlUjhU1us9puzY/NOUUjbwwFeFSUnE/A4thOCizx7wPjpWe\nPaYor9FdrjbvKuoHMZMhfWo2gS4NZ44WU0wXRhtVehFXiQbvnnS4tKWTRwpRCpWB\n0TvXKIFeuVrXisziHXzKLaFneJcwrEDb4M96/SW5FkvNVCNi9lnRzz3MrUHNo1/K\n2+fhis/KvTSSibQdkw0M\n=iDRx\n-----END PGP SIGNATURE-----\n", "payload": "tree 16c70f8883f703279c646e0f2ac646be169ca914\nparent 6e42feeb6132dd4dfca0a44d4ee51aaffd7feccf\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774129061 -0700\ncommitter GitHub <noreply@github.com> 1774129061 -0700\n\nBump pyobjc-framework-cocoa from 10.3.1 to 12.0 (#2609)\n\nBumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 10.3.1 to 12.0.\n- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)\n- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/main/docs/changelog.rst)\n- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v10.3.1...v12.0)\n\n---\nupdated-dependencies:\n- dependency-name: pyobjc-framework-cocoa\n  dependency-version: '12.0'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T21:37:42Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/15f1b28d242a89089c99050d51761f53c5670938", "html_url": "https://github.com/Tautulli/Tautulli/commit/15f1b28d242a89089c99050d51761f53c5670938", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/15f1b28d242a89089c99050d51761f53c5670938/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "6e42feeb6132dd4dfca0a44d4ee51aaffd7feccf", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6e42feeb6132dd4dfca0a44d4ee51aaffd7feccf", "html_url": "https://github.com/Tautulli/Tautulli/commit/6e42feeb6132dd4dfca0a44d4ee51aaffd7feccf"}]}, {"sha": "3602cbb7e4eb282bb15a0f9ebf7a6258bcfd41e9", "node_id": "C_kwDOAducuNoAKDM2MDJjYmI3ZTRlYjI4MmJiMTVhMGY5ZWJmN2E2MjU4YmNmZDQxZTk", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T21:37:52Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T21:37:52Z"}, "message": "Bump pywin32 from 308 to 311 (#2573)\n\nBumps [pywin32](https://github.com/mhammond/pywin32) from 308 to 311.\n- [Release notes](https://github.com/mhammond/pywin32/releases)\n- [Changelog](https://github.com/mhammond/pywin32/blob/main/CHANGES.txt)\n- [Commits](https://github.com/mhammond/pywin32/commits)\n\n---\nupdated-dependencies:\n- dependency-name: pywin32\n  dependency-version: '311'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "tree": {"sha": "c60db825211eba40907db978179c9cde1b276929", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/c60db825211eba40907db978179c9cde1b276929"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/3602cbb7e4eb282bb15a0f9ebf7a6258bcfd41e9", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvw+xCRC1aQ7uu5UhlAAAsxAQAD+7xrpQ2hN3xhzxwmai5Bv4\ncXPiExw/JQsttYFV4tVsfYxt80rXjQ4MTUfII+3cBWb0y9PWpFM5g0HGaa3WczCd\nhJvJBlEq/gufFK1CtXWND5jHybHrYqowcyvTyxwe9OAq53jZKFkn23/URMoHDHM+\nHu8XOINPjlcRqpGz0zmEq86XJkKqStOnJF3V4yVMkv2o409sbBrHdNDFcYTQ2IkO\nRQOawtoXAj4YmHLj8X1jxtIJq4vzOiGxBPdOiqefMaQsW/22kqPg2lVtW4eMD4+c\n4ACXICyGsn1dn1q1Pk45WCOA4uh2icHq92FIggz9GDX0GEi+KsSJpeNqNU//dJcA\nnYsd0YwQTuuUz6FVB2MF3/CU91kAhPuymkUZFJ7K2DHRPlJVzT9tnhxhHDeuUL4e\nBpO7TIRQQFK2odAo2wepvM5roW9dyY2IDjHbu6opUFyXbBKMX3IiVG5omyGVP+5t\nqIMz4o6u0nQIXlcV32uTCcRZlQJbKGoRuND0lb/ASqvirUPwn1XaHCF5LUt04cZB\niruKHWeIz/KfyMD1cLIaGJ+oALMeELD+LYRrMAIDusTt6LBCbs9RDevvJ+RpjF3f\nj9wl26kqYCJpsCmm+BTdeWVh6C7aFVgFyeo1y4xufcIy3S/1q8cgXNX52M3amDKm\nzSb6Pogi4G6dXTLOJINN\n=5WfQ\n-----END PGP SIGNATURE-----\n", "payload": "tree c60db825211eba40907db978179c9cde1b276929\nparent 15f1b28d242a89089c99050d51761f53c5670938\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774129072 -0700\ncommitter GitHub <noreply@github.com> 1774129072 -0700\n\nBump pywin32 from 308 to 311 (#2573)\n\nBumps [pywin32](https://github.com/mhammond/pywin32) from 308 to 311.\n- [Release notes](https://github.com/mhammond/pywin32/releases)\n- [Changelog](https://github.com/mhammond/pywin32/blob/main/CHANGES.txt)\n- [Commits](https://github.com/mhammond/pywin32/commits)\n\n---\nupdated-dependencies:\n- dependency-name: pywin32\n  dependency-version: '311'\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "verified_at": "2026-03-21T21:37:54Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3602cbb7e4eb282bb15a0f9ebf7a6258bcfd41e9", "html_url": "https://github.com/Tautulli/Tautulli/commit/3602cbb7e4eb282bb15a0f9ebf7a6258bcfd41e9", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3602cbb7e4eb282bb15a0f9ebf7a6258bcfd41e9/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "15f1b28d242a89089c99050d51761f53c5670938", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/15f1b28d242a89089c99050d51761f53c5670938", "html_url": "https://github.com/Tautulli/Tautulli/commit/15f1b28d242a89089c99050d51761f53c5670938"}]}, {"sha": "e65904c114bf47f3e0fd4cf85062a30637bf40d4", "node_id": "C_kwDOAducuNoAKGU2NTkwNGMxMTRiZjQ3ZjNlMGZkNGNmODUwNjJhMzA2MzdiZjQwZDQ", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T21:42:37Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T21:42:37Z"}, "message": "Bump importlib-metadata from 8.5.0 to 8.7.0 (#2541)\n\n* Bump importlib-metadata from 8.5.0 to 8.7.0\n\nBumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.5.0 to 8.7.0.\n- [Release notes](https://github.com/python/importlib_metadata/releases)\n- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)\n- [Commits](https://github.com/python/importlib_metadata/compare/v8.5.0...v8.7.0)\n\n---\nupdated-dependencies:\n- dependency-name: importlib-metadata\n  dependency-version: 8.7.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump importlib-metadata==8.7.0\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "405460b5ec10c4987d34cfa97bf929ce11253ee7", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/405460b5ec10c4987d34cfa97bf929ce11253ee7"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/e65904c114bf47f3e0fd4cf85062a30637bf40d4", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvxDNCRC1aQ7uu5UhlAAAlIUQAFA3PdiEG09whhin94LKjjjp\n820weB/t9484QW6SVYtx17KXstU8z2wxEvIF0TH/oL3cqHFRlgviLumSn+W4M1gU\nCPUtpcz3xAjgQxO/D5wOw+xO68xk+ge5KKZbLkFhoF2JxmsEaCxunP6E/39Vxbj/\njr/KeUG/Q8viQbJD4hX+aiPyjRNV6H1SPTVo/KuxR3Cz1MzJrt+bgosjo8RAc8cX\nNbMhmvGm5Zd2dH8oAIKnmuHKdgmMc1pXO9yRd4twhkuLIzD81jJL28lMbR9iutjX\nPUeI7gf7TzKGv1JkDujLtSv0EuZcWyjpVvDWRJNNejVAn/D46I/05BY9eTFxNQ9C\n3bzoGFu9BQ1jnJzywLTqPoGL2mns8MLQCmS1qOD7cm7Vnt9DaMAB0VDM8vZq26Ng\nXBBTVWSqHzFhv8gf6T6Rkgedn+r2k3eL4TBlxdojH230dXjjJSDQuzEyVYumE5fK\nj04NEz3hKQQozIkOakg+zzEDWvciuK7y49SKRnZW6Cmv7VMVeOw5fnlDjSN2h+Cn\nQcEnYfJw/2/lwCKjcTv5HoumiUMr1VzaN4nGvVKbq0WKFLyzhGFbPKoBQFT5zjQo\n7hftDRrUuGINUcKpFlQBkkXzhrVKRJisiEY9CtDdEp8DEK5s8BpMw6gAiXh/OENV\nuWp9qlxtbQl68a+z98hQ\n=nuHI\n-----END PGP SIGNATURE-----\n", "payload": "tree 405460b5ec10c4987d34cfa97bf929ce11253ee7\nparent 3602cbb7e4eb282bb15a0f9ebf7a6258bcfd41e9\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774129357 -0700\ncommitter GitHub <noreply@github.com> 1774129357 -0700\n\nBump importlib-metadata from 8.5.0 to 8.7.0 (#2541)\n\n* Bump importlib-metadata from 8.5.0 to 8.7.0\n\nBumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.5.0 to 8.7.0.\n- [Release notes](https://github.com/python/importlib_metadata/releases)\n- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)\n- [Commits](https://github.com/python/importlib_metadata/compare/v8.5.0...v8.7.0)\n\n---\nupdated-dependencies:\n- dependency-name: importlib-metadata\n  dependency-version: 8.7.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump importlib-metadata==8.7.0\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T21:42:38Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e65904c114bf47f3e0fd4cf85062a30637bf40d4", "html_url": "https://github.com/Tautulli/Tautulli/commit/e65904c114bf47f3e0fd4cf85062a30637bf40d4", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e65904c114bf47f3e0fd4cf85062a30637bf40d4/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "3602cbb7e4eb282bb15a0f9ebf7a6258bcfd41e9", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3602cbb7e4eb282bb15a0f9ebf7a6258bcfd41e9", "html_url": "https://github.com/Tautulli/Tautulli/commit/3602cbb7e4eb282bb15a0f9ebf7a6258bcfd41e9"}]}, {"sha": "7286f343753545f92dbd2321e52b4b6e82fcedf1", "node_id": "C_kwDOAducuNoAKDcyODZmMzQzNzUzNTQ1ZjkyZGJkMjMyMWU1MmI0YjZlODJmY2VkZjE", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-21T21:43:59Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-21T21:43:59Z"}, "message": "Bump importlib-resources from 6.4.5 to 6.5.2 (#2476)\n\n* Bump importlib-resources from 6.4.5 to 6.5.2\n\nBumps [importlib-resources](https://github.com/python/importlib_resources) from 6.4.5 to 6.5.2.\n- [Release notes](https://github.com/python/importlib_resources/releases)\n- [Changelog](https://github.com/python/importlib_resources/blob/main/NEWS.rst)\n- [Commits](https://github.com/python/importlib_resources/compare/v6.4.5...v6.5.2)\n\n---\nupdated-dependencies:\n- dependency-name: importlib-resources\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump importlib-resources==6.5.2\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "a5c023ea5f1cf79ddbaa38a0a1765ddd61e928b8", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/a5c023ea5f1cf79ddbaa38a0a1765ddd61e928b8"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/7286f343753545f92dbd2321e52b4b6e82fcedf1", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpvxEfCRC1aQ7uu5UhlAAAkcsQAFBMg/pgKX8joD8PJycwy2DA\n4Y+ave5F6rtOyYMzEpSmUuM1qWZh2r9qWf1yxLeepdSvSXL3cu0KhLJAIq4A5p2N\nLNjkyu3EjOzgJYiBzfsSdYRtgBj2IkbT3nPYQolqdR45dcHqGOVsxZVctlScbp5/\n3fa7iDHHfpR/7MJrCtaZELaUvAlcw0kVLcLV7+RkxBhzpClm81Fa1l519gqAlKR+\n7tpbPzm6WBY4tvua6fTIjn0SUEo9ArAinUyNMTnPEOaH2T76JblATF3AqgVQ0wXk\nhaddkOIfSiz0Of+/W1y0xA7ToDjJq2MnrS7Dvf/nROUIENKAgJ1TB7XiSMC8J3pm\nASvtFo3wzL3Rft/pW1P3LB/aAIRWMjdNPLw8+tzWmnCriHMdqYVtshvabuYooJY6\nptmZVqlvbxTMOFWSYDVyHHAmA00KEbmqnJg/oBx1Ep9qgRw8Gcs6e9KIWAGy3ci3\nDH5WDXPPJ1ejoSFvesuZYniRQUehZDXXPzwuqbZ3cxhgofwmPYjQhLE7rJsMUCHx\nQFNK0nLt+jCyeArhxVaUkyCbRD+WHcyHxiGFBiM+x93JTYib6MHe7C7sBdmvEPbW\noq7NDf54oGuEjts6sSBAHOx/mFbb3dHIn45kInoWJiaKFIXn9yrvt6s41t8kVkC5\n5wr7ZMW6A4LFJBoFSr8N\n=nruc\n-----END PGP SIGNATURE-----\n", "payload": "tree a5c023ea5f1cf79ddbaa38a0a1765ddd61e928b8\nparent e65904c114bf47f3e0fd4cf85062a30637bf40d4\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774129439 -0700\ncommitter GitHub <noreply@github.com> 1774129439 -0700\n\nBump importlib-resources from 6.4.5 to 6.5.2 (#2476)\n\n* Bump importlib-resources from 6.4.5 to 6.5.2\n\nBumps [importlib-resources](https://github.com/python/importlib_resources) from 6.4.5 to 6.5.2.\n- [Release notes](https://github.com/python/importlib_resources/releases)\n- [Changelog](https://github.com/python/importlib_resources/blob/main/NEWS.rst)\n- [Commits](https://github.com/python/importlib_resources/compare/v6.4.5...v6.5.2)\n\n---\nupdated-dependencies:\n- dependency-name: importlib-resources\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump importlib-resources==6.5.2\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-21T21:44:00Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7286f343753545f92dbd2321e52b4b6e82fcedf1", "html_url": "https://github.com/Tautulli/Tautulli/commit/7286f343753545f92dbd2321e52b4b6e82fcedf1", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7286f343753545f92dbd2321e52b4b6e82fcedf1/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "e65904c114bf47f3e0fd4cf85062a30637bf40d4", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e65904c114bf47f3e0fd4cf85062a30637bf40d4", "html_url": "https://github.com/Tautulli/Tautulli/commit/e65904c114bf47f3e0fd4cf85062a30637bf40d4"}]}, {"sha": "36013de26167d74d8521e365b54ff376fe4f6d62", "node_id": "C_kwDOAducuNoAKDM2MDEzZGUyNjE2N2Q3NGQ4NTIxZTM2NWI1NGZmMzc2ZmU0ZjZkNjI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:45:27Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:45:27Z"}, "message": "Bump importlib-metadata==9.0.0", "tree": {"sha": "67e04d28d7ba7f32831abba6907b85e835202867", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/67e04d28d7ba7f32831abba6907b85e835202867"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/36013de26167d74d8521e365b54ff376fe4f6d62", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/EXcACgkQm/TvTexB\nFjo8zRAAnydBxbIvJzDBKSYfmop3mbU+vqnmnlGPxpHutOgvmlXToR2KtUlFjZZO\nwg9obOTNT/hjs2XK4dpQ2jo/5WSK+y43XqgAHcHh0xW4vK0o9pfFn42KtMICOWSY\nTbZtueffM9sa829myjrn/Qc/GKmp5jex2eRdnoS5gSRj7Zv8a6QWKhMseaOrlLMf\nDsYPen7QLIEOtnw7xCc7GQDbLSMwvRFXahFcvxKpUY43S6MBoDZtZNUsQIPf7Zvz\nfxtUoLi1nUbqHO/YkjmroE8E8lbVVcwTTBd7ePZ+vVvzFeRmYgrHIM7lJrsdqNx3\nqR6S2S/QHKyABha0NWjpI1eeMg1cillR6+TOp2XVN6VDzxeu3jLvxRFfjBcNM3gQ\n4sQkM5Df04kctPQp5IXsFIGCU3fuOHr1MQjkAzisLwYqTCYTF3WtNkDQJ2R2x/fU\niM/w8AWwbFYXc25x+9nO0I9dFq+s7UYDhg5XRzgGfVyoAxGwSwhEOEVzv7Bse2I2\netKKGTFtJn++x/nmjjOT1QqSHGqGx31/9sy4qOr9DCCX3tRCqRd3cBB1ifbVrWAS\n2RyeGtdHS+ujwy8knQW5cwmz4VknCAlhVanQOxmn1kRi93aXhJ948dirsD/jIGKq\nuH7KKvRpXrwp2V/herffWQGudyCfGYZXrqhIAWdYsrXgA/Rvw+U=\n=qhXk\n-----END PGP SIGNATURE-----", "payload": "tree 67e04d28d7ba7f32831abba6907b85e835202867\nparent 7286f343753545f92dbd2321e52b4b6e82fcedf1\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774129527 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774129527 -0700\n\nBump importlib-metadata==9.0.0\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/36013de26167d74d8521e365b54ff376fe4f6d62", "html_url": "https://github.com/Tautulli/Tautulli/commit/36013de26167d74d8521e365b54ff376fe4f6d62", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/36013de26167d74d8521e365b54ff376fe4f6d62/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "7286f343753545f92dbd2321e52b4b6e82fcedf1", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7286f343753545f92dbd2321e52b4b6e82fcedf1", "html_url": "https://github.com/Tautulli/Tautulli/commit/7286f343753545f92dbd2321e52b4b6e82fcedf1"}]}, {"sha": "9ab878d1aebede8b838c7978d2d259f3ee8d67ff", "node_id": "C_kwDOAducuNoAKDlhYjg3OGQxYWViZWRlOGI4MzhjNzk3OGQyZDI1OWYzZWU4ZDY3ZmY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T21:59:55Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:32:47Z"}, "message": "Remove pytz requirement", "tree": {"sha": "615937a73f8cb838baa7169e4c6cf7bf1f3caab3", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/615937a73f8cb838baa7169e4c6cf7bf1f3caab3"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/9ab878d1aebede8b838c7978d2d259f3ee8d67ff", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/HI8ACgkQm/TvTexB\nFjpP+Q//XOS4+lBYxsYhiLbGKojWnr99L2/1aRu5eyGovoc5MS9GxAhGecox8Asu\n75xAyN4UNzK1sWLDp0J2dCPaz7hk10ZAzaT0S+OfJubFqcrBec+qBGSYtieXNPMz\n2leHnBd6/PbQAp6vu90uKR2cPcCuI+33VM/XT/9XEXgcjAwsWYin0fHbW681d+G3\nYUEGKcw2ejtlFXSUrUlCI69hJypYw4ZeHcBuYG6i2VdZtfaEUa2V3IU7eAdQEL3M\nAcCToJqUID9m8CDzPkOpNBJF2dsl1+onrD49MnOCucz4QVsICwq2NAEqnTpzS6ot\ny9+Ku2FCkguNHUtvmcsbUwUIxH+XC/jIvJc1sL7ENk26eherNBHcp+1JbxJG3YvH\nZeH0ZLH57J/72LtAJmZgq8AgB5+CTjmg4tD9JCYIJjYpP/cArjpIqy5Ix5/39vj4\ndgiiSH7fq3TkREChXcGABVuKZ7wrqszwmRC3fNMd8WVdhrSvd34T27cvtuqTmc7G\nL6JWUfEf8c92eVaX0vGMXpt0cjLiBisc8jsJgoHOPd0xY7JlBfrovDPwA3aLZIlP\nOoB1HLiRcSRUy7wQGhXAG3L1KAvJs/MEamiI8ISbcXVAExPosoLCjngFMIXuK2pK\ndozYN204YScxJ4bkMS9VJg9u7qyL0SehHpfaEVYqyehqEph9lh8=\n=TqQd\n-----END PGP SIGNATURE-----", "payload": "tree 615937a73f8cb838baa7169e4c6cf7bf1f3caab3\nparent 36013de26167d74d8521e365b54ff376fe4f6d62\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774130395 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132367 -0700\n\nRemove pytz requirement\n\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9ab878d1aebede8b838c7978d2d259f3ee8d67ff", "html_url": "https://github.com/Tautulli/Tautulli/commit/9ab878d1aebede8b838c7978d2d259f3ee8d67ff", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9ab878d1aebede8b838c7978d2d259f3ee8d67ff/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "36013de26167d74d8521e365b54ff376fe4f6d62", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/36013de26167d74d8521e365b54ff376fe4f6d62", "html_url": "https://github.com/Tautulli/Tautulli/commit/36013de26167d74d8521e365b54ff376fe4f6d62"}]}, {"sha": "26e422e06aa729d4f65374a7215bab930fab432a", "node_id": "C_kwDOAducuNoAKDI2ZTQyMmUwNmFhNzI5ZDRmNjUzNzRhNzIxNWJhYjkzMGZhYjQzMmE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:02:59Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:32:47Z"}, "message": "Bump Mako==1.3.10", "tree": {"sha": "069d3678c019f36c3f3f39e0fbdcce25bb66c73e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/069d3678c019f36c3f3f39e0fbdcce25bb66c73e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/26e422e06aa729d4f65374a7215bab930fab432a", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/HI8ACgkQm/TvTexB\nFjrg0g//Qimk7f6MbCQY3aLENPKfvZ64KLp6MttMta8FUtIridvmZ/7GOE30hQQ1\nd3bHp3eyUVacQKzllxOoVsusBjpW+ffI1tiZzQjA3ewV1i6KD8irWYr7aIMq5DFt\np1HaDQTujDd3VFyRKiZxkdvaSI7JbRiVDnzZAb0H+0tKXwGn+H+ekWVd/ZRnncYb\n0DjT0g2oTjHqiYK9+IUmzYbAfYqE7sHDO+2QXbAkoQGOlI28R0UmVXMQuj5BKiqG\n0IxMVqrHeyLmBPGdQvLkaIdc4B+Pud4hNgr44GZbKx+yGPUjj1zVCRJnmYtFh9mP\nD4RbiytyVCsXh5ooijHAkOnUHcdTxXIYgnWBp4k6++ixbmhAemzy5n5b1ABHBFD4\npIAjzqblRt2bDZRdewF4ggMqTVLipQDehgwmvdgrzkDP/+o+3RT5euniadLwJzze\nD7mNbPMtv3rGaWG7KPiL8zWned1CTRwXYMkD4ZZmEQff5sL6/I9S59iMtuUDChp6\n1jTHQr0lq7JwFhNGdWtWQoPrrJ49d/STk0H3Gh1VJ45TlXzMm8WWyuz3327nuRJg\nLTlqml+zTup4ecW9MWixQePvs2blKDrnisKl32CLEOQ/LuQiczLdPkVNpMtXrRQm\nXnu9MysDseW7yhfWdxKo8mZT6wbkJgAWzvY7yp4CN/3N4VEapoI=\n=nIXd\n-----END PGP SIGNATURE-----", "payload": "tree 069d3678c019f36c3f3f39e0fbdcce25bb66c73e\nparent 9ab878d1aebede8b838c7978d2d259f3ee8d67ff\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774130579 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132367 -0700\n\nBump Mako==1.3.10\n\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/26e422e06aa729d4f65374a7215bab930fab432a", "html_url": "https://github.com/Tautulli/Tautulli/commit/26e422e06aa729d4f65374a7215bab930fab432a", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/26e422e06aa729d4f65374a7215bab930fab432a/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "9ab878d1aebede8b838c7978d2d259f3ee8d67ff", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/9ab878d1aebede8b838c7978d2d259f3ee8d67ff", "html_url": "https://github.com/Tautulli/Tautulli/commit/9ab878d1aebede8b838c7978d2d259f3ee8d67ff"}]}, {"sha": "48823c86b69ae7bcabaa33b8f667468914b70c1c", "node_id": "C_kwDOAducuNoAKDQ4ODIzYzg2YjY5YWU3YmNhYmFhMzNiOGY2Njc0Njg5MTRiNzBjMWM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:07:49Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:32:47Z"}, "message": "Bump arrow==1.4.0", "tree": {"sha": "6a39ac8aac004d7ed0af7ad6eedbfab094c35605", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/6a39ac8aac004d7ed0af7ad6eedbfab094c35605"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/48823c86b69ae7bcabaa33b8f667468914b70c1c", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/HJAACgkQm/TvTexB\nFjpXbBAAqqWNM51L0MAjxbSbDjuujuB4/sCuzloOBNe2WFnHlM+FNGiH++cAr4Sc\nH+sCPAqc3nHND+IW1UNXJKYYR+cQNvdh8kO2eNrxEYw4gwL5h7LAlKfcuDbwBKkt\n5iukXPLzIe4tr5mhZyd0ua3lfuPYpupk12YOFt/qKhDM4Ty9Q3u9b4DGMm06eMmE\nCUM58fSpz5ZwoASPfA4XhCQUVdLOHwwA6fxFcogJKQEn931bbdEje076IZWzEZ+n\nVPb5YyyqHhQ0urJ3VMEbkLkrOmOvJcW452JptaOdsSo6InxS+pz57ftLHeBEbOlI\nitEWTsR3934jk43a8kFZXY4PauqmVrTyJMVBlSZhTE7v0EmbttB/vcyAIqP/fnV3\nZEq1ei8LFaEcY5BDVJ7edEQc9a42nKymLTDYWhxaD+LaqI1ZYHPqdAr6NZJZwg/K\nOzEdbU5StrL7kfAAfYKpA1u5LwJv+sDbE3DdDWvGX1Jl18cLO+asTAPzXOxlI+uY\nIhoJCcL8jlM4MpA/9lnfiiw4JGDxAE0D6N9BW/VWcGNmejUkozOdCiYKGNmmsEsP\nPudS2eOJJI7n0wi2HWkve+WmXB1sghqmhf76yhsZkcnfXEpLC7knARS21P+0CZ08\nKv45Tojvj8DEDUCgXmqG5vurdbYr3La+0ab9PwD9+adV1JqJpzs=\n=WNL2\n-----END PGP SIGNATURE-----", "payload": "tree 6a39ac8aac004d7ed0af7ad6eedbfab094c35605\nparent 26e422e06aa729d4f65374a7215bab930fab432a\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774130869 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132367 -0700\n\nBump arrow==1.4.0\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/48823c86b69ae7bcabaa33b8f667468914b70c1c", "html_url": "https://github.com/Tautulli/Tautulli/commit/48823c86b69ae7bcabaa33b8f667468914b70c1c", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/48823c86b69ae7bcabaa33b8f667468914b70c1c/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "26e422e06aa729d4f65374a7215bab930fab432a", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/26e422e06aa729d4f65374a7215bab930fab432a", "html_url": "https://github.com/Tautulli/Tautulli/commit/26e422e06aa729d4f65374a7215bab930fab432a"}]}, {"sha": "15de4e6804e86503779e37c26a9a6d7bd5bcb58c", "node_id": "C_kwDOAducuNoAKDE1ZGU0ZTY4MDRlODY1MDM3NzllMzdjMjZhOWE2ZDdiZDViY2I1OGM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:09:37Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:32:48Z"}, "message": "Bump beautifulsoup4==4.14.3", "tree": {"sha": "8d1219c8729639db22f4a0d229308e714286551a", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/8d1219c8729639db22f4a0d229308e714286551a"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/15de4e6804e86503779e37c26a9a6d7bd5bcb58c", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/HJAACgkQm/TvTexB\nFjp1XA/8CdjaGpuz/s6NYeD04puWn1HTzNtHvtmunD2OpC4/maIWcU6l7PyyykJh\nyXVwTUzVxaDbWvAneCaBPMSVOQ9iCXUzQ5W/Hkz4r7x284GPQYz2nThA4SU/PhHY\nUUuROQptbVodGBLKj6QQ7DJ0XbqYYlQ6UqSQqiCvwhqRbnzpgG5P19J7+Ws1mMjT\nIbDkxFMNHOHVyJBj+DQNlbmj8psc19J7MX0ulmWQkQ4nOhspPC8DbF+csCWZvCwU\nVmItjGML4jjSHUjTmCO5fVyTZ/IYRXbNpfibvxg5IyELl+6JIymD+o4/qm9QeB9V\nWyk94iC4We1IF7Mbd+579wxrZHioGVv7EIqq/tQc53zpole1vUUBqMPm1gCOJ2AO\noqy913ymxcvj8S8YGeC7vTklipuz4vTZCCg9LU9oD6s7lFDF19G+gAr9gsehHjwR\nxRWfEAHCW8uZH3eFxQqFfomEFHsUZdHD6HXDURbWXS9EiKEANoDQFlOL9nDiQV4d\n8QAlR4QDxQTjJG3PUKYKc2ipeoq9H9OD7j9EKgWgFE+RlWye4lFq0g+iFSksb8vn\nqwM9u0TYDoD36yIAsALLbk/Ur0Uf924724J8iyc/x4NSYroYvjkNFKLTV8G3sq7M\nS0bxbBJ/7ebXnZRSji4wLMUYFsihIXqypG0JJidegiG3Vp74elA=\n=2Dum\n-----END PGP SIGNATURE-----", "payload": "tree 8d1219c8729639db22f4a0d229308e714286551a\nparent 48823c86b69ae7bcabaa33b8f667468914b70c1c\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774130977 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132368 -0700\n\nBump beautifulsoup4==4.14.3\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/15de4e6804e86503779e37c26a9a6d7bd5bcb58c", "html_url": "https://github.com/Tautulli/Tautulli/commit/15de4e6804e86503779e37c26a9a6d7bd5bcb58c", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/15de4e6804e86503779e37c26a9a6d7bd5bcb58c/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "48823c86b69ae7bcabaa33b8f667468914b70c1c", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/48823c86b69ae7bcabaa33b8f667468914b70c1c", "html_url": "https://github.com/Tautulli/Tautulli/commit/48823c86b69ae7bcabaa33b8f667468914b70c1c"}]}, {"sha": "12d88d3bd749e6a741a8537ac36c653012d932f6", "node_id": "C_kwDOAducuNoAKDEyZDg4ZDNiZDc0OWU2YTc0MWE4NTM3YWMzNmM2NTMwMTJkOTMyZjY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:10:10Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:32:48Z"}, "message": "Bump bleach==6.3.0", "tree": {"sha": "1b9b8e87bc41558747432f82980ec2147a7dc6e7", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/1b9b8e87bc41558747432f82980ec2147a7dc6e7"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/12d88d3bd749e6a741a8537ac36c653012d932f6", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/HJAACgkQm/TvTexB\nFjrzexAAlzmlEirItSeGJlJGK/sD+MsB8/ddS8fTjzBtljrFFoZSh6DcsU1Et6lf\n1138XMiePR4VMofQl0vM/vsMf9t8fVNOnCFwoKHnLsVVxnbXXCcwYyzFj2Te/cGo\nh6r8LH+rLbUXkZdyuLyQ3J1kWYzDspUSYl2seIdod8mLIjwnF2Mlm3jQouli5TeY\nuni3LLPY7VJn03+xbqEGKPEWhL2i5WiaA7Nue39lkZlkCLhbN4b+YBis8d6kBtES\nvw/q0wAYZX9EudUU1r8jdwVfGiiZOneplyoqj0oexUPFuksbm2ES1CF+1SdK5naw\nk7wKvhybsLYK7pRFJA4O9hyVNdAQoJZyR0rbFvKhBu4/uJVCUorCx4dv4R03iOS0\n/hzwUSN8cg1wWLKUI4zmrx7x7W2YW+NmrcR2Z2iO5qXBf+0AxfKmKXbU9gVFTwvw\n9TKf/aCcp4qH+jNBPJQT9CeCd5ag3HkTADDEhJ13GrwLpo/UOoyI9scYKtpBQv7v\nYr5qGnZ07JOfCxn/qyQMJpWvFCWC3vACRHaZ3Th/SLuuurr89BjLI6pTqIK0mq3O\nwrN+/Z6eNyTQ8aK0frMmRczO66F6TzYy1H8pzU6A4+pXzt8Q2SuqMcXDVbhBp1+J\npkzBWjawWSSo9IdtoJvRwhiHUvnq7/N3xSCKFe+Qhd2UpX+aXpI=\n=wrH2\n-----END PGP SIGNATURE-----", "payload": "tree 1b9b8e87bc41558747432f82980ec2147a7dc6e7\nparent 15de4e6804e86503779e37c26a9a6d7bd5bcb58c\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774131010 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132368 -0700\n\nBump bleach==6.3.0\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/12d88d3bd749e6a741a8537ac36c653012d932f6", "html_url": "https://github.com/Tautulli/Tautulli/commit/12d88d3bd749e6a741a8537ac36c653012d932f6", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/12d88d3bd749e6a741a8537ac36c653012d932f6/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "15de4e6804e86503779e37c26a9a6d7bd5bcb58c", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/15de4e6804e86503779e37c26a9a6d7bd5bcb58c", "html_url": "https://github.com/Tautulli/Tautulli/commit/15de4e6804e86503779e37c26a9a6d7bd5bcb58c"}]}, {"sha": "da8f64f50cb2b758a1a5a75e4faabff66e63d854", "node_id": "C_kwDOAducuNoAKGRhOGY2NGY1MGNiMmI3NThhMWE1YTc1ZTRmYWFiZmY2NmU2M2Q4NTQ", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:11:27Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:32:49Z"}, "message": "Bump certifi==2026.2.25", "tree": {"sha": "4550b49fdd3d6449ee36ba530a205392b8b3d683", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/4550b49fdd3d6449ee36ba530a205392b8b3d683"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/da8f64f50cb2b758a1a5a75e4faabff66e63d854", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/HJEACgkQm/TvTexB\nFjoj0g/7BegrMtTxP8NlIFrsJLtIxgJFjK6c76dgXw5b89Mla3wOoTzOt7nSCowd\nyb3+UstJBDzRGviQEcUlt6s1tTpwPADclE0QMu2J4MlgP7Kq2Ji2WdJn8T74ibJz\nvKJTxKIjdRIjD7QXFMNkmOOUZX1i1SqXgbDcqvGCScFq1m4K5xa0/0ZJ9FR7QunS\n3qna4YCc71N6Bl4LHwuJHQdLThx5No9UJUfiVkBTLDioweCMgsgwhv4V73VPOTGN\nOAi5te91sT9gxHwK606WU6Apom8LVZD4O9rjpx8nv7SivYlXbHcL0RgB8H2nnzUO\n9hz/wGWne3o75+HpKkx6NF8XWKwoYNbgJUFu9MEqs4y2ibJ4qNYREsbS5tuQ8dCL\nj/xuxQn1eGCRMlPQx3pXGUGUbTm+BSL1GE96bGNPswu/lA7Xam26Ua7w/sptOeYe\nkGwiGU6uc6/OVZPkrtxHvpHR/qHYRpJbiy7Lowu5NLNUmBA4vb0c4xZUv92BbGWW\nnIRnou9d/D0Z3LgnZmRlj3cwVSj0XEWpf1Q3qrOVZRDYEUxcaylR1CRWHV3wJ766\nniZJ8YMskHkpH4wTjf95qVVBw0NoCMpy7htbs9fKktC5MWYoCItMY+z2hIxlDDhl\nrvfQA8CvLclDhLX7asIwQAeYg2QN9+4MozaaDiMtHnHw+DMzgZE=\n=m9g2\n-----END PGP SIGNATURE-----", "payload": "tree 4550b49fdd3d6449ee36ba530a205392b8b3d683\nparent 12d88d3bd749e6a741a8537ac36c653012d932f6\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774131087 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132369 -0700\n\nBump certifi==2026.2.25\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/da8f64f50cb2b758a1a5a75e4faabff66e63d854", "html_url": "https://github.com/Tautulli/Tautulli/commit/da8f64f50cb2b758a1a5a75e4faabff66e63d854", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/da8f64f50cb2b758a1a5a75e4faabff66e63d854/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "12d88d3bd749e6a741a8537ac36c653012d932f6", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/12d88d3bd749e6a741a8537ac36c653012d932f6", "html_url": "https://github.com/Tautulli/Tautulli/commit/12d88d3bd749e6a741a8537ac36c653012d932f6"}]}, {"sha": "3c09b2194cb26d3b1b961a2d9051a3d9fb310f69", "node_id": "C_kwDOAducuNoAKDNjMDliMjE5NGNiMjZkM2IxYjk2MWEyZDkwNTFhM2Q5ZmIzMTBmNjk", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:12:42Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:32:49Z"}, "message": "Bump cheroot==11.1.2", "tree": {"sha": "8286b2c1e01341181c1933e6044c67bbe19b7790", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/8286b2c1e01341181c1933e6044c67bbe19b7790"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/3c09b2194cb26d3b1b961a2d9051a3d9fb310f69", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/HJEACgkQm/TvTexB\nFjovpRAAzlu86rGB9YrYQ+a853SXEIFIFetWNzyoFHU1P7V10/fp1qIfmVgX1LIH\nKKXyA1yvnp07Nan9nsu7KIkeKo35djVi8ZikVwP2MNs++koHO9HUiTzKYes0n6+L\n38yW8+41TpP21KG+v5O+VCCztPH/dhUWIj70aNJo6YyIuYEHAg3F3k89z3yrwIPv\n10QLhH8C31vcF+ri4XZaz/PNTVo6ilXgsDbUQT8JMrrdVU6a0GbpI7Ia8XcWvkiq\no9yy75iwj3y/zBCa2eMaZCjts6ThAjXNMrLsgO+BOdaaXqnUDLdUr+TQpYlHug0h\ntF/zsM2mxh/67IxXcv3JILJHaB9UP4yyEuYx+yqBy6wiGjSIV1uG/8MBN4KbcB1Q\nIGUKqCJBfYgm4t08KHnrbg9cy2FPGNnXHiXJEQ46iZ/JX/M5V0VZmmpTRtFQfeFC\nRQGdx7MQA6skXnclMYO3aP2vQGUMUH0eYWoFUmotRZEwGs+39NRJaY0loU+hn8xn\nFV9TxqNz9S5Ul9ynxXFj4xy00YWS0jtv0pFJV4AzbyayP2h6ftRrYXnWvc31DCqn\nhqVC8523gSMnZUzJjKKTUYfWSKx768X+c60ZSo2Sw75NnYxARD6BRBfu9d/8qIuD\nDC+CPXT0veLQDy8sTyx2gWvUb07RRLFVSmi4F5caFsxFgEB+KlA=\n=//xF\n-----END PGP SIGNATURE-----", "payload": "tree 8286b2c1e01341181c1933e6044c67bbe19b7790\nparent da8f64f50cb2b758a1a5a75e4faabff66e63d854\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774131162 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132369 -0700\n\nBump cheroot==11.1.2\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3c09b2194cb26d3b1b961a2d9051a3d9fb310f69", "html_url": "https://github.com/Tautulli/Tautulli/commit/3c09b2194cb26d3b1b961a2d9051a3d9fb310f69", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3c09b2194cb26d3b1b961a2d9051a3d9fb310f69/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "da8f64f50cb2b758a1a5a75e4faabff66e63d854", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/da8f64f50cb2b758a1a5a75e4faabff66e63d854", "html_url": "https://github.com/Tautulli/Tautulli/commit/da8f64f50cb2b758a1a5a75e4faabff66e63d854"}]}, {"sha": "dbfd7252e5f54abd4d12f5bcbb3d03b813874250", "node_id": "C_kwDOAducuNoAKGRiZmQ3MjUyZTVmNTRhYmQ0ZDEyZjViY2JiM2QwM2I4MTM4NzQyNTA", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:16:58Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:44:51Z"}, "message": "Remove portend requirement\n\nInstalled with cherrypy", "tree": {"sha": "1d9b2ea6017b997533927166a769c64947245ed0", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/1d9b2ea6017b997533927166a769c64947245ed0"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/dbfd7252e5f54abd4d12f5bcbb3d03b813874250", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/H2QACgkQm/TvTexB\nFjr0HA//dykADBsFz3EhQ683hLCafHdypWhVvFPh/J/P4wXoeOO31mzz434JzgLp\nrTZ9/eg+yahXyHSpco9VJ2MnE+xuyvx9pp538uwWKnRJM8um8pBrlPLMRkJg3kVw\nYRc9aF56ZxvKGm00ibySrF2r5tXUTqyfedKnBjxbzK32lTrjzGTQxF2gsuJRmrv9\n+g/1FQbVgC66onEl/q095KMBCwZYYUX8hnpidHRAMqqKuRpeuDjZZlUAR9qrUI7U\nzsiTOO20qNYH0FMoNae5qtNGufKtohR17+aEpP1vHDrSGS+emDZO59HUVH1vzGcd\nPeN/yJWhuc+bNSahCCggEEUKR3FvEdIVL4py6Vr/J2j39rd7aEEjIY5V03aS0cxs\nWGjZEVLmG1Y93/GLNX18u36cx1lkvmwp/peIzoFoW5a5SNp+8dnLdt651JTJMJtY\ndiXXWuE+6iwbBqZwPOjJYI6gsBBFmXTzaMYxQw6u1dvm2KCzIo2kZum+rGoyL4Aq\nxP9aP1kjg6ATKUljkvamRl6uZ6o4ZvLbPFUQvTRk+eddPbPUaOZXcPkcSd2Vqh+h\n4Jgq4SkmDxYXz4zBgaKS8MtPU+N5FmJspHHWfKjT5HJFYtjnvBaJco/kivM6TAII\nflbROu/dgcSHXhpkfe1vepDEoW29NeVAvYBp7tBp1dqSa2yEQS4=\n=/PQT\n-----END PGP SIGNATURE-----", "payload": "tree 1d9b2ea6017b997533927166a769c64947245ed0\nparent 3c09b2194cb26d3b1b961a2d9051a3d9fb310f69\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774131418 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133091 -0700\n\nRemove portend requirement\n\nInstalled with cherrypy", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/dbfd7252e5f54abd4d12f5bcbb3d03b813874250", "html_url": "https://github.com/Tautulli/Tautulli/commit/dbfd7252e5f54abd4d12f5bcbb3d03b813874250", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/dbfd7252e5f54abd4d12f5bcbb3d03b813874250/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "3c09b2194cb26d3b1b961a2d9051a3d9fb310f69", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/3c09b2194cb26d3b1b961a2d9051a3d9fb310f69", "html_url": "https://github.com/Tautulli/Tautulli/commit/3c09b2194cb26d3b1b961a2d9051a3d9fb310f69"}]}, {"sha": "e91236b797eebaca38b45875599f41665e9d3a8e", "node_id": "C_kwDOAducuNoAKGU5MTIzNmI3OTdlZWJhY2EzOGI0NTg3NTU5OWY0MTY2NWU5ZDNhOGU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:17:05Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:45:03Z"}, "message": "Remove tempora requirement\n\nInstalled with cherrypy", "tree": {"sha": "4928ea5e524fe8bd68f85807323ec8d9725c8cbf", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/4928ea5e524fe8bd68f85807323ec8d9725c8cbf"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/e91236b797eebaca38b45875599f41665e9d3a8e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/H3AACgkQm/TvTexB\nFjqsuQ//cb0eDiPfIumojoJf7Op0VksVFk884GeUVo8y8i0zmE7EfACuMmTOgPgF\nobW/BX+fwjLJtKZN/xS3jBa7jxpWPGP1uADwZSfFPesWhRY/v/Aypi0aJ/zuoQg/\ngxIHU1br6c+YRIBCa7+2gXzWfMcfGVxahknOipvQSmjR5ZOwifNGREcRNcuHj9bz\nMTMaNBQONaPvteZM7ZyRD8AN7afuyeb2uYK/9bSsrEnTIfGG3crDjq3SIgVJx6x+\nVKZODjymTsvvF6oyraZQILAc2vv71zxn6Epw/cHtsa6wFJ+ctV8i7YTZuPSz/EgJ\nqqo1EeQAPXKFUEDPjSPBwICp+NsitI9MNWibK/aQU0cghTkqKTYhe9rR/go4F5Bv\n4xMKQDXDduPoigPb0KZIupBQzwfsUqN5Xzly2yk5ZP1WZ2l3Gk9zXPpPyMc7BJiL\n249eASp0UaTbVAajHUpqmq9jCDXBj8oRy2Cn/vzkH1eEudZFG765Vg8EJ39ocMMd\nNMv7jhIxSwJXWsjNMODcpTVeVCkhIBkmADsHauaKbVJGfaPl1FBlaeXFQqLuexN7\nw7QlfFPZZ8mCI3ec6woBlG3REV6ryHXlojdAvr8MzL4WU0KpLRc0UjiQdgtuKD2q\ncHQbWzc8ijizmKvNI6A2bYHxy6lmuiH3MRlx5Sm9KybEE1mlssA=\n=dH+T\n-----END PGP SIGNATURE-----", "payload": "tree 4928ea5e524fe8bd68f85807323ec8d9725c8cbf\nparent dbfd7252e5f54abd4d12f5bcbb3d03b813874250\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774131425 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133103 -0700\n\nRemove tempora requirement\n\nInstalled with cherrypy", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e91236b797eebaca38b45875599f41665e9d3a8e", "html_url": "https://github.com/Tautulli/Tautulli/commit/e91236b797eebaca38b45875599f41665e9d3a8e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e91236b797eebaca38b45875599f41665e9d3a8e/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "dbfd7252e5f54abd4d12f5bcbb3d03b813874250", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/dbfd7252e5f54abd4d12f5bcbb3d03b813874250", "html_url": "https://github.com/Tautulli/Tautulli/commit/dbfd7252e5f54abd4d12f5bcbb3d03b813874250"}]}, {"sha": "f7ef6d23297de6c1d498161ef593bcdc84d3ce01", "node_id": "C_kwDOAducuNoAKGY3ZWY2ZDIzMjk3ZGU2YzFkNDk4MTYxZWY1OTNiY2RjODRkM2NlMDE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:18:41Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:45:04Z"}, "message": "Bump cloudinary==1.44.1", "tree": {"sha": "7cd61dc1092db541a1d07c8894c0a49ad48d4c20", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/7cd61dc1092db541a1d07c8894c0a49ad48d4c20"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/f7ef6d23297de6c1d498161ef593bcdc84d3ce01", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/H3AACgkQm/TvTexB\nFjooNxAAyKVZtq2D2Cu6HW8gnrIWDatBEyPrmD0XKNR6QGC4cU7nsTlaZ+32hdAU\nsCSFQf/auCXogYbBm8/L3D+bIaNIcTHMkYEoSWjGnmIQ6fgt+qmzNHmQbpFtW9Pe\nGRmtmKb430f93nBiNR2TkTAOX829c56zn/00D90B36qblP9g/4U4gDXF37A3q4AN\nb1pKsBLIu4rYFxVd6Kbp3lBCRlN/HT5Esp3RCpSRsh5MCG6/FbzJv6tzhmPcr4w7\ne3IfKKsaw23vpMrXVMldwXW4IEmhFO3hEV4cfWfbRPN2fzzThCEhrVek50BQuAmw\n+XvL/FVwB6rh5suUVIKnf4b/LzK2QBCJtK/qWUwswe2z4llNKjtUlEm3/H0Ew1wm\n/3urHhVTs+rgEelScmyqktI+xVIl8qGHgucf/CEynXAbvISaQoUGugnXTRuy+R+6\nbjr5G6+NL/2zz2ddph5m3MbfUYaOj5Dg5yItd8wu2D5CObVOaAQuEF2Dn1WMIg2a\nRnorymd+xvgwHSf3uVHTRQymn8y+nerZt0EtErqu9I1SNP0caFxsa/OLaTVOAR0q\njJ4R2RKYKUsJk4IINaF1bmJrfGAJrTxMlDqtSguRxmYn44Cn8sknuD+/BWe2pZPY\nCBVte335Lirdhttc1FfThEL80Nx1cWM+fLdMtHf1aBdE31+bC54=\n=JF6K\n-----END PGP SIGNATURE-----", "payload": "tree 7cd61dc1092db541a1d07c8894c0a49ad48d4c20\nparent e91236b797eebaca38b45875599f41665e9d3a8e\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774131521 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133104 -0700\n\nBump cloudinary==1.44.1\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f7ef6d23297de6c1d498161ef593bcdc84d3ce01", "html_url": "https://github.com/Tautulli/Tautulli/commit/f7ef6d23297de6c1d498161ef593bcdc84d3ce01", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f7ef6d23297de6c1d498161ef593bcdc84d3ce01/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "e91236b797eebaca38b45875599f41665e9d3a8e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e91236b797eebaca38b45875599f41665e9d3a8e", "html_url": "https://github.com/Tautulli/Tautulli/commit/e91236b797eebaca38b45875599f41665e9d3a8e"}]}, {"sha": "2cda2aa76022a5a0872737d03a8bfd2799a7aef6", "node_id": "C_kwDOAducuNoAKDJjZGEyYWE3NjAyMmE1YTA4NzI3MzdkMDNhOGJmZDI3OTlhN2FlZjY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:26:10Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:39Z"}, "message": "Remove future requirement", "tree": {"sha": "16eedf426e167fd508ed2ad49b2b452a09c371d2", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/16eedf426e167fd508ed2ad49b2b452a09c371d2"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/2cda2aa76022a5a0872737d03a8bfd2799a7aef6", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JecACgkQm/TvTexB\nFjoZ1Q//RivFx4+aiIgqSQWoCrBuIIpXKHcvT36Hj9XZN6KqBG2uYosFZdvPsLc3\nzsTtBx/cdS0FMkCgOZpMBdlApg1pOYM7/RIM99mTf57zxmemMAZ8eGi/hAvSgQm9\nosCRQjXP/Rv8+bI3SfyTfjX7FKaWM7mi3HnfTUX7v083hkCGYUT1nWWIwf6TZjn+\nPpKKtoUoQCORq/iULBuYwJzy93uLcKEPSOJ1P+nkrj1z0rFkRcXj83+77y0d4oU/\nAg0vLKrJNDdWuwdUoFDdIAmTbWCCMHkAIxSOmFe6E2Hit5WJjS0gHkbAMwMhmY7c\nFmda6RyU15CVEVdAaXzNpy8i5ounWnQFhJeLYTSM3Jp7z1mGpdMjufaRy47W9mdE\nWibWecXm5RukYk9zU0eS42pq/NbHOxLbod7EpSN22K4ZwkI2US48UwMjkDsVE0LZ\nP3wFucO/xOnRNc2J24hCzx887ulL5aezUbE8ZiVDT7U8Xb1JsL/5bp6v/hsRRC8d\nVNI9MJhdYVnN+mvWoic0yO2YPt8kgppjyrpfDqnBlLtVe1UgYGwwcxVT0h41CdEe\n7RPLecHLjZKUJUuwROpV/GTnmu+9WU+4oev5Nuiw5lAEl1zwxxNgcAdNo0Q8//bn\nC/6MLg1aAbMa8zg3SJ1+fvgtz8wQ8f3uG3XkCbr9jl6SfDJ8qCo=\n=LJCv\n-----END PGP SIGNATURE-----", "payload": "tree 16eedf426e167fd508ed2ad49b2b452a09c371d2\nparent f7ef6d23297de6c1d498161ef593bcdc84d3ce01\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774131970 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134759 -0700\n\nRemove future requirement\n\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2cda2aa76022a5a0872737d03a8bfd2799a7aef6", "html_url": "https://github.com/Tautulli/Tautulli/commit/2cda2aa76022a5a0872737d03a8bfd2799a7aef6", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2cda2aa76022a5a0872737d03a8bfd2799a7aef6/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "f7ef6d23297de6c1d498161ef593bcdc84d3ce01", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f7ef6d23297de6c1d498161ef593bcdc84d3ce01", "html_url": "https://github.com/Tautulli/Tautulli/commit/f7ef6d23297de6c1d498161ef593bcdc84d3ce01"}]}, {"sha": "cb1dd5cb2590776645acff1d2423814e2488e657", "node_id": "C_kwDOAducuNoAKGNiMWRkNWNiMjU5MDc3NjY0NWFjZmYxZDI0MjM4MTRlMjQ4OGU2NTc", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:27:41Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:39Z"}, "message": "Bump httpagentparser==1.9.9", "tree": {"sha": "1a3d7f9af8df800ae232d87faa6886c244ea463e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/1a3d7f9af8df800ae232d87faa6886c244ea463e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/cb1dd5cb2590776645acff1d2423814e2488e657", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JecACgkQm/TvTexB\nFjpExg/9GeBMv+BBeDy8xXEcgJ5GMzfIzvJc6UYcOhTr/BCEBM4/KuxrJPmuO0jm\naX0uuykD2Gr+smRju2qCdqGscD4/0Ej9RPlVL39lF8S0HiNqCmTqEUyJqAaq0zwM\nDwQ21YiVSMLFwmJaD9y79HgloKPzS2X+LitGJqJVP1NQvifx0e++In+0FJ3nCBvt\nWSp5WW74/X9TdJfYZQsrjmuYL7rJ10BKfHHQWW8NK8UIXIkhgZlN8//chHu9fcML\nCh14/eDbkPnzZIZa11xzFIfs+ZQvaNrHJC5+H8aRdh6UGqTJxL/6vpFnSsnmdaCM\n3qDAExw/PjxMgggDKFD0RkMvocPFhuBuSANse8Vgrcp5FqwgmtkeO6x+m2GmR5YN\nbxEAy/SvYT2/YUt+p8GnjpMVepcAO5BMJVcnaVZp/NLjb0p/4TGTdDqhT6tk5+Sl\n+N0OEVxWwv6ZGq1EWtYI2tO3RaYg3QIspSQQmjV/ahcgqiiBXy9sLPDuTi0OM/J7\nLU/dtDwuHoMgHUr7CmYKZnF4xHj3AmvELR7emRPmphD6bzA9TzODIVnCl7l1gAGI\nb5OmC1108thfa//xBNt7zapn58CkUPG5LHdd6qHf00+pr0Z/NlBfGQ70L7wQD6O4\n4QzkbLkMxKVGGUi4h1eKbc8IqpYXia4Axiw4iws4+rFVTIxVgp4=\n=8e/2\n-----END PGP SIGNATURE-----", "payload": "tree 1a3d7f9af8df800ae232d87faa6886c244ea463e\nparent 2cda2aa76022a5a0872737d03a8bfd2799a7aef6\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132061 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134759 -0700\n\nBump httpagentparser==1.9.9\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cb1dd5cb2590776645acff1d2423814e2488e657", "html_url": "https://github.com/Tautulli/Tautulli/commit/cb1dd5cb2590776645acff1d2423814e2488e657", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cb1dd5cb2590776645acff1d2423814e2488e657/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "2cda2aa76022a5a0872737d03a8bfd2799a7aef6", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/2cda2aa76022a5a0872737d03a8bfd2799a7aef6", "html_url": "https://github.com/Tautulli/Tautulli/commit/2cda2aa76022a5a0872737d03a8bfd2799a7aef6"}]}, {"sha": "b6108d632bbdb09fcc94a2a907baf2d8c313f0af", "node_id": "C_kwDOAducuNoAKGI2MTA4ZDYzMmJiZGIwOWZjYzk0YTJhOTA3YmFmMmQ4YzMxM2YwYWY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:31:32Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:39Z"}, "message": "Remove packaging requirement", "tree": {"sha": "9ea46931696d9c0d9c1110e181ffd2fdaafa7a95", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/9ea46931696d9c0d9c1110e181ffd2fdaafa7a95"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/b6108d632bbdb09fcc94a2a907baf2d8c313f0af", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JegACgkQm/TvTexB\nFjppMw//aGaeISHKTN6l2jnzSafGIJx6XJyjSXI41k9gWW6JJNLVEoyeeK3dJ6JS\nvVIsn0PoEw7ZwdVapEkznbBmJ92RYg4HpPZVPkxhnwTp7MWK29vuzGVSxDlZxo4b\nm/MqYOm6+cubUESfnwU1+rdt+4cRgEESLNrQVfdbwJdaPk0HCnk7Oh32XwWU1fsd\nImhSeGupwLjIB2nCh7xfo8W81ZAt4mtkdcGDCKbOaAnU9HWZVUlvz+4wcgLlqJk9\nCRtFuXQ3h96e8K1rMdfWZ9cyiuI6bMWg9HTVmhnotBQreW8FiS8HOoSUi3oxMMcF\nIzjrlei1p3DHLVlv1vrjeVL/tvH2AeLEOFw/8dgYiYA0KoE3qYOl6IJI5V2qJGq7\n0TjyMxpS7sKyE6FYQEETHKOQ4k5iUBa9rbf+P50B+kBxf1wivP0CaIc2HYtIf0d8\nw79ZO+IydbyOFxdq4If/ofq94/7Em8RkscShtz1EDN12871rSADT8vxYU339l60n\n9LJ8gsQItBCm37BJnUFl6EtgSxcSSIoq8Nv+jk/iRDiZreYV+r+bD+2PEBsiNSuX\noVhdIJ05gCcyAfiHDKJ1/vWWqQyUPoT7KP93icSmANUOlAOs+HLPSbOkeRBHkVQC\nS8KhAjXP5jqH1DuVZXcjMUKjR7LOKKSWq2wAVporHZTQ9+8MRUU=\n=OvK7\n-----END PGP SIGNATURE-----", "payload": "tree 9ea46931696d9c0d9c1110e181ffd2fdaafa7a95\nparent cb1dd5cb2590776645acff1d2423814e2488e657\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132292 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134759 -0700\n\nRemove packaging requirement\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b6108d632bbdb09fcc94a2a907baf2d8c313f0af", "html_url": "https://github.com/Tautulli/Tautulli/commit/b6108d632bbdb09fcc94a2a907baf2d8c313f0af", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b6108d632bbdb09fcc94a2a907baf2d8c313f0af/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "cb1dd5cb2590776645acff1d2423814e2488e657", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cb1dd5cb2590776645acff1d2423814e2488e657", "html_url": "https://github.com/Tautulli/Tautulli/commit/cb1dd5cb2590776645acff1d2423814e2488e657"}]}, {"sha": "6606e95a314c3d83318fe152102ac5659a09bf6b", "node_id": "C_kwDOAducuNoAKDY2MDZlOTVhMzE0YzNkODMzMThmZTE1MjEwMmFjNTY1OWEwOWJmNmI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:33:54Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:40Z"}, "message": "Bump platformdirs==4.9.4", "tree": {"sha": "c49042e8795da1ce3a7f6b1d298e818fa7169a6a", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/c49042e8795da1ce3a7f6b1d298e818fa7169a6a"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/6606e95a314c3d83318fe152102ac5659a09bf6b", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JegACgkQm/TvTexB\nFjoR5Q//b15U4qTaObNBFur/TAVfhOSDZ0KSgoqQeCpj1Yisurp3J/QHV0rQndvC\nUdCp1STrMw7HLYUaltQ56ZIR3WKePMEOaUkf1v4HGluUpU4Z006ZgWtQDRC+eHNL\nQSZXQMOuI25IKVksBer9vl8cPIEZreFw05wE8h6hDeFkS+9vLPhnOGU6OiNGQleP\n0uujaR3QC3bMw9aivVai1LReUWqFAYRqVJSVb8k3G4Kszt0l2N84rjgVSFvQ0Vvz\n7Dng1P+N3pT/KeBLBiLFgJL3sTEpIWL7mMtAtwasDGwvJX89wOqhukiU1/wkEg5z\nFPAmeDrkD6mhNsBMEJKuVKh8VRPbLd2b58PPOORLTDdWVAC9B7CQiI75fqsjok/C\nzIIQXQ0t1Y1KGxLeYKXAW36TjCAqqxIwK8lunbHSVIUHAq33sioYBHw2G3laZqnH\nUgaignMU3OXBrM/U2UN0TLuPhxgIUj5pmUww2u2pDobwYOSnPkDFwbo9S2bRxwSy\nQ3PAlo0kEUrh7FOB4L76SaCPl8xS1oWSKo3F/CMDQGn2sDhsMCdWF+RZ1gGcSJ1J\n1HQLiZb4wZp7WGOGrwJNwe86Ro2Hhmuu/jJ1YsEMlv5b64Gitx9AuLWc7WX8v4hB\nEabgaLEbsSTDx5ll9ssJtJgBhEmtwTUAGPzRLikg14o8/xpOKek=\n=laT9\n-----END PGP SIGNATURE-----", "payload": "tree c49042e8795da1ce3a7f6b1d298e818fa7169a6a\nparent b6108d632bbdb09fcc94a2a907baf2d8c313f0af\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132434 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134760 -0700\n\nBump platformdirs==4.9.4\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6606e95a314c3d83318fe152102ac5659a09bf6b", "html_url": "https://github.com/Tautulli/Tautulli/commit/6606e95a314c3d83318fe152102ac5659a09bf6b", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6606e95a314c3d83318fe152102ac5659a09bf6b/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "b6108d632bbdb09fcc94a2a907baf2d8c313f0af", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/b6108d632bbdb09fcc94a2a907baf2d8c313f0af", "html_url": "https://github.com/Tautulli/Tautulli/commit/b6108d632bbdb09fcc94a2a907baf2d8c313f0af"}]}, {"sha": "25f25a0f4a7536f1cac5ec0112ded966efea87be", "node_id": "C_kwDOAducuNoAKDI1ZjI1YTBmNGE3NTM2ZjFjYWM1ZWMwMTEyZGVkOTY2ZWZlYTg3YmU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:35:07Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:40Z"}, "message": "Bump plexapi==4.18.0", "tree": {"sha": "5e6a7c55b756cfb1af912870bc509487045c59b6", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/5e6a7c55b756cfb1af912870bc509487045c59b6"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/25f25a0f4a7536f1cac5ec0112ded966efea87be", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JegACgkQm/TvTexB\nFjpknBAArT/Mif4qm2uj0Nbnwq2rJihfIWUHHfbOt1Jth+9dhU/nYojxY6WZS/eP\nEyb5PgMD5uGf0Iw5ye73iOW9badnlciLOu3m2pzy1rFLJDYsdrNuvRZeEwcNbgzp\n7mx2nh/IZKHGLHz1MzU5QKq+NQNBc40b/UZk82g4UAY8QS+0M6tYjV3gEH7/E4oe\nuzmNQD1U59elBKgJ79wjvahnF2PhKcCVI3SHaXn7YJ/lcv8aN+LwvQzhioNV5YxV\n5dJxsVnPWOYwxqmvulkJHQWVULA/ZQgg4aImB6Q1w4KUGhBWWEwORwD8GvaibVp3\nzCZinW/FYtrC9zEGaF1mz8t/4cjF93XCDDXoYEQf1/QbobyfBQvugop7m00QlD5r\nDM3G4CShVoCpYIgo2ENV5fUjeK/YdhPmhiNNRoVErovXAU5CQLK+ipUplnWxcpGW\nfSrSvB4MZyfxac6rwGwscoQtuwrBc5OJI7coyuPTRxNYDoyzBYFQVW8NOm8im1Ul\nqfsZ/brGm3kr6d+FuoM1SJpT59cNbZsikm00TTV8unALkxgXDwLpSOg+/YDbo8ao\nUn6VzREm7uQ0ZXVH+Yi/gnvFghJWKz7WxEb+wDjacydGYSP1LQAErOlaxoYVb+im\nUmVhfEcCgfJra5rhmveZWM2mkUxBLr9rolPKbe2fYfk6bQmxM3A=\n=ECCc\n-----END PGP SIGNATURE-----", "payload": "tree 5e6a7c55b756cfb1af912870bc509487045c59b6\nparent 6606e95a314c3d83318fe152102ac5659a09bf6b\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132507 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134760 -0700\n\nBump plexapi==4.18.0\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/25f25a0f4a7536f1cac5ec0112ded966efea87be", "html_url": "https://github.com/Tautulli/Tautulli/commit/25f25a0f4a7536f1cac5ec0112ded966efea87be", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/25f25a0f4a7536f1cac5ec0112ded966efea87be/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "6606e95a314c3d83318fe152102ac5659a09bf6b", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6606e95a314c3d83318fe152102ac5659a09bf6b", "html_url": "https://github.com/Tautulli/Tautulli/commit/6606e95a314c3d83318fe152102ac5659a09bf6b"}]}, {"sha": "03dc87599c10171d62bfbc48a6528dab12aeedf4", "node_id": "C_kwDOAducuNoAKDAzZGM4NzU5OWMxMDE3MWQ2MmJmYmM0OGE2NTI4ZGFiMTJhZWVkZjQ", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:41:35Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:41Z"}, "message": "Remove simplejson requirement", "tree": {"sha": "7c9060ca5b4dd72390439699052d8b2e1e056626", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/7c9060ca5b4dd72390439699052d8b2e1e056626"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/03dc87599c10171d62bfbc48a6528dab12aeedf4", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JekACgkQm/TvTexB\nFjoEew/8CABOUnKUbmBuDg+T8IhQCzqbaC9/Aq5EqcHjaI6gE1NxzFuUOHDQq9Ga\nivIlHcRyBJJl846DSJsQGNyynm6DMjQ/TloogRnUlLTiSRY0/fnGKsD7fMgvIOwq\n1a6NTVAMudHgHn39x+3GKWlWgxcyAf78gi/WHtnjYYd3idQkLEudanzCQxfog/hR\nCTNwHaqawmeW95CCSgkQC0ZrTomVDq7OwXXT8DpUc3B/ePDuAcVkFzBTSiPd3NUS\nSc6scq5sAbqsSYA6n5eF+WhOcKsAQTLFcHUUM+u7oODkZ0lDUxXQA5rqYw2WaskJ\nFn4C6Ah6hbvx03C8wgfU6XCV5PHVfceA+a/8I0LfNslHbY2VVYaVApfN4aNY/AiM\nP+msUO0tR5PvP9n0PT+fesDAK1r7sNaJAaKVTKA9alNBA0BvZxO6oMTOh0K3rvbn\nJ3JDgT6OBHAGfMDRKVY5/Whaa2l903xomWflImBorhoFvsEc+nVxC46AfbE1QwrV\nHps6u4Z3GHYG+cIVdIuPiOKd5lg9zURFgpfdA7t10rimw7pxI2KcZ1Nu5+TXvWE/\nvAzrtRKxgASJxyaSbIddCK3SZVWwusDkFwkoYbEgrpq8RIbwAED4qeBEYmnSWsV0\n+L7fYekOpljSPKfkHtzzsK9bL5Wxdo3vMwocemphj56+Tf3OJfo=\n=DzPX\n-----END PGP SIGNATURE-----", "payload": "tree 7c9060ca5b4dd72390439699052d8b2e1e056626\nparent 25f25a0f4a7536f1cac5ec0112ded966efea87be\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774132895 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134761 -0700\n\nRemove simplejson requirement\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/03dc87599c10171d62bfbc48a6528dab12aeedf4", "html_url": "https://github.com/Tautulli/Tautulli/commit/03dc87599c10171d62bfbc48a6528dab12aeedf4", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/03dc87599c10171d62bfbc48a6528dab12aeedf4/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "25f25a0f4a7536f1cac5ec0112ded966efea87be", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/25f25a0f4a7536f1cac5ec0112ded966efea87be", "html_url": "https://github.com/Tautulli/Tautulli/commit/25f25a0f4a7536f1cac5ec0112ded966efea87be"}]}, {"sha": "bfe1c17c91e1187dc67a736fef9cc721cd7b910b", "node_id": "C_kwDOAducuNoAKGJmZTFjMTdjOTFlMTE4N2RjNjdhNzM2ZmVmOWNjNzIxY2Q3YjkxMGI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:43:20Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:41Z"}, "message": "Remove tokenize-rt requirement", "tree": {"sha": "721aec85d3586155a8b04f5879e54ead65b0d2ec", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/721aec85d3586155a8b04f5879e54ead65b0d2ec"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/bfe1c17c91e1187dc67a736fef9cc721cd7b910b", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JekACgkQm/TvTexB\nFjoK0hAAsVRo1HzJT4tjr5m2lx82/+dNy1hD074dtj5o5vkl5DDw2Edxtg6yjC21\nWpKsoIzuz/1w2VXIZk0B9Q/j3L1VIBKpLbSvyGQC2T7TpzAVJFbwJCRBocSK8C7x\nyXMLejYvFsBFdXHwQQ04wgoYPSXHROO0c/kgetji1iuXmNYP1R0UXQ0HgXgSqMSd\n1wYtJKNEGDUizdvhf6cNR5UoYUnXosWg6pIFjhP9tEYkUvRqPqmUX/J5ChKqtJjB\nxZv2S7i3HqpRjv6e2Odjn3M8WgX3TlYclrfOH/jSLtCLinqRrVbA+ZTRndRRCege\neimmtVYSXOhJ22zwtpqkmZR9eafEt4EpY3ciB51huJaSjYWEnhXzyP1ShBqD0xZd\nGM/wSlib6S5w6A4JhAK+N4/75SqhGJ5satI2nOvkM4QEx6yNzb89rJeJJcfyTHo8\nFCN6CCmjBk6BPlTfTk8NeorUSuerSbUT+HjizFO5F9tMDVI4th+uZAZX1VmQf8dF\n41CLRkIdCDvGZrU8cjzaEsidJXznb4b3Ce+p0u38ma646/E9OMumWUntwQsWJyvm\nzNYUcZiDG6/ZLaFmXd0NXOV5pwwCGahtwlOiRTrQ1qXqSm72cT4C5P3rY1qoaVHQ\nqvXGm6gWe5CrcFPLCaHMSMsbcPaEQfh3Zg3FeP9fPAODKGP2O9U=\n=umbX\n-----END PGP SIGNATURE-----", "payload": "tree 721aec85d3586155a8b04f5879e54ead65b0d2ec\nparent 03dc87599c10171d62bfbc48a6528dab12aeedf4\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133000 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134761 -0700\n\nRemove tokenize-rt requirement\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/bfe1c17c91e1187dc67a736fef9cc721cd7b910b", "html_url": "https://github.com/Tautulli/Tautulli/commit/bfe1c17c91e1187dc67a736fef9cc721cd7b910b", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/bfe1c17c91e1187dc67a736fef9cc721cd7b910b/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "03dc87599c10171d62bfbc48a6528dab12aeedf4", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/03dc87599c10171d62bfbc48a6528dab12aeedf4", "html_url": "https://github.com/Tautulli/Tautulli/commit/03dc87599c10171d62bfbc48a6528dab12aeedf4"}]}, {"sha": "7a1f9f5fd94b62a1118037c80d62fa46543587bc", "node_id": "C_kwDOAducuNoAKDdhMWY5ZjVmZDk0YjYyYTExMTgwMzdjODBkNjJmYTQ2NTQzNTg3YmM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:44:24Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:41Z"}, "message": "Remove webencodings requirement\n\nInstalled with html5lib", "tree": {"sha": "d86a0ce5c829b8306134fe811ca279accaf39405", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/d86a0ce5c829b8306134fe811ca279accaf39405"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/7a1f9f5fd94b62a1118037c80d62fa46543587bc", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JeoACgkQm/TvTexB\nFjrDixAAq7mLQoWQ7Smrw6vXDrqfRvcamGG5StVZgJIINW0z7yPFqzHZshXn4dxI\no6PTXdiku05r2uOL+PB+he1vGDluZ+BJfs+6PWaFv9hDaWqD8q1nTkSL2XL3LYk4\nwOhi/a7WKT85ns8rL04ftF/DKMmTtTrTficgYk/U/EEbJ0s3I2I6xHpXMV0CpSFS\nf6o1ZT9CZAIEmn4I7rPjOdUY3p5UaAbKOHLNJuuxjTYY3J5dNQuYRFCwN3f3D/Td\nKuAIzAAdIoKX3XAqg6+JQ1BjTJGKoQdP2hz6Vum8Lez1wtkBaxMr5otWPendGTIb\nJfOYbNBAB/gkzbFtCLCe69u/gZn+1L+kjnKnY2GVsDOHkowe+2pM3ZW6eqqzf7PL\nqOD0igogQhwSFrdVRQRKCVqSC5PheQYxPLBxUrVpUVMV+/7pdzRRQxRyjJij3yd9\nMbAsEsGwcDO0CFlNq8OsTEiEdCBECICAoyL9jn1JNvpo++zjqhaDqh/im9skueX7\nW/mE/lw7t6yWhQvnkEYYOiB0yTqDLKYz5eqOlENl94U/4fockNXt7hZZ3KzQDHG/\nSGNFsu2qpVFBwUjJJdvSBN9ey+35gA44Q6S0N67cNw4vcd+pCtLSa2zpUg3tkDk8\nWMd9NNjx+cC+aGPUszW9A8TzAOqgYuF8ipOmI6nK03juCHEXFbs=\n=R8Pm\n-----END PGP SIGNATURE-----", "payload": "tree d86a0ce5c829b8306134fe811ca279accaf39405\nparent bfe1c17c91e1187dc67a736fef9cc721cd7b910b\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133064 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134761 -0700\n\nRemove webencodings requirement\n\nInstalled with html5lib\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7a1f9f5fd94b62a1118037c80d62fa46543587bc", "html_url": "https://github.com/Tautulli/Tautulli/commit/7a1f9f5fd94b62a1118037c80d62fa46543587bc", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7a1f9f5fd94b62a1118037c80d62fa46543587bc/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "bfe1c17c91e1187dc67a736fef9cc721cd7b910b", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/bfe1c17c91e1187dc67a736fef9cc721cd7b910b", "html_url": "https://github.com/Tautulli/Tautulli/commit/bfe1c17c91e1187dc67a736fef9cc721cd7b910b"}]}, {"sha": "67a5c16699902b553495fd78dbe08266eeeb0aa3", "node_id": "C_kwDOAducuNoAKDY3YTVjMTY2OTk5MDJiNTUzNDk1ZmQ3OGRiZTA4MjY2ZWVlYjBhYTM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:45:59Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:42Z"}, "message": "Bump websocket-client==1.9.0", "tree": {"sha": "bb9146af2320375f4ef5890062abee571053a967", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/bb9146af2320375f4ef5890062abee571053a967"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/67a5c16699902b553495fd78dbe08266eeeb0aa3", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JeoACgkQm/TvTexB\nFjrfAhAAz80QLs74nS6SX2S+3oMHM6nKjOkEYp/Q/AdCHVuXV2WbqZ2dT+Do8PXV\ntKeDA8lFOw+z12iO5aVtfzu2iCjdhYuyZxMQdohFXhsmm9UmkgPpYUPqQbkZ39AO\nRnIrykRmqN+RH/gutRu1DImTE4mNBQQXrISPP5lYDsI0zTqSGFq+CEbpycrS69PE\ni8F0vq1D42L6VgQmL0m4jSigNZYf4A318F7dlCbB+7IVAR3VofkBhHPoQEtF7ksC\ng2ENm7Lu1MySNcjOMtDvr/iAQUP2quF/6Hd6Bm+dM4KwOlTMtFm5PQx+4GmL7dn8\n9JZ8bvyf1J/iMq/ukkLeRtVvhzfYiM7TZPgH/De/vbOsSdK8casbKU5tlY8zLPIB\neP9q4DSJIYbeKV4MrSqEh+w6EutzgkxO3FeWKPK+fhFQq2vrDfBzkbxsj4SfLdc2\n8O0auyoRvjBTseSoirnO9IlPAUSPwpDJLqdURb2oVKhhJv4JMBClw+/ao7eKYiRz\nxSDM7wrDfti522g8Ap/DYGjNVjO4J3l7it3a/G3EgXGbDZu81Pavh31fgOmIlC1M\nA6vmAjseFcC1muoxKpyDGjxip7sT6/TvJgaRNAuhNH3vPemJRvtPH+99q8NHKl59\n+LALrTzDAVWJjpFFqBNm6l9T/kwi0cqfoAqoIoYoET3+IhXC8N8=\n=0NJU\n-----END PGP SIGNATURE-----", "payload": "tree bb9146af2320375f4ef5890062abee571053a967\nparent 7a1f9f5fd94b62a1118037c80d62fa46543587bc\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133159 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134762 -0700\n\nBump websocket-client==1.9.0\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/67a5c16699902b553495fd78dbe08266eeeb0aa3", "html_url": "https://github.com/Tautulli/Tautulli/commit/67a5c16699902b553495fd78dbe08266eeeb0aa3", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/67a5c16699902b553495fd78dbe08266eeeb0aa3/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "7a1f9f5fd94b62a1118037c80d62fa46543587bc", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7a1f9f5fd94b62a1118037c80d62fa46543587bc", "html_url": "https://github.com/Tautulli/Tautulli/commit/7a1f9f5fd94b62a1118037c80d62fa46543587bc"}]}, {"sha": "a42c3b08ce9a3c1d1cee9e20250ec370aabfe0ab", "node_id": "C_kwDOAducuNoAKGE0MmMzYjA4Y2U5YTNjMWQxY2VlOWUyMDI1MGVjMzcwYWFiZmUwYWI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:47:19Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:42Z"}, "message": "Bump xmltodict==1.0.4", "tree": {"sha": "80adaacef469e9ba12c64d66ce2057dde45ef39b", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/80adaacef469e9ba12c64d66ce2057dde45ef39b"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/a42c3b08ce9a3c1d1cee9e20250ec370aabfe0ab", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JeoACgkQm/TvTexB\nFjq7ig//TJmwc7KATlJXjAGSePb+f+jfUg6RUrIWq+6EQwIGPfbEfhz4Uv6Unl8X\nWaOvSejXC5H14W4g2VeNfgTl9si6A5XIevFBsQ3UP9Tjb8Xniw8z+w+bBJCDdlSp\nkQEaWSYYvkGFzcFfzotULNW2GrHpJrA5fzrRBGF+7z5mafwtI2fyOc6EVS2hA+Nk\n/Qx1bVaT9BOTmEcvN1R5oRj3gNyL4FTtasoRKTeH8Wb5SlSeGC6SdegZK29uZQ2L\nnCHBzhn3Jy/4va376XNeqc7kWqrpe+HxJ27hkHEFGj8p3pPQNYCYCGLwfnwEIWbF\n5tyy8JGf5j+xuMDEDf/CBRVQZySBtlehqX3I0OPGdcO4hancqNy/+t1kbbpnppb2\n57bf3JOIQ9KTbD69EM3+psF0Q3h4M0wLkVQ8obtimvmZslCKgy70R6arDuFVARbu\nc9v6Ia2rNZLRTfqw6t1sndOZwK0HtijCNHoq1wbM9uaIh9Dp4eWKC8PYdmzLiCJd\ni+QYIf9xYAwyZ27rdiyTr0HOgzIl7dHg5lQryi6HvU5mcjlXLdNPuoeAIZuPz0DW\nbg1Cr8HSXyvZdtyBROFxLLPUi0NrYyFb7JMjPX8ycp6Hk7jqREYSooGICj9z68WO\nNadr02KR5M9WIHcC0aFZdw8sJpPCFIJDU1UCgQOOu1pqsfFqDLQ=\n=8pxk\n-----END PGP SIGNATURE-----", "payload": "tree 80adaacef469e9ba12c64d66ce2057dde45ef39b\nparent 67a5c16699902b553495fd78dbe08266eeeb0aa3\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133239 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134762 -0700\n\nBump xmltodict==1.0.4\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a42c3b08ce9a3c1d1cee9e20250ec370aabfe0ab", "html_url": "https://github.com/Tautulli/Tautulli/commit/a42c3b08ce9a3c1d1cee9e20250ec370aabfe0ab", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a42c3b08ce9a3c1d1cee9e20250ec370aabfe0ab/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "67a5c16699902b553495fd78dbe08266eeeb0aa3", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/67a5c16699902b553495fd78dbe08266eeeb0aa3", "html_url": "https://github.com/Tautulli/Tautulli/commit/67a5c16699902b553495fd78dbe08266eeeb0aa3"}]}, {"sha": "d574cef36aba9f55982c30856e6eb9bb12619538", "node_id": "C_kwDOAducuNoAKGQ1NzRjZWYzNmFiYTlmNTU5ODJjMzA4NTZlNmViOWJiMTI2MTk1Mzg", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:47:47Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:43Z"}, "message": "Remove zipp requirement\n\nInstalled with importlib-metadata", "tree": {"sha": "48bdcfed33abc65e9a3878ce17d171d111aa4205", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/48bdcfed33abc65e9a3878ce17d171d111aa4205"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/d574cef36aba9f55982c30856e6eb9bb12619538", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JesACgkQm/TvTexB\nFjpI1BAAtCQOIOeyaf+yvfQdNDfdymJPG4tW6H4owXjJQLcYVA+DZM6kjQ8Z/lxo\nqybY6BziYFNSc0o6e7dS+T++SLC2pbYh5P5Z60y8QjmEWUTzxFj62FmZQmQ75Zc1\nzZ2K540LFW39g2L1CXGL+x+LV1TGnC6LMsuGey6nF2Wob9UV4A4wMphYFwRgY7vM\nTZk522Tp6DvHI5L5HZcKoVglzypwcF1oOzW1G5ekY9KxOciTXbchYthGbLVJJHiD\nbEYakN3K3ieq31hX95smHi3gd7/Hrn7AhBrLnyOTlvgjMdM+rgiRC77xr/M9Hej2\nJTUPkyytzfCso0MgdPCLKMS1frB6RZtONLTG0issd3rFrAW7jMUAXXFDP2l5oihE\nwCRazlPqLljiEyVLsCMx0pJx7nvaWRJ1DFuqhQBATcmFZTqD3UVJLcqekS76tC0O\n+hNiaekkKX7l8xKaU3LXRhmtf/WjfBE2PLWWFfQ4q+xdTPxxSjOECAe8A4rbPy9Q\nTphhpemtSz84UxnJwwTp6gLzWJBWIgtjYz0rRQqKEBgg2/Zuu/Wqg1yRXKPwHiEE\nNSDpo93KMNJOqIVYlEWsUmrTjSQm6SuWc6Mjps4kU13XvW3GbSfmYoWT//NrATx7\nmCqSWQ/2376YobetOuOYAjfGyQ05ga9ondJqpg87UjYp0sjL/ls=\n=9k2J\n-----END PGP SIGNATURE-----", "payload": "tree 48bdcfed33abc65e9a3878ce17d171d111aa4205\nparent a42c3b08ce9a3c1d1cee9e20250ec370aabfe0ab\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133267 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134763 -0700\n\nRemove zipp requirement\n\nInstalled with importlib-metadata\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d574cef36aba9f55982c30856e6eb9bb12619538", "html_url": "https://github.com/Tautulli/Tautulli/commit/d574cef36aba9f55982c30856e6eb9bb12619538", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d574cef36aba9f55982c30856e6eb9bb12619538/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "a42c3b08ce9a3c1d1cee9e20250ec370aabfe0ab", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/a42c3b08ce9a3c1d1cee9e20250ec370aabfe0ab", "html_url": "https://github.com/Tautulli/Tautulli/commit/a42c3b08ce9a3c1d1cee9e20250ec370aabfe0ab"}]}, {"sha": "00d1530c2b715f954761d5757f5108dc4c82d954", "node_id": "C_kwDOAducuNoAKDAwZDE1MzBjMmI3MTVmOTU0NzYxZDU3NTdmNTEwOGRjNGM4MmQ5NTQ", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:53:29Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:43Z"}, "message": "Bump requests==2.32.5", "tree": {"sha": "9aefab35f5e6360732158bc489ee47273abee5da", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/9aefab35f5e6360732158bc489ee47273abee5da"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/00d1530c2b715f954761d5757f5108dc4c82d954", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JesACgkQm/TvTexB\nFjptGBAAqn2JuW2cz5hMrXYlLL5F/PzObiLYf1wagmn5tfI1XBoSy2OJkK7FtJkO\nktcsEwxnOx45EM1Nuf+lnYxB+jNCqvHDAXC2K2vP4kPkPaC5dwv6Bp3/li2/lU62\nkIpf6uBiMpdAIYwDf53HI+dJ4JMq5hjxecyFVgTnNbAkGvmzn/QlK5wTkzXa+9xq\nx40niJmO4sF/YJy+ncMHaZ62mCYWI2YDtqfpzUOjfBVlFEGrHU1ZaJIoOFK6E3Bc\noqLlriixAEW/wzlpyfDp9FZF/W+zsVPQzIeyKMv53lNG9j+jiPB1JkAV35Ra9nCV\nHrbYc7XWhuMMBV6/VZXtKb0HmITPOqlc/NyQDa1O2QUzYubXxLeRjTw9E0Z12PvN\nbmMMPo0zy+TPQPnVHE+UGvqqAguvwcOBa5Ofyeper9wT2JW0cOmMtw1pRcqnDste\n4JNAZHlVW7EhKcyf/YrSs3avggre42zsl6HB4WxoZtSVXlzhLv25Wj8CEsdAZRsb\nQCLoxaQrOzjs5ozguNvFjLbEi9VAfbVK/IOSIq+Sc8RjIv/kgSddTd3b0PiZjpTP\nfYwlooj+pLsBqOtd3UOsgL2souLXXoRSOws0ajTwn4gUfxTi8z7PKcNt116fZ1Ql\nWlG6JSlURHB+53Gr4/sVZnC6B3jKOmHrL/FzMXc2lFM8kCrKKXA=\n=nAr1\n-----END PGP SIGNATURE-----", "payload": "tree 9aefab35f5e6360732158bc489ee47273abee5da\nparent d574cef36aba9f55982c30856e6eb9bb12619538\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133609 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134763 -0700\n\nBump requests==2.32.5\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/00d1530c2b715f954761d5757f5108dc4c82d954", "html_url": "https://github.com/Tautulli/Tautulli/commit/00d1530c2b715f954761d5757f5108dc4c82d954", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/00d1530c2b715f954761d5757f5108dc4c82d954/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "d574cef36aba9f55982c30856e6eb9bb12619538", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d574cef36aba9f55982c30856e6eb9bb12619538", "html_url": "https://github.com/Tautulli/Tautulli/commit/d574cef36aba9f55982c30856e6eb9bb12619538"}]}, {"sha": "76190ad6c6d1fce1f194a62ea763552b6126fc19", "node_id": "C_kwDOAducuNoAKDc2MTkwYWQ2YzZkMWZjZTFmMTk0YTYyZWE3NjM1NTJiNjEyNmZjMTk", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T22:55:11Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:44Z"}, "message": "Remove requests-oauthlib requirement\n\nInstalled with python-twitter", "tree": {"sha": "1df05815ee307b6a53d64016092cc8e7767e2402", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/1df05815ee307b6a53d64016092cc8e7767e2402"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/76190ad6c6d1fce1f194a62ea763552b6126fc19", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JewACgkQm/TvTexB\nFjr68Q/7Bv6q+YYSwDV6wpQAZx0sjHBuNyZtGXOZaUG0t47aoLLB0JBu9AwNSV9H\neFGKg/HYDAW6VvhnxhXRt9I9/6nqTk3XMYiwaH1TgEfa02ys4G3jq/pLJZcp4AQg\nQPEG1ot0VpqrzJ87MjCa9prtvW5AkXJg2Fm4IP2xNMgHHICUAawIBJUcnOjig+Be\nXgU7fnShhVAfyHLjc0pDGla7MWGVUJn3t5QUl0t8ccGF8UNZuUWpGjKNz30WokBg\nAnVxogcK98G19cPAGsml+1RxRVdqkqZ2G9VZLsg7Dp6Tc+rHxpfdYq5KAn18Phbw\n02owI5UaLpDdXCmA/045gJAatZxWRd0712V1EpY87Un8JW+RNVtBY2rY1ihKEPw2\nfEBzMADqXVROZTRAsmugwCOAIdAocum8RIEMSkcW0DG9ZpA9XppIG+3hqjPTmyZm\n0BKNIAcRnF80Hei41UpRjoz0AQ1vPo4XxmPvfm7Npo2UhwUA+sQKuUPEUMibaNyD\nRi5Laq+lfO4Thql4q9dhqYTvVs8kppIduVwBRLQH+SY6+CxKYyIgH8+Vnt0PBIFd\nExzQnkiqpu2W1t3g5vm1Rg+WFwsJvl8DdZ3Xks2QJ38pJfw5hHdtzo5gheL50VUL\nCPIXGgngXmg/fpqokfxnOrka/Iwdor4coVhYRDG/k2GWcf3V/VU=\n=J/5D\n-----END PGP SIGNATURE-----", "payload": "tree 1df05815ee307b6a53d64016092cc8e7767e2402\nparent 00d1530c2b715f954761d5757f5108dc4c82d954\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774133711 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134764 -0700\n\nRemove requests-oauthlib requirement\n\nInstalled with python-twitter\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/76190ad6c6d1fce1f194a62ea763552b6126fc19", "html_url": "https://github.com/Tautulli/Tautulli/commit/76190ad6c6d1fce1f194a62ea763552b6126fc19", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/76190ad6c6d1fce1f194a62ea763552b6126fc19/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "00d1530c2b715f954761d5757f5108dc4c82d954", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/00d1530c2b715f954761d5757f5108dc4c82d954", "html_url": "https://github.com/Tautulli/Tautulli/commit/00d1530c2b715f954761d5757f5108dc4c82d954"}]}, {"sha": "f4b8845709ee89d1e2d73ab06142fd88fc347e7b", "node_id": "C_kwDOAducuNoAKGY0Yjg4NDU3MDllZTg5ZDFlMmQ3M2FiMDYxNDJmZDg4ZmMzNDdlN2I", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:00:14Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:44Z"}, "message": "Remove pyparsing requirement", "tree": {"sha": "f61b8907d36f3edcf8c47b46cdbb719905504c55", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/f61b8907d36f3edcf8c47b46cdbb719905504c55"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/f4b8845709ee89d1e2d73ab06142fd88fc347e7b", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JewACgkQm/TvTexB\nFjrPpxAAxjV6wMpT8PZ6Ts7APQR8dxQejuEXF0QWZhTM7+MpG6hZNKjH8McM3wxd\nrlnJOfCkynB00TwnRFEC1gF7/DWOn1bLmozwoFzbQF6kx+SwKJI0tA0etqIjeOLl\nA5QDY/s+lNZHb3iBpa25YPUrtOdFR8bYAsmuQ4bFOT+mEb0Nhn0zgW3zCALjEKtF\n2VnPuz+YJZkT5Ra44+HHBWAo7VeW8xVLPhpchutt4pW4fNJ45W12i51CQRPQFiHl\nuIrYs8QA8yk8zrdP4q47OhI/TN0W8DuksDf6Ks9s4ph/sUads+WBYYfp+kJ/tsGp\n2T0mnzZk6ayvl9ZHQJ2HN/peAhxVja5mz7N/JWRu5Hp/bChVGkaYKb6LfLsmyfkX\n9fgpWRjh+txv3uWYqIVhbTnoR/lyWilO2SYkOT03pzTnfFojVmuwAnT6okW1RPRi\nFQpkyN/oNvvGVBUux6aSFvU8OMkBWbzpygd2a3Ls1CcVj+XE3XZX2RNt3+bQDWor\n6iSuIFAA5R8UTTyH0YZu0ItWmH2fUFj2YzZTYKn1E6B+ych71sDDPa80JsZNq/xQ\nnfHn2Tra0PQ5Lnqp6+HycD0QQgxpYepemW/cZMXHkYFh0Stn4mJf55bEyeH9yb6d\ncoks3N5FFo4Z6CkycarXiQI0LKoD+vCEtFDA4MTJIx9yezNYPpE=\n=69jD\n-----END PGP SIGNATURE-----", "payload": "tree f61b8907d36f3edcf8c47b46cdbb719905504c55\nparent 76190ad6c6d1fce1f194a62ea763552b6126fc19\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134014 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134764 -0700\n\nRemove pyparsing requirement\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f4b8845709ee89d1e2d73ab06142fd88fc347e7b", "html_url": "https://github.com/Tautulli/Tautulli/commit/f4b8845709ee89d1e2d73ab06142fd88fc347e7b", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f4b8845709ee89d1e2d73ab06142fd88fc347e7b/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "76190ad6c6d1fce1f194a62ea763552b6126fc19", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/76190ad6c6d1fce1f194a62ea763552b6126fc19", "html_url": "https://github.com/Tautulli/Tautulli/commit/76190ad6c6d1fce1f194a62ea763552b6126fc19"}]}, {"sha": "c6295cbabd8ae547a04659a90cbe845d6ef9df55", "node_id": "C_kwDOAducuNoAKGM2Mjk1Y2JhYmQ4YWU1NDdhMDQ2NTlhOTBjYmU4NDVkNmVmOWRmNTU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:12:15Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:13:04Z"}, "message": "Update ipwhois==1.3.0", "tree": {"sha": "cc08ee4073189da5f6d16d4d290653cda8034904", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/cc08ee4073189da5f6d16d4d290653cda8034904"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/c6295cbabd8ae547a04659a90cbe845d6ef9df55", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/JgEACgkQm/TvTexB\nFjrsuRAAsU4poMRLTuwraSAIHPc049gkVnZk97f0x0QMgCRv9yehUtW6t7gcV8kc\n/a9AQxgWxNwH8z67HsccAXNn1t1zdw2bRHFeGwsFn/gC8nFCXmzbP1V48o6TDDjZ\n7ZCobpMMnDxlf87H3P/FAGcLWOemV5HYGtGLdMoU3OW1YPC9GHyMajgb+poLmbAa\nVoUXrP4n5E29k22sRlCsGakpUZULQmZuaSV478Yf2n2iibUB4EuVqWFCB/vowndL\n5C42edBSbIj12zgpypSwCmEA5IZX8UpBuk3n8iJ3v9y6O0YjNHnOMwAhJSHPi7E6\neoUqByzTgOpknvmAJeyeyujTZOk2fjHDNnLAtuh7EdsX8YLlS1HMEp3V79j86F6y\nAh+e8B7Fom0Br4Q7pOafXtxQrAfuZX2Z3RGhU9v2sIKMtsALUIB+F9RXKycetl/F\nBpJsWAlKOBeehkbKVe2jQRl2fOdlTZglOCAFpqqQ493jSCOm7j32mdW7m4YHUM3P\nP70F93IivpiaGegNn7+BjB7xIQ6hWzOnmoFdZMY6kQg+60K+eCD1uwWLh+SiK9UY\nUpxtSOuwghp1noJou5h8WaH/w1A9bZYjQn3CU5w8I8j4Pbs7Ffh3funJiY1aIZyv\nt0L0J3E6zQiu2PDUvxr1g/c8sEbCVAtd/gbhVTE39QHhGGaxX9U=\n=kT3q\n-----END PGP SIGNATURE-----", "payload": "tree cc08ee4073189da5f6d16d4d290653cda8034904\nparent f4b8845709ee89d1e2d73ab06142fd88fc347e7b\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134735 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774134784 -0700\n\nUpdate ipwhois==1.3.0\n\n\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c6295cbabd8ae547a04659a90cbe845d6ef9df55", "html_url": "https://github.com/Tautulli/Tautulli/commit/c6295cbabd8ae547a04659a90cbe845d6ef9df55", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c6295cbabd8ae547a04659a90cbe845d6ef9df55/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "f4b8845709ee89d1e2d73ab06142fd88fc347e7b", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f4b8845709ee89d1e2d73ab06142fd88fc347e7b", "html_url": "https://github.com/Tautulli/Tautulli/commit/f4b8845709ee89d1e2d73ab06142fd88fc347e7b"}]}, {"sha": "cb6d23cf4bcfa6fb06566074e0ad5857e819034f", "node_id": "C_kwDOAducuNoAKGNiNmQyM2NmNGJjZmE2ZmIwNjU2NjA3NGUwYWQ1ODU3ZTgxOTAzNGY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:21:38Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:21:38Z"}, "message": "Remove sgmllib3k from requirements.txt", "tree": {"sha": "68d89ec01b5c1cb6c63b0b83acf1d709619d41c9", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/68d89ec01b5c1cb6c63b0b83acf1d709619d41c9"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/cb6d23cf4bcfa6fb06566074e0ad5857e819034f", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/KAIACgkQm/TvTexB\nFjrPiQ//fXDhXHQdWKJ0V1Hh04G8moK/IotIiK+NH+PYul1DGT4HEXV4OTMZvWPl\nPlpOfPLu7REgz3wGJZBlmDVYGAMx67fRQU2dhqSTc26BcCwNxCXCZDT7bfDBh3ie\nB4Zd7bglPjcPwXEb9lUg7UQ8ojS5tM/E6d1W0OjbYMXRxrIap2Esx9swgUtDfQc8\nMvNSALAZvwdTzv/xuiTyBiVs5oUIcGGa3riMtTtEKBwQBCwKXaMnfU5RYrX4PbYk\nQrNf+XHKwVBVyuAlHfY7ewS95QmlDZ56OVJ4/S7Fbdhs4eajlZEB5cSAOCcIbMKy\ndTEmTwUS9OK6skkE4K7xOjzlbZ2ZBtza7nUo0a0S7DJAJOL6qI9QwYEZ7WBnXNAh\nznNrYLug3Qh+1zsXLxn0s2XkG8ZiJzc7nJBSDmfAEm8J+cm0yjhFbwXrnPp3+f8r\nUlL8ADVHVYPqDeorcOcx5azhTcBs+8X1OG6y7z2x6blgUbwiKdp2MI78jCGznnS2\neegHJ9wGzJ1R02Sq4tUD/IDlAYXe//8gAwEyTnIm2Hpm7AsYY9DBYwtvOTVTDpJD\nwQM/mgrVjtuFnczYtwGRXpCnXZurBe27oolQO97bqmuSxrDo+nsZOO+ieWP/2/GJ\nLEn2PkE0UM4CvbsIRjrry0joR1/IbjJRABfnc2mynzlUTIl61ew=\n=aGJW\n-----END PGP SIGNATURE-----", "payload": "tree 68d89ec01b5c1cb6c63b0b83acf1d709619d41c9\nparent c6295cbabd8ae547a04659a90cbe845d6ef9df55\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774135298 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774135298 -0700\n\nRemove sgmllib3k from requirements.txt\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cb6d23cf4bcfa6fb06566074e0ad5857e819034f", "html_url": "https://github.com/Tautulli/Tautulli/commit/cb6d23cf4bcfa6fb06566074e0ad5857e819034f", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cb6d23cf4bcfa6fb06566074e0ad5857e819034f/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "c6295cbabd8ae547a04659a90cbe845d6ef9df55", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/c6295cbabd8ae547a04659a90cbe845d6ef9df55", "html_url": "https://github.com/Tautulli/Tautulli/commit/c6295cbabd8ae547a04659a90cbe845d6ef9df55"}]}, {"sha": "197cd80943834f2826c7f10cc30b61c68a2386e5", "node_id": "C_kwDOAducuNoAKDE5N2NkODA5NDM4MzRmMjgyNmM3ZjEwY2MzMGI2MWM2OGEyMzg2ZTU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:29:47Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:29:47Z"}, "message": "Remove unused libs", "tree": {"sha": "8fa029df910916082a112d9ec4665aaa0fcc5de2", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/8fa029df910916082a112d9ec4665aaa0fcc5de2"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/197cd80943834f2826c7f10cc30b61c68a2386e5", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/KesACgkQm/TvTexB\nFjrPIA//ZyRlfT/Mx/M7BJJLSswK2wsGJr4wh636zzsyAMYFoPJzy31OziNbDu+I\n5wVzzHNXdhKekTzD9FpRp6bMr81X4NJa7FswdOxXJG53/4cY8LK459I6GPDrSKgY\n8CVj5XUBSdY1dlJS42DLxvM1yscOjelzStiv35FfPl/AHSLWq72MlE5TdVyOWyVx\nD10h5dC1F3KRLEQ/ZnuiO6KnE4Ij1ulT/pnj04e6uM+QjbTZyZTvUL+3UcaU2Eaa\npLQcbjL3epyvHNwjZtliFrSrOPWdx8KhjQaFEmctBuOZZu/V8em2HvGAOoinftls\nTIUdb+uopJkUiMiMkrXn3hM8Q2sFNoeeMjCXWEEWTebG5RBsYntCW4VaZiLRSFuN\nHOtZ+bGVePDlxrbd9GiWarempDXt0btuhavZNtNNWOr5ptsaB1JB8mIciBHIADwS\nOhrWO/Bw+S8Bb7wvvQhtfApkThhxQSaaVPKa9v64tPWq2nGzYwoUqjs4f4WzWyZn\nU5gaW5Lvz38lCgDRqTc6pJo1czJ1AHqrKikX4Wvs7ROmicZBdwgLCh0mgQWalv7/\nE6nJYHlLLSDbhH8+9W+dORgN2h4BiYMpMo69exwpZ4gwMCf0pG0Ie9z5iaEL7uUQ\nGojm8ffI/uUVXOB2UA8xAWyhkcJvWpzg3atSn+FLgT5Bhcc2XA8=\n=Tey6\n-----END PGP SIGNATURE-----", "payload": "tree 8fa029df910916082a112d9ec4665aaa0fcc5de2\nparent cb6d23cf4bcfa6fb06566074e0ad5857e819034f\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774135787 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774135787 -0700\n\nRemove unused libs\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/197cd80943834f2826c7f10cc30b61c68a2386e5", "html_url": "https://github.com/Tautulli/Tautulli/commit/197cd80943834f2826c7f10cc30b61c68a2386e5", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/197cd80943834f2826c7f10cc30b61c68a2386e5/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "cb6d23cf4bcfa6fb06566074e0ad5857e819034f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/cb6d23cf4bcfa6fb06566074e0ad5857e819034f", "html_url": "https://github.com/Tautulli/Tautulli/commit/cb6d23cf4bcfa6fb06566074e0ad5857e819034f"}]}, {"sha": "7547399813085fecf3c4f5ddc938d3302a3c19ca", "node_id": "C_kwDOAducuNoAKDc1NDczOTk4MTMwODVmZWNmM2M0ZjVkZGM5MzhkMzMwMmEzYzE5Y2E", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:30:59Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:30:59Z"}, "message": "Update dependent libs", "tree": {"sha": "81b9274ec15f3d72ba350e5d3a31f6630805993e", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/81b9274ec15f3d72ba350e5d3a31f6630805993e"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/7547399813085fecf3c4f5ddc938d3302a3c19ca", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/KjMACgkQm/TvTexB\nFjrOuRAAjx38flDbSdosCRVn5xT85NXs+/Lx/iJlYYL9Gd4lfonCZklVbrQwb/Eo\np3Nw0gx4gS0fN2esigcuqdIfHcgeU9e/Tjri5E/ZHzA6+dIzagjetw1Q0b/LaAiw\nbQ4AGzosk1CmJ7A+j4rXNtW19d2c3vxZy9Hy4y9YydG3vOQwtcS/M+ISVeb8SwcN\nfcgR2u3Msn4hEZIjf+6B5olmN0+++d5DJc2Em6gDASMupqWkSAXwFFzgfDjhde6z\nQuNAHOMjWmVt3EdxaovNWzl/s6nOgFJnSvQ0eH+RF8OJ4zXgoT7btfNxIfv20Xgk\nAhcsTY1inYYYThuDhSBTewnMBsbn+IiDOELurkOqB+yWBwiftioMqugC8s6rprH9\nrxNpsKy8ijwOQS8dm5b5lQ964xJ/qghtw7A+Zz3CS4m5qIjXOUwscEv0nvNDORmo\nFtl8yMmuVYLeqL/MQCVmaew0CT05PHi9Zhjm57mp+UxaKGkCwCuUxG7X62OcAM9i\n2JMvlC8EIfTjzhZdtRbxwH9mRmR7E7KAoCJl9nPMeiPnBsNEOqYpnMcKJEi1j5Ev\namrdBLNinzV1UYMSuCeqoggbjspbZXLFsFfvtbk6hDC+QOi6U0AqM2Y6tnU5qGyd\nqQ6urIeekKe7VS9anbJj14xMGh6qPjGX/BvlLpFd2e2mmMyriX4=\n=AYWz\n-----END PGP SIGNATURE-----", "payload": "tree 81b9274ec15f3d72ba350e5d3a31f6630805993e\nparent 197cd80943834f2826c7f10cc30b61c68a2386e5\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774135859 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774135859 -0700\n\nUpdate dependent libs\n", "verified_at": "2026-03-21T23:35:39Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7547399813085fecf3c4f5ddc938d3302a3c19ca", "html_url": "https://github.com/Tautulli/Tautulli/commit/7547399813085fecf3c4f5ddc938d3302a3c19ca", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7547399813085fecf3c4f5ddc938d3302a3c19ca/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "197cd80943834f2826c7f10cc30b61c68a2386e5", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/197cd80943834f2826c7f10cc30b61c68a2386e5", "html_url": "https://github.com/Tautulli/Tautulli/commit/197cd80943834f2826c7f10cc30b61c68a2386e5"}]}, {"sha": "de358b1f125f5d30cf5f138cec1e72f08202ab6f", "node_id": "C_kwDOAducuNoAKGRlMzU4YjFmMTI1ZjVkMzBjZjVmMTM4Y2VjMWU3MmYwODIwMmFiNmY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:49:40Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-21T23:49:40Z"}, "message": "Bump pyinstaller==6.19.0", "tree": {"sha": "6b69047f05c41ee49b8d783354d2621635f21c4b", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/6b69047f05c41ee49b8d783354d2621635f21c4b"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/de358b1f125f5d30cf5f138cec1e72f08202ab6f", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/LpUACgkQm/TvTexB\nFjoaFg/+O9fqI8kuF4GUUdvcN5Tix+Wx/2DYojj6m+N1i5ihFrHu0XHmRE+T0nUh\nfZpEBck/rWzv8CF3XFbCJJsEspZks0h6ibbolcXUmKg41xmIviNlW+KoZ0/ADCPd\ndRaFWZ3Gemi3hFBCxy/rInj9KMaYtZEkI8aXxskoVQzIFAUOtUL8715fvQllm6Qp\ng19pOa8uiMnrxnWTlsGIDXZbDAOKy5rF7PBOrwyKR8WurDK26rd3uxlkOW0BaE4E\n1F9G01Qu3v53vrcoqGCKjgH684HXSgEJlTLjQKmly7OZL8ljPYfO5axesFieZTvy\n9i9vYLlo3/xDs9ap+fi4J266RpQ4nencnYj23Qjg9y4BXieUUDYD6R218eC0/LmW\nmXN+UbzZQ/GOP28QWwbL0bvV0nBj2ZCNxrUaoJ01cMKurRJdfBdLZxWGEk9CWbB2\ncqd/L0HqeX2vrfpsSRIPTh8rQOmv21iyYmhBHoC0r8fPq98ngXVSDuseCaS5i93f\nHIYBlkHK9gE7L82ils05tB297vXYwlggxTh9xz8SV/01QTfX7ab2MOsOTsrUxAnS\njgzpgQIZopktbF/O4V7D0YZiW13X5ApO5zzWaLb2S/8Yf60rOplUa8NpuBEF+XLU\nM8k9jVu6F7K+CMPMfn+6y72o/eTMvwPSff6I4P8ZMPlcUqsDnug=\n=/DZ7\n-----END PGP SIGNATURE-----", "payload": "tree 6b69047f05c41ee49b8d783354d2621635f21c4b\nparent 7547399813085fecf3c4f5ddc938d3302a3c19ca\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774136980 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774136980 -0700\n\nBump pyinstaller==6.19.0\n", "verified_at": "2026-03-21T23:49:46Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/de358b1f125f5d30cf5f138cec1e72f08202ab6f", "html_url": "https://github.com/Tautulli/Tautulli/commit/de358b1f125f5d30cf5f138cec1e72f08202ab6f", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/de358b1f125f5d30cf5f138cec1e72f08202ab6f/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "7547399813085fecf3c4f5ddc938d3302a3c19ca", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/7547399813085fecf3c4f5ddc938d3302a3c19ca", "html_url": "https://github.com/Tautulli/Tautulli/commit/7547399813085fecf3c4f5ddc938d3302a3c19ca"}]}, {"sha": "44201cfa200cf82ff676bc46be3058543f4cf8d5", "node_id": "C_kwDOAducuNoAKDQ0MjAxY2ZhMjAwY2Y4MmZmNjc2YmM0NmJlMzA1ODU0M2Y0Y2Y4ZDU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T00:09:07Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T00:09:07Z"}, "message": "Bump infi.systray==0.12.0.1\n\nMerged with https://github.com/JonnyWong16/infi.systray", "tree": {"sha": "bf7196f608b2c083622be3cc2df5f50b78eb8044", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/bf7196f608b2c083622be3cc2df5f50b78eb8044"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/44201cfa200cf82ff676bc46be3058543f4cf8d5", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/MyMACgkQm/TvTexB\nFjrPdxAAzn2VczJng2JV323F5JuYxLYqo3wGHbp/qYKeheKJvF9CtxTMtffF7q6y\nSXMjAnrQaxB/st8ngYEGyynhLV3VeQrrvRbZxEHaNwnrFITCXwIEeRR5lk3c+nIf\npGWu3VnnamzQxyeNTqDZd+raNVVOpLH65G90Q7vGlHwa7RIZA53sB+vDjYAk2K1l\nZEUoRPaPxaokqcE8LDyLHGRw85BGL/8mW+yNLTXFLSJf+xIIVIZd2rBrleb4341x\nEYabV5nTUF1VrQUcxh0v1lrXgVXCOp95HSOGgOqpO7lI1yHPj8cOusoWwEsaJx6P\nY2R6RkOp3UyRC2EF0eClGqF45q16HCDM6995gJjAXpBra1awbtG2DkJaB6Y+gUWr\nUabkyM5NoxWOtuNkiBR9F6ryA7AvMxqHfyAz6w+v0bQ0vgeFuAcGTwxaKghO/Qr1\n/gb6Cmcoyy6eJIgickdrb/w3GJTjFP2y+rCNf0rUpSkjKy1QE6Emi4b524UiDhqZ\nefV3VFmAn+G2TM8283/nfwFoHigmt5f3I2yOW6UF6owo9Ij/1+styWNCTCszWcvh\nHq9Xo0U1lH9F6FBzASiUIHdRoLOlEIt4DkMNkrywv/lZk+mQBxYQqVsljOo/3HYJ\nvXgedDN5Jff8wOvFih411PxePJdFVGCwoA0AWWhh5H8zwKT2B5s=\n=Uc2G\n-----END PGP SIGNATURE-----", "payload": "tree bf7196f608b2c083622be3cc2df5f50b78eb8044\nparent de358b1f125f5d30cf5f138cec1e72f08202ab6f\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774138147 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774138147 -0700\n\nBump infi.systray==0.12.0.1\n\nMerged with https://github.com/JonnyWong16/infi.systray\n", "verified_at": "2026-03-22T00:09:15Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/44201cfa200cf82ff676bc46be3058543f4cf8d5", "html_url": "https://github.com/Tautulli/Tautulli/commit/44201cfa200cf82ff676bc46be3058543f4cf8d5", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/44201cfa200cf82ff676bc46be3058543f4cf8d5/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "de358b1f125f5d30cf5f138cec1e72f08202ab6f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/de358b1f125f5d30cf5f138cec1e72f08202ab6f", "html_url": "https://github.com/Tautulli/Tautulli/commit/de358b1f125f5d30cf5f138cec1e72f08202ab6f"}]}, {"sha": "509c42b6654250cebe99f7aa0672e4d71e56c26f", "node_id": "C_kwDOAducuNoAKDUwOWM0MmI2NjU0MjUwY2ViZTk5ZjdhYTA2NzJlNGQ3MWU1NmMyNmY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T01:02:52Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T01:02:52Z"}, "message": "Fix logos exported as jpg instead of png", "tree": {"sha": "20fe05e4d34625b60f7d9898de86bde31dd2b9fd", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/20fe05e4d34625b60f7d9898de86bde31dd2b9fd"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/509c42b6654250cebe99f7aa0672e4d71e56c26f", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/P7wACgkQm/TvTexB\nFjqYUg/8CjnR2wydkX3Fie9lo3ppq8DMNpdwaBcOM6KlHjUabLYpmN7pG1dv3gIE\nsp+t3rqN6W3XtCnJRnGRFrUWD0PbgdRRj6q82DFQugVZH1ftYJghUjxsWakW3+4i\nRnDi0I0/Df1r+TwAIkR/12D8I3il5SO7hDHNdfQ7Mxw3NDdjLUAOA30wVD05fF11\n8uvnaGhuQ93vn2sw8ed+Xear2qTGjIq5TW5/GFuYF7qOau66p8MOQZeDHtEwB/RM\nevBHjBmm1QTP3i78gO/EwQqzAh64ggCO5qBGRnKGDHhYlfET2VqtR7VLqmgUq8OT\nsqnWo0pESNbxJWfrxNSAlpCR/KpK0u8coWVSH2uo/CjHQ2zv9OEGv1nbLe1LkJvd\n3huHKgcUMT4LjJ25fJWpVJfK8oZWnp9EQIzmOvrjxUEDlXxZ3RvtAo8LA/vOlyyb\nH/8lzaFOrv3yYEwtmpWHypl/97/rw00vwT0slKgAgZ42NwFGDyq2WOqhKmSzebAy\nSCd5GQC9XWAeApO/4FTiM5k8s9p+v3Cz/XbrEfiz5xuvRFrNFJVkeaZ+6GY8dtc3\nGQ0kuxTFcPhG3AVfc+MCMszW1PFmosAPgNuSRHPo6rdaM2vPVn8y3rtNrOBqWOwt\nt3Q0QwISLpDut2EKB+so7HCthD7rJ8A0TRl6hrVdRfEdU3h+xOU=\n=bYny\n-----END PGP SIGNATURE-----", "payload": "tree 20fe05e4d34625b60f7d9898de86bde31dd2b9fd\nparent 44201cfa200cf82ff676bc46be3058543f4cf8d5\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774141372 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774141372 -0700\n\nFix logos exported as jpg instead of png\n", "verified_at": "2026-03-22T01:44:03Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/509c42b6654250cebe99f7aa0672e4d71e56c26f", "html_url": "https://github.com/Tautulli/Tautulli/commit/509c42b6654250cebe99f7aa0672e4d71e56c26f", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/509c42b6654250cebe99f7aa0672e4d71e56c26f/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "44201cfa200cf82ff676bc46be3058543f4cf8d5", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/44201cfa200cf82ff676bc46be3058543f4cf8d5", "html_url": "https://github.com/Tautulli/Tautulli/commit/44201cfa200cf82ff676bc46be3058543f4cf8d5"}]}, {"sha": "ee275bcdfdaf9327f8abf12abec4f6682d8e3707", "node_id": "C_kwDOAducuNoAKGVlMjc1YmNkZmRhZjkzMjdmOGFiZjEyYWJlYzRmNjY4MmQ4ZTM3MDc", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T01:10:11Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T01:38:57Z"}, "message": "Add square art to exporter", "tree": {"sha": "a84df719a15ae1f43c75c277357a7814c65193b7", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/a84df719a15ae1f43c75c277357a7814c65193b7"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/ee275bcdfdaf9327f8abf12abec4f6682d8e3707", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/SDEACgkQm/TvTexB\nFjrgXBAAoskaH5bel96vE22vDwRgH4ALfibjS7kmF1dEnHS0ZDZKgQDFwe7VjsZ/\ncHPmi9anR+17QTshDecFtp46ml8o/dsogzs697p5/dtcCaKwYPuu2T0h+t27domK\nOMxF3TefhOOfNTElfQndkjXGFkhUo5SUPThcilAr+8428ulnlOmNvpQfbUmhepCg\nziyEHUtdewr5nLimkvKfXGAMokPTPTzdgQUY50+ELF/hfFj/ZV3LDfH8FPFAwBNm\naCtmvcGxxrEGU1WE5n45z0n5s+TE/mUEuogilN1K4rLJLs23noPTRdyFk/vpJh/P\n65VgSJ90AudLCpjXtsFNLW/mXyJfGbuOrgl9E5CABKk08dxOR8xmAfLDfDNh3ucT\nKMvcz9b3J4QuDFgOZKn/+Ea1/nsprwMXUYS0OG88kyI/+wQ4O/XCQUO3jPuJMj1Q\nTDbjFrGRBNz7NPCLNWpaT44hmK+oRWHBcBxWs1SGacbwOqyTsFXwlBgLuzvIH+pf\nDTpJGguvra66oi/u0cXank/+//HVUhJ0BP/zCnZXJJmjoR3ydHSjc3Wk147ABaq7\na7an/NdAoVLTkWr2V/ybMF/EclUzg1Zoj2cWqFLBynIboolCuty2uSUy5GpaZluc\nXKxaQny3w0fmcxdXJfuDvn+l7Hu/DuoPtsOZYEoCCprifyGOu2M=\n=clz6\n-----END PGP SIGNATURE-----", "payload": "tree a84df719a15ae1f43c75c277357a7814c65193b7\nparent 509c42b6654250cebe99f7aa0672e4d71e56c26f\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774141811 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774143537 -0700\n\nAdd square art to exporter\n\n", "verified_at": "2026-03-22T01:44:03Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ee275bcdfdaf9327f8abf12abec4f6682d8e3707", "html_url": "https://github.com/Tautulli/Tautulli/commit/ee275bcdfdaf9327f8abf12abec4f6682d8e3707", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ee275bcdfdaf9327f8abf12abec4f6682d8e3707/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "509c42b6654250cebe99f7aa0672e4d71e56c26f", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/509c42b6654250cebe99f7aa0672e4d71e56c26f", "html_url": "https://github.com/Tautulli/Tautulli/commit/509c42b6654250cebe99f7aa0672e4d71e56c26f"}]}, {"sha": "e048e78b682b41aebb645689c149da9aab666ebc", "node_id": "C_kwDOAducuNoAKGUwNDhlNzhiNjgyYjQxYWViYjY0NTY4OWMxNDlkYTlhYWI2NjZlYmM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T01:36:08Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T01:38:58Z"}, "message": "Add themes to exporter\n\nCloses #2654", "tree": {"sha": "df1829a31cb00cc277d6c2ba15c90750ff0a5714", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/df1829a31cb00cc277d6c2ba15c90750ff0a5714"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/e048e78b682b41aebb645689c149da9aab666ebc", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmm/SDIACgkQm/TvTexB\nFjomRBAAiMC4CDM6PN1Q5xIWF/wsuTkTiU53BGvI5+kAbYxrFUitdPA/q2okAjti\nyB2aAHvnjupfHy2pOlwvh67wAPgC4ePpP9ItzrI7j5hETLXtThN/MV7WFOkf+tmF\nkceZ/V66ze2ZS9TwXyRgOFDYnJp1gHDkji4D7Xco1XTSkGexReb5EWX6UPjK/qu1\n1sN/7LOpLL4tXmv012rxKutvEoMyeAgDl7oWGvgkh+bQ4NDBRGLExokVueXJajbQ\n1fRBMsVy20jyW3Lbu0MqZ3wv86Y0eJiDCAlyHDBdzUO/M4Yb+aZ4LT+ywozFW1Bp\nlKhAuuzsK1dla+AXSwLVCLjTOKRSNJdN/CbhdIlEZ3QUp4XODhRUkZTl0Oxa0rvm\n6gsN367Bkkn5vjKGlwxsS4kuEnwpCyWOKAHJnXxlKe5n7VEpsZbdUecxel86L7rD\nlLJOpdCCdpP7YBRRFiQ23QB7ecKJSmXswzP8HYUDpGR6zctEuC5KBTmoZwz7ZiYr\nhRtc7GztMnNjVTiURa/EB/X7e178CleIv7zRDJPvS3QC9x/ZYfKE709CfLR2SsiR\nfkpFe9LcXEnm3lugtcHKYAsqUPyYaV1W4braS+NY1iJjg48HeztomB14pSqdADZX\noXuC7WijTrk0PLmTqfGcWmUEqRP6ycwKiHJ4o8P3Exc+3EuFWkg=\n=Mpz0\n-----END PGP SIGNATURE-----", "payload": "tree df1829a31cb00cc277d6c2ba15c90750ff0a5714\nparent ee275bcdfdaf9327f8abf12abec4f6682d8e3707\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774143368 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774143538 -0700\n\nAdd themes to exporter\n\nCloses #2654", "verified_at": "2026-03-22T01:44:03Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e048e78b682b41aebb645689c149da9aab666ebc", "html_url": "https://github.com/Tautulli/Tautulli/commit/e048e78b682b41aebb645689c149da9aab666ebc", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e048e78b682b41aebb645689c149da9aab666ebc/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "ee275bcdfdaf9327f8abf12abec4f6682d8e3707", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ee275bcdfdaf9327f8abf12abec4f6682d8e3707", "html_url": "https://github.com/Tautulli/Tautulli/commit/ee275bcdfdaf9327f8abf12abec4f6682d8e3707"}]}, {"sha": "6a8ad93bfa419ed7226cfc20df2a2ee9258926aa", "node_id": "C_kwDOAducuNoAKDZhOGFkOTNiZmE0MTllZDcyMjZjZmMyMGRmMmEyZWU5MjU4OTI2YWE", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T23:54:42Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-22T23:54:42Z"}, "message": "Do not export episode logos and square art by default", "tree": {"sha": "717f9e0c57f8be86ca68485efba39947ea7ecf7d", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/717f9e0c57f8be86ca68485efba39947ea7ecf7d"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/6a8ad93bfa419ed7226cfc20df2a2ee9258926aa", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmnAgUIACgkQm/TvTexB\nFjrhag//boRJgkwU0Ki7FaiviJrnD9puXi49gNYq+P+LLQg/NGgqnjEVDqboBWlp\nekPLwYJw8d3avMS1KkDHVe5hDs3JzN/5ETWV56agJTBKzqbTx2AVy2TrYmJ0/UF9\nPsDDzPoX6zJfoVtqCtzNWqFC8LTjePaguG/SNUIBjkk6+l15JeQuUHRh1lC56glj\n+AtDRXaBJYHEeX8CYr/7EGhVgHDTROu9r8nv2FKWlXrekyuqHxcMvCDLRcL1EaxT\nUWhq139eyTc9sLHwAz3GBxEBfOvmkPEGGyNy8Xq9HAK2lTCC4debAYfXwoXWues3\nWAGOQk29c4M8Q1ychXKBtLaqwGyO7Lir8jEewUn5nseGugwfB9MvdaNBj0DCJQ6I\ngPNLbxoIWYzf7AnTpDNe2GQtahyF1mzQtQJOrGaxBmm0/IrDNXdAuH+p3YTwqPpf\nczGuqu5HBhszmU6DRXOpbdlMOvZfEEZDi4gxijWXxgfrC/4saIlyKq1YrULs0JUS\nzXiROWx3Q8E7aQPEmnQinqTQrNDDOI3xKVox/R0YZ8m/YNTH4xdnokC0dkHDoYWX\nw6QaUW4+ywcgMyOomCaIwlqn1XTXGEyy5k1FsKImxb1csK4StnGpOTJccP4Ybk84\ndVvJge0S8bJAOXRAdmqR+TXn/v67kYkk042XjlYYlJkvTdcsxfU=\n=Zn/7\n-----END PGP SIGNATURE-----", "payload": "tree 717f9e0c57f8be86ca68485efba39947ea7ecf7d\nparent e048e78b682b41aebb645689c149da9aab666ebc\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774223682 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774223682 -0700\n\nDo not export episode logos and square art by default\n", "verified_at": "2026-03-23T00:06:21Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6a8ad93bfa419ed7226cfc20df2a2ee9258926aa", "html_url": "https://github.com/Tautulli/Tautulli/commit/6a8ad93bfa419ed7226cfc20df2a2ee9258926aa", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6a8ad93bfa419ed7226cfc20df2a2ee9258926aa/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "e048e78b682b41aebb645689c149da9aab666ebc", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/e048e78b682b41aebb645689c149da9aab666ebc", "html_url": "https://github.com/Tautulli/Tautulli/commit/e048e78b682b41aebb645689c149da9aab666ebc"}]}, {"sha": "f52b017d984d0efcd96370e4a41bdd606cb0a595", "node_id": "C_kwDOAducuNoAKGY1MmIwMTdkOTg0ZDBlZmNkOTYzNzBlNGE0MWJkZDYwNmNiMGE1OTU", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-23T00:05:43Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-23T00:05:43Z"}, "message": "Fix export modal theme music typos", "tree": {"sha": "bf76298be85468d0d8a22850795f00af1dd65750", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/bf76298be85468d0d8a22850795f00af1dd65750"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/f52b017d984d0efcd96370e4a41bdd606cb0a595", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmnAg9cACgkQm/TvTexB\nFjo4AA/+KhbojXjJEj/31+UUStu151rhxpp5vnOX9O7YiyIQYoO47PJWvMI6OOyQ\nTRfNBxX3bruQP2GdAhdPz5GLScqKyl1cEgCMf+9mVD5Vjm/9RewoVXMkApqjmm+X\nwVusYz+s9GnMM2f4VCgvbDNo0d/YQlgIwKWMVrX7kXwZQIs38YYSUv8eoc8MVOUX\nlD9rQRHN5Ldrd3du2SYNL1a+e80HEFNTv0y4dca4BMi4RwGuBfqc4vUck99WXFzU\nPaN8YrVviagfIl/5YYOSJqpoIFlBPqy5JcqQG0aBaMMRtGv43++mZE42YIxVqspo\nNqG9LwqsMEhJuKkBaZQrj7EWHDODIkwf8P7YKqbQWS9jQj4BabO26LpyKxLaeMDA\n/xCpxjeD4llAoMc0vFTwsF36vkVeVfKX1CCOhAgXn++cghVQK7NQuokPS+u9EL0x\nyJMj1f8VUzX6inKIBEo6jAr9J1ap6DwsLbBfWysvg+p4tx3q4Aw98AN4znIrvaXU\ngC0Rr/2N7HRGPHE5SJyQjZDxXZACT197sw/uYouGyHWUHNOFWAZw8lA4zytYATSf\nAN4jEndqMfypzclak310pOmdElwWRJGXR+iYqwpTtkkTUfxconMniTJ9JwNSSIzG\nZNYjgrxBfLu8TT+x63i8ls2rDuR9i71SxSuNIepO8yfLjxvkuxM=\n=yTgz\n-----END PGP SIGNATURE-----", "payload": "tree bf76298be85468d0d8a22850795f00af1dd65750\nparent 6a8ad93bfa419ed7226cfc20df2a2ee9258926aa\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774224343 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774224343 -0700\n\nFix export modal theme music typos\n", "verified_at": "2026-03-23T00:06:21Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f52b017d984d0efcd96370e4a41bdd606cb0a595", "html_url": "https://github.com/Tautulli/Tautulli/commit/f52b017d984d0efcd96370e4a41bdd606cb0a595", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f52b017d984d0efcd96370e4a41bdd606cb0a595/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "6a8ad93bfa419ed7226cfc20df2a2ee9258926aa", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/6a8ad93bfa419ed7226cfc20df2a2ee9258926aa", "html_url": "https://github.com/Tautulli/Tautulli/commit/6a8ad93bfa419ed7226cfc20df2a2ee9258926aa"}]}, {"sha": "4258c37f4e3b3f3151b3b15615856d88e85b2bf4", "node_id": "C_kwDOAducuNoAKDQyNThjMzdmNGUzYjNmMzE1MWIzYjE1NjE1ODU2ZDg4ZTg1YjJiZjQ", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-25T06:10:22Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-25T06:10:22Z"}, "message": "Remove package requirements", "tree": {"sha": "b21044c55a30038a485f05db4803fe7802fb5ce4", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/b21044c55a30038a485f05db4803fe7802fb5ce4"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/4258c37f4e3b3f3151b3b15615856d88e85b2bf4", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmnDfE4ACgkQm/TvTexB\nFjpjEg//ZzvDgQgif2xg32K1AI/vkpT71fae0ftOxu+Q+4+OU8uwzMmdBdcNMoSg\nm0uh7wyOycmYcQqFwUmOZKijkbQtjHDlmZ80bedbNuESDD3wOuQZZSImKpKG4yA3\nJiQo+gcQ3Ecaw5Q9EGAEP88O0FtAZp1iRyhMAFAQAS6HXv/iLxQDU19PW5zU3MnF\npFcm+EKc1o5bpGOYFZ9Wsz3nD158LzbDCaA91lYqh5Zn95OQF5lMx2/IpSlIzBq4\ngXmn+YMomRB6g/2/FoZNH4O4JxtRZrlPCDJ6JnDXskBq9A5K1sf2N7NcPuTudWcW\nCBjrSgS+/4Kq0qStYuCy5HKHVt1L79LQpEncYJB+DHY57pLdeN7dsJpSJUnWa4rt\n0bVIRzIqjyCUZI6Ob1DA8THyedfTtXzUYfeDeraikZ10zCMXZFOkZn7m/DqbuFS9\nN42IlAGZeIKUe2k4JjUa7HJFUUStqjX8yXNHgLzZ4AFnguliigTncPg/ROZcv6w4\nJGsmUacpVthW0lJ2lCjW5DuyJPqgNWqzYHJ3z2tKa8pCR7ZvJ+1BG13E7eK1a7Fk\nCS8WCgGApcMVGsVZ1FoxIcWf4pMPUistwC+eHQiBRGaZYsxO6vLNNsTjddycAoq0\ngjCDDSp6YrtpZEjfTkG54GCMmiK/vDUvRQJckbyQdexgTx9rRgE=\n=/sNh\n-----END PGP SIGNATURE-----", "payload": "tree b21044c55a30038a485f05db4803fe7802fb5ce4\nparent f52b017d984d0efcd96370e4a41bdd606cb0a595\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774419022 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774419022 -0700\n\nRemove package requirements\n", "verified_at": "2026-03-25T06:11:06Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4258c37f4e3b3f3151b3b15615856d88e85b2bf4", "html_url": "https://github.com/Tautulli/Tautulli/commit/4258c37f4e3b3f3151b3b15615856d88e85b2bf4", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4258c37f4e3b3f3151b3b15615856d88e85b2bf4/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "f52b017d984d0efcd96370e4a41bdd606cb0a595", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/f52b017d984d0efcd96370e4a41bdd606cb0a595", "html_url": "https://github.com/Tautulli/Tautulli/commit/f52b017d984d0efcd96370e4a41bdd606cb0a595"}]}, {"sha": "ab3969fca7b21786cb84dbc64d42b5db12a6a3c3", "node_id": "C_kwDOAducuNoAKGFiMzk2OWZjYTdiMjE3ODZjYjg0ZGJjNjRkNDJiNWRiMTJhNmEzYzM", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-25T06:14:44Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-25T06:14:44Z"}, "message": "Revert \"Remove package requirements\"\n\nThis reverts commit 4258c37f4e3b3f3151b3b15615856d88e85b2bf4.", "tree": {"sha": "bf76298be85468d0d8a22850795f00af1dd65750", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/bf76298be85468d0d8a22850795f00af1dd65750"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/ab3969fca7b21786cb84dbc64d42b5db12a6a3c3", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmnDfVQACgkQm/TvTexB\nFjpFmxAAk9c/nvY94iIs2tPrBRVEjs+3WpfLXz9nZZDx6bfQf59cwNpUzx/CEYoc\nmJnQ9hUalYgFQDvC/qwef/NkRpaZF7ck1EKPfaqfrNzlFHL8sLjPrQYAWP5HLsU0\n4fVIsUpOZah3js43PshRuL8JQP0Qx9keY5RdCqcElipRJV+dxRbfN07MXBMPZFbI\n6vWoEMzLYi6A7zmI+nedKAq3bojnyxeOiCO7ZoCGZr7gCbaHXizrTyDyXLHHkV49\nFUJvTsCucKeJ8I+LOWnyDIrN/cU6lMbJVMsNNfcM01XSQJ65LLS6ADukzCV2+Lxq\nkcbyJJHIGfyLFZb9T5zYBUiyfWSQzTrmQnYDbwYZ2flHtMyGShMF5dTule56drFg\nJP50Q4PIwGxbKn/VHOvD03IEbDITtJmHlQ6d4kYeZMqTKMYyHKrQGia9H/rDQha7\nTM9xZmW4nB7eNaW2V4zByPrj0c6mcXD8QMrJmpUdERbe6NnL6BGxokRuwAeLPTwk\nlQa9JoCmiN+w60DN8YALctNT1A1EaI3TE5zoiony3E2MCHd1QWj8KGZM5L7qqth1\n43XoPclkS6VSqCXnJOY3kGkaFcsjs4UlDJHWvo24HTDVBTcUWXSZA8BVvg/JYnEl\nox4LEQLYnXYDzYRbiOAEamjbx3tllUiTb4lzKGR463X1r3nNmkA=\n=6N+D\n-----END PGP SIGNATURE-----", "payload": "tree bf76298be85468d0d8a22850795f00af1dd65750\nparent 4258c37f4e3b3f3151b3b15615856d88e85b2bf4\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774419284 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774419284 -0700\n\nRevert \"Remove package requirements\"\n\nThis reverts commit 4258c37f4e3b3f3151b3b15615856d88e85b2bf4.\n", "verified_at": "2026-03-25T06:16:41Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ab3969fca7b21786cb84dbc64d42b5db12a6a3c3", "html_url": "https://github.com/Tautulli/Tautulli/commit/ab3969fca7b21786cb84dbc64d42b5db12a6a3c3", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ab3969fca7b21786cb84dbc64d42b5db12a6a3c3/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "4258c37f4e3b3f3151b3b15615856d88e85b2bf4", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4258c37f4e3b3f3151b3b15615856d88e85b2bf4", "html_url": "https://github.com/Tautulli/Tautulli/commit/4258c37f4e3b3f3151b3b15615856d88e85b2bf4"}]}, {"sha": "082843d6b846345ff00f0a363a401e60aa006aaf", "node_id": "C_kwDOAducuNoAKDA4Mjg0M2Q2Yjg0NjM0NWZmMDBmMGEzNjNhNDAxZTYwYWEwMDZhYWY", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-25T06:16:12Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-25T06:16:12Z"}, "message": "Remove apscheduler from package requirements", "tree": {"sha": "95b9af12f0782196080587d1c48265ccfaa9cfd8", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/95b9af12f0782196080587d1c48265ccfaa9cfd8"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/082843d6b846345ff00f0a363a401e60aa006aaf", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmnDfa0ACgkQm/TvTexB\nFjrPdRAAqJoqlc2w4EzzflBeXW2IX0/EaOv4/G371zHF28zzF2CHYHmqqzX2Jcz9\naS2xzUW4JxBaUABh26NPjxkqlfWZmf4sJcs1RYstBV+bd+F9xIzePVkRyfiobmh4\nKR4nHdKtTJrrf3IMNWTnk8FaoRwJmlksDMH3hAo7cm0kfDnuhxUqJyNohoLehbgg\niqOXzVeGMjiCsIJ4oIk6soD5N/3ug9KEMJHGvdilZH/RkPTRyjo7ZQfmCh2MrzVD\nmqD0Dm8gMJTpLZTHX5Gm5dWoNjyIaJQQx7T9leh/OJHDcd1lbmxScgd2BoLeGmSD\nEj/omiy8XOMbnLokIk49xbsiDXVNk0GprnbpbI5aSjR6db+4kL4sazf5F5uwtfGD\nnp7PLth1heMiQRkIqI3Vb6QDrSUd00QSyj7x9A+hDfvzPcBvi+IGTSq1LwzGzihX\n8E0kUORFCSkodfMrCfv59lcO5je72DNWhvVvIs6sT3DLgj32wyn22FpjnLnTq85f\nYI+1KMFJdr0o9tJK/M1+1TbMQ1QHVt6T2x9SX/9AIXCakbRRvE/sOD256gZ9JP8R\nM81DQ446hvSH9b11CTvVLQbuqabGOf13HY893X+UVNAOJP7+hS0I2kdhZBOuUUPQ\n+4RZbPg4iqhRvm6hNFs8xA1nPZ13fbqEu1rXwzpEAy6q4XWr7+c=\n=RfwI\n-----END PGP SIGNATURE-----", "payload": "tree 95b9af12f0782196080587d1c48265ccfaa9cfd8\nparent ab3969fca7b21786cb84dbc64d42b5db12a6a3c3\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774419372 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774419372 -0700\n\nRemove apscheduler from package requirements\n", "verified_at": "2026-03-25T06:16:41Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/082843d6b846345ff00f0a363a401e60aa006aaf", "html_url": "https://github.com/Tautulli/Tautulli/commit/082843d6b846345ff00f0a363a401e60aa006aaf", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/082843d6b846345ff00f0a363a401e60aa006aaf/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "ab3969fca7b21786cb84dbc64d42b5db12a6a3c3", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/ab3969fca7b21786cb84dbc64d42b5db12a6a3c3", "html_url": "https://github.com/Tautulli/Tautulli/commit/ab3969fca7b21786cb84dbc64d42b5db12a6a3c3"}]}, {"sha": "0bb2877774e6d65703209f0c99c0a9043525d4d2", "node_id": "C_kwDOAducuNoAKDBiYjI4Nzc3NzRlNmQ2NTcwMzIwOWYwYzk5YzBhOTA0MzUyNWQ0ZDI", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-25T06:20:06Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-25T06:20:06Z"}, "message": "Bump pyobjc-core from 12.0 to 12.1 (#2658)\n\nBumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 12.0 to 12.1.\n- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)\n- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/main/docs/changelog.rst)\n- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v12.0...v12.1)\n\n---\nupdated-dependencies:\n- dependency-name: pyobjc-core\n  dependency-version: '12.1'\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "f7b030b37a1bab1e73b923fc5a3ba1995a834c83", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/f7b030b37a1bab1e73b923fc5a3ba1995a834c83"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/0bb2877774e6d65703209f0c99c0a9043525d4d2", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpw36WCRC1aQ7uu5UhlAAA2oUQACQIMDrC01c68wLcldgnAtVW\nNoyebd7zh9ugR0yjAAqIoPFbtjMTNCmKQXY522d9FJtbRkXCZB3hVgTBzmWzCEDu\nNv+uMJt7MkdUEztCUuP4MGLuaIyX4osf09cyQswOCdQidUcJhayJ2FG50usbXi5m\nBqZdRwFU18IRk6Ov2jvkaL16SY8WnIuSvCqFFpMllrddN+cIKh1W4IoE1xnAbaX9\nlOovRdhMe5OHvy2qqZrB+nVwsUWsCQihjHP6IwX1iH+oi4cIv8T7tP4hWyAeCixM\nt0QoBhCGwhhM/lKCyhHolooqpQ1ggLA/o/dnesfmP7RDRNMBiVQ6WKdwrtrjJgZD\nfxh0RI3kVS0+9Efa4rDACkVZvNFkNurU2V+24dM5VhMZahD+3lYGMuOj0NrLzAXl\naN7jOaiV9bw8s5xvX0Nco6S1ffGXZNaUsVxOGztOAPlEkLgZ3J8umrdI8/t1OxSx\nJI33zZrFGBqP9KaaQospTJ0GmHkyMrc8TzQRFpTafDB8FRwcUg/oKPPDV8EjjXqY\nhJrw08ZneDsVXj/wWZTCTuD8HOWBUlX2kzh8pGmAW7veoRzpcNbuF+mCGZWb2XDa\n9FSJPnzFmfcmuSoXPXvuexDzP+EwGDMV7EhbgIchlXCfEy2M+whsSpohLlS/xPUP\nQVYpl0Sm6TFm71sDt2xT\n=XQLA\n-----END PGP SIGNATURE-----\n", "payload": "tree f7b030b37a1bab1e73b923fc5a3ba1995a834c83\nparent 082843d6b846345ff00f0a363a401e60aa006aaf\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774419606 -0700\ncommitter GitHub <noreply@github.com> 1774419606 -0700\n\nBump pyobjc-core from 12.0 to 12.1 (#2658)\n\nBumps [pyobjc-core](https://github.com/ronaldoussoren/pyobjc) from 12.0 to 12.1.\n- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)\n- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/main/docs/changelog.rst)\n- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v12.0...v12.1)\n\n---\nupdated-dependencies:\n- dependency-name: pyobjc-core\n  dependency-version: '12.1'\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-25T06:20:07Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0bb2877774e6d65703209f0c99c0a9043525d4d2", "html_url": "https://github.com/Tautulli/Tautulli/commit/0bb2877774e6d65703209f0c99c0a9043525d4d2", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0bb2877774e6d65703209f0c99c0a9043525d4d2/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "082843d6b846345ff00f0a363a401e60aa006aaf", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/082843d6b846345ff00f0a363a401e60aa006aaf", "html_url": "https://github.com/Tautulli/Tautulli/commit/082843d6b846345ff00f0a363a401e60aa006aaf"}]}, {"sha": "d05439d2cf61d50bcc113ff4114eaf08ebbf0fc5", "node_id": "C_kwDOAducuNoAKGQwNTQzOWQyY2Y2MWQ1MGJjYzExM2ZmNDExNGVhZjA4ZWJiZjBmYzU", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-25T06:26:34Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-25T06:26:34Z"}, "message": "Bump pyobjc-framework-cocoa from 12.0 to 12.1 (#2659)\n\nBumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 12.0 to 12.1.\n- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)\n- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/main/docs/changelog.rst)\n- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v12.0...v12.1)\n\n---\nupdated-dependencies:\n- dependency-name: pyobjc-framework-cocoa\n  dependency-version: '12.1'\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "24046561ebdbaec45193b5ed57535f943290d591", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/24046561ebdbaec45193b5ed57535f943290d591"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/d05439d2cf61d50bcc113ff4114eaf08ebbf0fc5", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpw4AaCRC1aQ7uu5UhlAAAEMsQAHo7+2TquGIV78TzJYGNWqGL\nrEHFyd2WrDXQ9jbXJ7UpKMQSke1zoExQDriAi0HzXgM1mzV4eFBFf7NQ5oNpgLcI\nKb8jL/Bw3ttdu0XKj2iQBdxdb61nemFxiJNHzztLu0bH/WN5HRZnucH8w2S96UHK\n1t4FIyQyF9xrYyGOMktNROM6vcRiJZuZv9v5HxfepsFborS0uzSEBnrU0mehk4vu\ndn1tU2KhEvLOK0jDRlEV9VH5AQCRd+vEx+No+TB+/X4Z79GS5RcGLWJDFkACB3kx\n7HPUv4f6iFh9p7xiHXqeJj0DVFhq2jYV4TmEjC8Y7nTIxnWqNsutESjdNNAXcRbo\n7Ti2EF/isXv7r2H/OtohZTgWxl3G51LLXdW5eutavUW8jYsRTFhC7eOAFTnD8jZU\npzE5FLS+1JrRrOvN76xSxbNr1OjOfQquc8rTndgw7QLvhQehbQBpleZCzYYgLxtK\ngIs8KSVgqOpyZJneygD7TiE5ZOsrghslVSAlOqbYC+SSjkkiqSnCqideXaKZVXjk\nq6JJTJ6fw0He0nWIwPmHUWMaTDyVYHcfPDYKyHcwIeW+555zFTk8qnZ9t4ZhIYAV\nNXE67XxQDnDKSXcnT2s554UyTEUbV7uaZQRDmg2jd8EKLTSflgW95Y9E+42e1K0U\nSxnk+npkVc0skGJJ9wiQ\n=EX6S\n-----END PGP SIGNATURE-----\n", "payload": "tree 24046561ebdbaec45193b5ed57535f943290d591\nparent 0bb2877774e6d65703209f0c99c0a9043525d4d2\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774419994 -0700\ncommitter GitHub <noreply@github.com> 1774419994 -0700\n\nBump pyobjc-framework-cocoa from 12.0 to 12.1 (#2659)\n\nBumps [pyobjc-framework-cocoa](https://github.com/ronaldoussoren/pyobjc) from 12.0 to 12.1.\n- [Release notes](https://github.com/ronaldoussoren/pyobjc/releases)\n- [Changelog](https://github.com/ronaldoussoren/pyobjc/blob/main/docs/changelog.rst)\n- [Commits](https://github.com/ronaldoussoren/pyobjc/compare/v12.0...v12.1)\n\n---\nupdated-dependencies:\n- dependency-name: pyobjc-framework-cocoa\n  dependency-version: '12.1'\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-25T06:26:35Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d05439d2cf61d50bcc113ff4114eaf08ebbf0fc5", "html_url": "https://github.com/Tautulli/Tautulli/commit/d05439d2cf61d50bcc113ff4114eaf08ebbf0fc5", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d05439d2cf61d50bcc113ff4114eaf08ebbf0fc5/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "0bb2877774e6d65703209f0c99c0a9043525d4d2", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0bb2877774e6d65703209f0c99c0a9043525d4d2", "html_url": "https://github.com/Tautulli/Tautulli/commit/0bb2877774e6d65703209f0c99c0a9043525d4d2"}]}, {"sha": "18d27d0550edcbcd1bdf095aa68e79b5490fe5d8", "node_id": "C_kwDOAducuNoAKDE4ZDI3ZDA1NTBlZGNiY2QxYmRmMDk1YWE2OGU3OWI1NDkwZmU1ZDg", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-25T06:28:08Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-25T06:28:08Z"}, "message": "Bump importlib-metadata from 8.7.0 to 9.0.0 (#2660)\n\n* Bump importlib-metadata from 8.7.0 to 9.0.0\n\nBumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.7.0 to 9.0.0.\n- [Release notes](https://github.com/python/importlib_metadata/releases)\n- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)\n- [Commits](https://github.com/python/importlib_metadata/compare/v8.7.0...v9.0.0)\n\n---\nupdated-dependencies:\n- dependency-name: importlib-metadata\n  dependency-version: 9.0.0\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump importlib-metadata==9.0.0\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "tree": {"sha": "b3d13b7615fc3616d1ef29be4f7ce9787d5e13fc", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/b3d13b7615fc3616d1ef29be4f7ce9787d5e13fc"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/18d27d0550edcbcd1bdf095aa68e79b5490fe5d8", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpw4B4CRC1aQ7uu5UhlAAALw4QAI5YTYbCJHx5/Tg1Kyvdac5/\ncQDDGS2bbKulIZxa4FLYVIWPxeGQ4WRm78kUj6lkc+7mc3RzE7xYkBtivVqynBZ+\n6ljti5lbGUbczetFNs9AUuF17oj2fUuQrFil8iO4CQDane8M35k93J9h+3Upw5w5\nOR+pdtSybpKMPUjgH8a0gyAIGec/AvJdGeO+qinA9zRIZ4hXhc7nVwvmwlehOgGf\n6xh53/3r4cHoqh/z5wMOtlVeSwcoDU0oxANrPUGXrIcMD1ATntcDmLJkFGccdQyX\nNx6XZKHpYLQfpwh4HUoyLVnaQLCPh0AK+T03W5TarejCKCV3Z99UtcczfJ2GfT84\nsvHoIyj0ApOvE3q1ybg+v2/GvXOzM/S6UmzwRL+ZHhE8uPd9lVAtYR1+gBVf6xFH\nl9YSRZuM3H8cBmJHp5vErEY652lG34NjX08+Akb8SajYuJycbq/6RYTIKhdl/Eo7\nBipp68fprBvSSjVUv0nnkBxGeYwViQTYFqA4qHU8OyPD4RUSe6RipViFHykSk2iF\nGeXl6QKF9umRvcgjrVb524mcA/EzGAD7Te+2HzaLbiesbQF8EF26stFc79yRG5PO\nX/nAVMvjcKb0QIZIjMmnJcB4wriDDzHtSJgQ9acGw3Ev2fqm7cLGb5wVgNqzy6cQ\nxa49FRsoIe9/up+zTTic\n=wY9N\n-----END PGP SIGNATURE-----\n", "payload": "tree b3d13b7615fc3616d1ef29be4f7ce9787d5e13fc\nparent d05439d2cf61d50bcc113ff4114eaf08ebbf0fc5\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774420088 -0700\ncommitter GitHub <noreply@github.com> 1774420088 -0700\n\nBump importlib-metadata from 8.7.0 to 9.0.0 (#2660)\n\n* Bump importlib-metadata from 8.7.0 to 9.0.0\n\nBumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.7.0 to 9.0.0.\n- [Release notes](https://github.com/python/importlib_metadata/releases)\n- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)\n- [Commits](https://github.com/python/importlib_metadata/compare/v8.7.0...v9.0.0)\n\n---\nupdated-dependencies:\n- dependency-name: importlib-metadata\n  dependency-version: 9.0.0\n  dependency-type: direct:production\n  update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump importlib-metadata==9.0.0\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>\n\n[skip ci]", "verified_at": "2026-03-25T06:28:10Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/18d27d0550edcbcd1bdf095aa68e79b5490fe5d8", "html_url": "https://github.com/Tautulli/Tautulli/commit/18d27d0550edcbcd1bdf095aa68e79b5490fe5d8", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/18d27d0550edcbcd1bdf095aa68e79b5490fe5d8/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "d05439d2cf61d50bcc113ff4114eaf08ebbf0fc5", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/d05439d2cf61d50bcc113ff4114eaf08ebbf0fc5", "html_url": "https://github.com/Tautulli/Tautulli/commit/d05439d2cf61d50bcc113ff4114eaf08ebbf0fc5"}]}, {"sha": "23b2af1cf98a754adeede715dfc9a38306a9239d", "node_id": "C_kwDOAducuNoAKDIzYjJhZjFjZjk4YTc1NGFkZWVkZTcxNWRmYzlhMzgzMDZhOTIzOWQ", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-25T06:28:17Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-25T06:28:17Z"}, "message": "Bump plexapi from 4.18.0 to 4.18.1 (#2661)\n\n* Bump plexapi from 4.18.0 to 4.18.1\n\nBumps [plexapi](https://github.com/pushingkarmaorg/python-plexapi) from 4.18.0 to 4.18.1.\n- [Release notes](https://github.com/pushingkarmaorg/python-plexapi/releases)\n- [Commits](https://github.com/pushingkarmaorg/python-plexapi/compare/4.18.0...4.18.1)\n\n---\nupdated-dependencies:\n- dependency-name: plexapi\n  dependency-version: 4.18.1\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump plexapi==4.18.1\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "tree": {"sha": "5e7249837806eae31578a8535ddfc54e21f144e2", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/5e7249837806eae31578a8535ddfc54e21f144e2"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/23b2af1cf98a754adeede715dfc9a38306a9239d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpw4CBCRC1aQ7uu5UhlAAA0x0QAKdHw7U09Bz4ek9Hg9yYR/b7\nQsRsffeWuKFd6+K84aI3Of+5E5XqTUuPxGvTFe73yokkaTaEy9nK3MtVdsZyHgb7\nwLDsg3biCPfIHqPPTerqyxlf7Lbr6M6W/QnJDcl2do52niqYpx+ClpCNj+WlYMXh\nwVs8JNRK8lugPJw834yghHrTlfmYmOHX2mv3/sn8Dipoz2h5UzGSjTCQQPALnndO\nhnQ4maKW1ZYGtiJ3MQ60ZKN/GoZXyt1mGboXYqyrPURiihXbUUN3z7vyhvavOHrN\ndFG6t6BcQluJo9I4YSrZPyMLkyhslaPmvrdbK+IYeN19hiCxZR9qsX+Ft1LLewWF\nx/I0FUDCoZGk+tka3yT24szyfD3Weqe4WmW7LSnYjakxdVN3iujMRVoROlqAf3ZT\npeIYMF0SYGiGo7WKQ2z8FT66OBBTxZrTTxEeKeI1Z43wd5+vABbEzPZUS+niwtfP\nBQbPzSsfoydqf7EG8qzI5XCREVJe8tRzkOkATQZOwfM6m9b90OFh0TfeCvsdbW0s\n0LZamL9PFgKitiKONLSboKm5iB9g0zPy1PnXPiPi3ECDfWTgsJLt7imnASOCrvRL\nuND9TSXMvhtb6Bv/2T6d6eUcsqoJGHCsnpBGrUehczjWvVQGPV4oO90Fhx5ClOhw\ngDddNkoeWSJKysVcaX+a\n=VEoL\n-----END PGP SIGNATURE-----\n", "payload": "tree 5e7249837806eae31578a8535ddfc54e21f144e2\nparent 18d27d0550edcbcd1bdf095aa68e79b5490fe5d8\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774420097 -0700\ncommitter GitHub <noreply@github.com> 1774420097 -0700\n\nBump plexapi from 4.18.0 to 4.18.1 (#2661)\n\n* Bump plexapi from 4.18.0 to 4.18.1\n\nBumps [plexapi](https://github.com/pushingkarmaorg/python-plexapi) from 4.18.0 to 4.18.1.\n- [Release notes](https://github.com/pushingkarmaorg/python-plexapi/releases)\n- [Commits](https://github.com/pushingkarmaorg/python-plexapi/compare/4.18.0...4.18.1)\n\n---\nupdated-dependencies:\n- dependency-name: plexapi\n  dependency-version: 4.18.1\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump plexapi==4.18.1\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "verified_at": "2026-03-25T06:28:18Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/23b2af1cf98a754adeede715dfc9a38306a9239d", "html_url": "https://github.com/Tautulli/Tautulli/commit/23b2af1cf98a754adeede715dfc9a38306a9239d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/23b2af1cf98a754adeede715dfc9a38306a9239d/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "18d27d0550edcbcd1bdf095aa68e79b5490fe5d8", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/18d27d0550edcbcd1bdf095aa68e79b5490fe5d8", "html_url": "https://github.com/Tautulli/Tautulli/commit/18d27d0550edcbcd1bdf095aa68e79b5490fe5d8"}]}, {"sha": "57576199d97c37d67e221d91a96dc379fd42842d", "node_id": "C_kwDOAducuNoAKDU3NTc2MTk5ZDk3YzM3ZDY3ZTIyMWQ5MWE5NmRjMzc5ZmQ0Mjg0MmQ", "commit": {"author": {"name": "jma1ice", "email": "jammusicant@gmail.com", "date": "2026-03-26T06:04:48Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-26T06:04:48Z"}, "message": "Add rating field to get_home_stats API response (#2655)\n\n* Add rating field to get_home_stats API response\n\n* Updated so rating field is in all of the applicable API responses", "tree": {"sha": "3f78de1ee9a3198beb390ec66705137576d5f455", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/3f78de1ee9a3198beb390ec66705137576d5f455"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/57576199d97c37d67e221d91a96dc379fd42842d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpxMyACRC1aQ7uu5UhlAAAXKYQAAuTzUXTejoZeo3rRtjJ6wRM\njcUn4bQnYxBB9RoUc58v/5MMeF7a11iNB/yQ4ne3cQa42k/COzFQDtBgLlR0YfBh\nrsMRDSe7tzuyragjyHEhuhosxIFqVz6NT3JhSlvVvFpI0XFtcZ1u6XTy7HrMVbe/\n8tSacS3UtlOqb1LTVffEl4EPtuTmDh9ss6OIA5tnsvVUb2Ljz479imvkS8aevpJV\nkQ6jiIvVVllpoeGkvkAKnh+COOZBksDURpM32SaKolgQ8lvqFFddQvYhS2eAELXF\nNL3SNbSvIzCZqtBzjlXUiHU1+FH+QceEnz3YthDestBlDn8d8Eb7ycRECu/6V4Ms\nWb8xBk3MY+2ngn+ArdXPQHhjveJjIhvYS3GgrpcFFMgi6GQYVOJ9w7KLkiZjCvdZ\nvj9szOJoq1tocoXywds4uYrcYoFdp4I4V1m+Jjty/OLXBDh2X7BxSo3CHuVVc9/0\nSQVW0BGz7QdDQRNZffWTxqiylQzKyR1wv23uY8fVcLqLGJzX6wo4gjhSCQXLtqvo\nMc4NJa6KJ0Z3jc/OjnZxtn/c2FZkf1iC6/IYo5pNyrxtKxHRA7/3nDMzOZqvVH59\nb9sWmIsbxnxS4yP6szlUxlwWRkDPlKrNO1EYr6+dBviqcEBe7JDjOT2BEYrtS1I3\nBiJaVD64iLiO0X/VRT4W\n=+G3C\n-----END PGP SIGNATURE-----\n", "payload": "tree 3f78de1ee9a3198beb390ec66705137576d5f455\nparent 23b2af1cf98a754adeede715dfc9a38306a9239d\nauthor jma1ice <jammusicant@gmail.com> 1774505088 -0600\ncommitter GitHub <noreply@github.com> 1774505088 -0700\n\nAdd rating field to get_home_stats API response (#2655)\n\n* Add rating field to get_home_stats API response\n\n* Updated so rating field is in all of the applicable API responses", "verified_at": "2026-03-26T06:04:49Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/57576199d97c37d67e221d91a96dc379fd42842d", "html_url": "https://github.com/Tautulli/Tautulli/commit/57576199d97c37d67e221d91a96dc379fd42842d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/57576199d97c37d67e221d91a96dc379fd42842d/comments", "author": {"login": "jma1ice", "id": 207265579, "node_id": "U_kgDODFqfKw", "avatar_url": "https://avatars.githubusercontent.com/u/207265579?v=4", "gravatar_id": "", "url": "https://api.github.com/users/jma1ice", "html_url": "https://github.com/jma1ice", "followers_url": "https://api.github.com/users/jma1ice/followers", "following_url": "https://api.github.com/users/jma1ice/following{/other_user}", "gists_url": "https://api.github.com/users/jma1ice/gists{/gist_id}", "starred_url": "https://api.github.com/users/jma1ice/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/jma1ice/subscriptions", "organizations_url": "https://api.github.com/users/jma1ice/orgs", "repos_url": "https://api.github.com/users/jma1ice/repos", "events_url": "https://api.github.com/users/jma1ice/events{/privacy}", "received_events_url": "https://api.github.com/users/jma1ice/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "23b2af1cf98a754adeede715dfc9a38306a9239d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/23b2af1cf98a754adeede715dfc9a38306a9239d", "html_url": "https://github.com/Tautulli/Tautulli/commit/23b2af1cf98a754adeede715dfc9a38306a9239d"}]}, {"sha": "37ff03258db1a9fdfb24bcbc5463827bb728325d", "node_id": "C_kwDOAducuNoAKDM3ZmYwMzI1OGRiMWE5ZmRmYjI0YmNiYzU0NjM4MjdiYjcyODMyNWQ", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-27T15:30:52Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-27T15:30:52Z"}, "message": "Bump cryptography from 46.0.5 to 46.0.6 (#2665)\n\nBumps [cryptography](https://github.com/pyca/cryptography) from 46.0.5 to 46.0.6.\n- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)\n- [Commits](https://github.com/pyca/cryptography/compare/46.0.5...46.0.6)\n\n---\nupdated-dependencies:\n- dependency-name: cryptography\n  dependency-version: 46.0.6\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "tree": {"sha": "720a80de378e49767e3905ed7072dbb8ceb02946", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/720a80de378e49767e3905ed7072dbb8ceb02946"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/37ff03258db1a9fdfb24bcbc5463827bb728325d", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpxqKsCRC1aQ7uu5UhlAAAxF0QAIMLoS/oYnnq/HVMgrzJ30cB\nLToeHqZitKqeQ4QGzr3ze5SXgXx9p9MV+Q/Fo9wloSHk+KVb2FwbwoXHbRZWrxgZ\ndfptfvE5e51nAe7fu+0nuqy3nt5B0J56WX5mpT2oaibQzgNwlXiTxTIXDs/u3trt\nFRRhgGu92lBedUjvi2f5v35KLHqNqQoOi851Te/v7TZkLt3YvOC3Eo2y5fu0dSam\n4mtsPOzcf+0kQN2SMDEexrHDMMvZBSR8tWoymLfVSiocrkcFf9OgAkfWZyqrt4Er\nlhYPM5QNSirbD80yVy09B6s1MphXizoCB9614Ab1KUKWMskLrQtmC5QtRmrXXp3P\nWF3uD9tx3Kuuelr7fDHNc8gdEzqmQhqedrG9mEhGDQXYcYHK7hGEArzV2GdNhlCe\n5JpXiNsSB/PwAEZ5LcBRocqziQb/1rBJLKmxFAS9WeLATyQaN3b4S+FTIhNFrPIt\nbhLof3d/rLVXWJ5RtI8Ml6YBNM/HLhF6NwytJvbaIiVLvJdtee140M84DCHsZd9W\njRxR2+NhGwL4KzvgGbmJwJAz6zpOjAm4M7kuU4ii/3Y047bhDTHLoJarVAkIk1JG\n3USLpiFHkjyFR/wFkjuhbQvi98hPbSzvGBM7P75xauxJpYzXsds4EcoXAHKfUzr6\n43JsIzAtH21YZIiAHxJ4\n=igiX\n-----END PGP SIGNATURE-----\n", "payload": "tree 720a80de378e49767e3905ed7072dbb8ceb02946\nparent 57576199d97c37d67e221d91a96dc379fd42842d\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774625452 -0700\ncommitter GitHub <noreply@github.com> 1774625452 -0700\n\nBump cryptography from 46.0.5 to 46.0.6 (#2665)\n\nBumps [cryptography](https://github.com/pyca/cryptography) from 46.0.5 to 46.0.6.\n- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)\n- [Commits](https://github.com/pyca/cryptography/compare/46.0.5...46.0.6)\n\n---\nupdated-dependencies:\n- dependency-name: cryptography\n  dependency-version: 46.0.6\n  dependency-type: direct:production\n  update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", "verified_at": "2026-03-27T15:30:52Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/37ff03258db1a9fdfb24bcbc5463827bb728325d", "html_url": "https://github.com/Tautulli/Tautulli/commit/37ff03258db1a9fdfb24bcbc5463827bb728325d", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/37ff03258db1a9fdfb24bcbc5463827bb728325d/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "57576199d97c37d67e221d91a96dc379fd42842d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/57576199d97c37d67e221d91a96dc379fd42842d", "html_url": "https://github.com/Tautulli/Tautulli/commit/57576199d97c37d67e221d91a96dc379fd42842d"}]}, {"sha": "4ccd313a4b0e6b0162d286a1e86c9b7f4702101e", "node_id": "C_kwDOAducuNoAKDRjY2QzMTNhNGIwZTZiMDE2MmQyODZhMWU4NmM5YjdmNDcwMjEwMWU", "commit": {"author": {"name": "Anthony S", "email": "anthony@swierkosz.dev", "date": "2026-03-27T21:01:35Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-27T21:01:35Z"}, "message": "Update Bootstrap CSS to v3.4.1 and decouple overrides (#2662)\n\n* Externalize Bootstrap overrides to tautulli.css\n\n- Restores bootstrap.css to the official v3.3.5 distribution files.\n- Relocates Bootstrap customizations (body background, textarea focus) to tautulli.css.\n- Overrides font-face to map Glyphicons to Tautulli's specific font directory.\n\n* Update Bootstrap CSS to v3.4.1\n\n* Remove Bootstrap CSS source maps\n\n* Update Bootstrap CSS to v3.3.7", "tree": {"sha": "577b69a0f529c4d6100c0bddd57fa14e44d42875", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/577b69a0f529c4d6100c0bddd57fa14e44d42875"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/4ccd313a4b0e6b0162d286a1e86c9b7f4702101e", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpxvAvCRC1aQ7uu5UhlAAAFPsQAI6K2xcKifaloyuCD40wKsuD\n910QSkvwucYE7RvhxVfqawwvqCYTCj43kl6iDRsfxWnSpFZ9XpGQEYYyMS8+F0ag\nqOwjjFMBxi7OmC6yCPGX8H+aQFsqr8C/0zoq26RSMN3JNNPZ2REdaq6hF3R6UxWG\newUHjrroTgOG7BGROC7ldibROqHbzGbGFTHFDB+eAz4Ls8L/OjySqO+QlOTwoiuR\nz+h4bacuvUl/g/DpC2khDZweLVEBRUc9W5CaJHN4HxHr6+QM4gI7DvpExAa8W0Fa\nWxwoN72ej3I2/Sf7q52Ws74p9ny3iwO9MfeSEcRtE+fm0GAIzzPS4gQXsZBRVXt3\neWUT2UhxxAgamFvCQDFWRrA0CQsXYnhxRklrboFfaWxMtqIidWsZrflIox1ln7GC\nR75X3xT0LJIJC/ag4THe8icLvrJtLolwGUc28/Qes6NlsyFNbz9OSdBY71XoQkKy\nbtZK0Zxd3FR3x/71gEjzy0vf2ph+ftbqRUnxn0YCvqKFFOoMnqgEXGpEsHb+qO1c\nsDVW7JpfLVkePLOGU604rJvcryaFcn6vmQ9codoWBuS6/gexCh5/ZKhYc8GK8qHx\n/yP0rGIPV7dpBAFURcscXw+fojRZROzGHlJKo3wLTzV61CklQ1i6s6aXI/xANF+z\nDf/jNc/EN4K/ygUT9xk+\n=Gq/c\n-----END PGP SIGNATURE-----\n", "payload": "tree 577b69a0f529c4d6100c0bddd57fa14e44d42875\nparent 37ff03258db1a9fdfb24bcbc5463827bb728325d\nauthor Anthony S <anthony@swierkosz.dev> 1774645295 -0400\ncommitter GitHub <noreply@github.com> 1774645295 -0700\n\nUpdate Bootstrap CSS to v3.4.1 and decouple overrides (#2662)\n\n* Externalize Bootstrap overrides to tautulli.css\n\n- Restores bootstrap.css to the official v3.3.5 distribution files.\n- Relocates Bootstrap customizations (body background, textarea focus) to tautulli.css.\n- Overrides font-face to map Glyphicons to Tautulli's specific font directory.\n\n* Update Bootstrap CSS to v3.4.1\n\n* Remove Bootstrap CSS source maps\n\n* Update Bootstrap CSS to v3.3.7", "verified_at": "2026-03-27T21:01:36Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4ccd313a4b0e6b0162d286a1e86c9b7f4702101e", "html_url": "https://github.com/Tautulli/Tautulli/commit/4ccd313a4b0e6b0162d286a1e86c9b7f4702101e", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4ccd313a4b0e6b0162d286a1e86c9b7f4702101e/comments", "author": {"login": "aisgbnok", "id": 10838153, "node_id": "MDQ6VXNlcjEwODM4MTUz", "avatar_url": "https://avatars.githubusercontent.com/u/10838153?v=4", "gravatar_id": "", "url": "https://api.github.com/users/aisgbnok", "html_url": "https://github.com/aisgbnok", "followers_url": "https://api.github.com/users/aisgbnok/followers", "following_url": "https://api.github.com/users/aisgbnok/following{/other_user}", "gists_url": "https://api.github.com/users/aisgbnok/gists{/gist_id}", "starred_url": "https://api.github.com/users/aisgbnok/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/aisgbnok/subscriptions", "organizations_url": "https://api.github.com/users/aisgbnok/orgs", "repos_url": "https://api.github.com/users/aisgbnok/repos", "events_url": "https://api.github.com/users/aisgbnok/events{/privacy}", "received_events_url": "https://api.github.com/users/aisgbnok/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "37ff03258db1a9fdfb24bcbc5463827bb728325d", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/37ff03258db1a9fdfb24bcbc5463827bb728325d", "html_url": "https://github.com/Tautulli/Tautulli/commit/37ff03258db1a9fdfb24bcbc5463827bb728325d"}]}, {"sha": "656315c75236dee4591a0bc04850b76882556f67", "node_id": "C_kwDOAducuNoAKDY1NjMxNWM3NTIzNmRlZTQ1OTFhMGJjMDQ4NTBiNzY4ODI1NTZmNjc", "commit": {"author": {"name": "Anthony S", "email": "anthony@swierkosz.dev", "date": "2026-03-28T00:34:24Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-28T00:34:24Z"}, "message": "Update Bootstrap-select to v1.13.18 (#2666)", "tree": {"sha": "d26c6fbc7855f89416eedbdad3a4dd3fa19037d1", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/d26c6fbc7855f89416eedbdad3a4dd3fa19037d1"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/656315c75236dee4591a0bc04850b76882556f67", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpxyIQCRC1aQ7uu5UhlAAAY38QAAbHpSsUFxsZi+l/ioTZ9/Ld\n6bHC7VO919aFOGlW/LKS87R/R/JYknKy8p2bx8+abxh+gyZtUQd2/WT79XbfGlbD\n9OOVPt+HJA64ABcHGj7DX+lKem8bb63CKhlYD+6s+LFXhrnWDU6mBbfmd3IISF6j\nTe0sS2pfOSdEbJdXqy8V4C7VjSY1ZSzosPJRoqIlX1eCFSbRAPjy8xakir0YC+zw\nb1ssP7fo2DwlWIiHxxQYT+5rq0RdusNOAtxezVkbexLfEhTOUKERQW6yY4JWWDTT\nI4T93LFYHPVuPU+KYzCjaNGFrJ3hxdLh5AtQ0X57okX2gCyjA9oBuG1SDSxquOvM\nm7img/0B7ihl/edMcd0CtYS+9aOMMWxvSz4cXpKuOLrpGPe7zO2paImrdYih9xCy\nvvVc/mrBZ7NabLB5ZACYHxpi1Vv7Jzf3iko055+/EGutJMUP182Mp3j1OYFWyhcQ\n43dfncdBlNKCEM6luh5bo0xOGeS/igiAByCaMmrnSV3sPC+nU8A/zGXV6xj/bb1Z\nb67FdBozMDvgUiNO9wtiGAgJPpreKzrQJQYXCa7x5JMCkNzZPXYAgFPj/b56Oy/4\nJW829EZdtiZG+51erIQSqxAd+EbV/8xUR+u6bD3yugbz1W8gi6pLNAaFlCVRDG7U\nV7ImKpVj0w45+ibLhQXf\n=MXzL\n-----END PGP SIGNATURE-----\n", "payload": "tree d26c6fbc7855f89416eedbdad3a4dd3fa19037d1\nparent 4ccd313a4b0e6b0162d286a1e86c9b7f4702101e\nauthor Anthony S <anthony@swierkosz.dev> 1774658064 -0400\ncommitter GitHub <noreply@github.com> 1774658064 -0700\n\nUpdate Bootstrap-select to v1.13.18 (#2666)", "verified_at": "2026-03-28T00:34:24Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/656315c75236dee4591a0bc04850b76882556f67", "html_url": "https://github.com/Tautulli/Tautulli/commit/656315c75236dee4591a0bc04850b76882556f67", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/656315c75236dee4591a0bc04850b76882556f67/comments", "author": {"login": "aisgbnok", "id": 10838153, "node_id": "MDQ6VXNlcjEwODM4MTUz", "avatar_url": "https://avatars.githubusercontent.com/u/10838153?v=4", "gravatar_id": "", "url": "https://api.github.com/users/aisgbnok", "html_url": "https://github.com/aisgbnok", "followers_url": "https://api.github.com/users/aisgbnok/followers", "following_url": "https://api.github.com/users/aisgbnok/following{/other_user}", "gists_url": "https://api.github.com/users/aisgbnok/gists{/gist_id}", "starred_url": "https://api.github.com/users/aisgbnok/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/aisgbnok/subscriptions", "organizations_url": "https://api.github.com/users/aisgbnok/orgs", "repos_url": "https://api.github.com/users/aisgbnok/repos", "events_url": "https://api.github.com/users/aisgbnok/events{/privacy}", "received_events_url": "https://api.github.com/users/aisgbnok/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "4ccd313a4b0e6b0162d286a1e86c9b7f4702101e", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/4ccd313a4b0e6b0162d286a1e86c9b7f4702101e", "html_url": "https://github.com/Tautulli/Tautulli/commit/4ccd313a4b0e6b0162d286a1e86c9b7f4702101e"}]}, {"sha": "aac32b3b5e39579094f965c508177980a78f05a5", "node_id": "C_kwDOAducuNoAKGFhYzMyYjNiNWUzOTU3OTA5NGY5NjVjNTA4MTc3OTgwYTc4ZjA1YTU", "commit": {"author": {"name": "dependabot[bot]", "email": "49699333+dependabot[bot]@users.noreply.github.com", "date": "2026-03-28T00:39:41Z"}, "committer": {"name": "GitHub", "email": "noreply@github.com", "date": "2026-03-28T00:39:41Z"}, "message": "Bump requests from 2.32.5 to 2.33.0 (#2663)\n\n* Bump requests from 2.32.5 to 2.33.0\n\nBumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.33.0.\n- [Release notes](https://github.com/psf/requests/releases)\n- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)\n- [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.33.0)\n\n---\nupdated-dependencies:\n- dependency-name: requests\n  dependency-version: 2.33.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump requests==2.33.0\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "tree": {"sha": "0f3c72a4a81d7e719c34f3e3550f8d2d9feb3d91", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/0f3c72a4a81d7e719c34f3e3550f8d2d9feb3d91"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/aac32b3b5e39579094f965c508177980a78f05a5", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJpxyNOCRC1aQ7uu5UhlAAAAMoQACAkvVvSQD/NltDNz+M3rhAm\nU0ll90EqFVepMkV2At41ym8TrG1q4AJLEa2WBWSepxd5Rl5gSPBW7TWkXWiJyPqE\nE3XltBS91rUPFRPFehPbdv6sanNM/wQf6adOaJm9CEBBb1ENPp935vQ+Q5+i6GHD\nOd/hijr059TzBxUt8+yEfiXzgVHov/CqhyAX73aItBvAo/U5q/XwOv5ZilMRlbSM\nQQudt9C/k8k00HqhfNjVRvL96INnmerFhJFu1SPSNSUbdSTKpLbNDJdzZpnHLTRY\n/SynKW823RzcD6gG0AAGM8bF1W6hBfQgteanoUShPWIzsbcIEIZbFrEzFq00k9Wp\nTP+6pI9As78x7qPngfzB2fWdkdwyo0yhuUoadDfX+eSIYMWg5ijSOPO+2RmuCC9M\nf0eEBVDAm0jHFv1qq/LXi2YVCn5d2ODwpwU96/iZJ2s1q2VyMe1jP8PlgLUgtFNl\neERwdx7ZmdyjXZlARV8R+3imMdQhLz77Zx9snuODKqvtIawqO44BMq4bru8cj3Hh\nGuG+3moHYLzk2ktWgBSeL9S8fDpBT6fDvgtF1ZqIevkCeEvY0/hxZdrfaBitDESm\nbTVzgrMT3mNNZsMsZNC76AEakpgAhDIUTBssAW0qe+uam7HJpDncALk0CYOndl59\nsK8CyA59ZhceQcOxXYCp\n=u3l0\n-----END PGP SIGNATURE-----\n", "payload": "tree 0f3c72a4a81d7e719c34f3e3550f8d2d9feb3d91\nparent 656315c75236dee4591a0bc04850b76882556f67\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1774658381 -0700\ncommitter GitHub <noreply@github.com> 1774658381 -0700\n\nBump requests from 2.32.5 to 2.33.0 (#2663)\n\n* Bump requests from 2.32.5 to 2.33.0\n\nBumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.33.0.\n- [Release notes](https://github.com/psf/requests/releases)\n- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)\n- [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.33.0)\n\n---\nupdated-dependencies:\n- dependency-name: requests\n  dependency-version: 2.33.0\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\n\n* Bump requests==2.33.0\n\n---------\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\nCo-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>", "verified_at": "2026-03-28T00:39:42Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/aac32b3b5e39579094f965c508177980a78f05a5", "html_url": "https://github.com/Tautulli/Tautulli/commit/aac32b3b5e39579094f965c508177980a78f05a5", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/aac32b3b5e39579094f965c508177980a78f05a5/comments", "author": {"login": "dependabot[bot]", "id": 49699333, "node_id": "MDM6Qm90NDk2OTkzMzM=", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "gravatar_id": "", "url": "https://api.github.com/users/dependabot%5Bbot%5D", "html_url": "https://github.com/apps/dependabot", "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false}, "committer": {"login": "web-flow", "id": 19864447, "node_id": "MDQ6VXNlcjE5ODY0NDQ3", "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", "gravatar_id": "", "url": "https://api.github.com/users/web-flow", "html_url": "https://github.com/web-flow", "followers_url": "https://api.github.com/users/web-flow/followers", "following_url": "https://api.github.com/users/web-flow/following{/other_user}", "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", "organizations_url": "https://api.github.com/users/web-flow/orgs", "repos_url": "https://api.github.com/users/web-flow/repos", "events_url": "https://api.github.com/users/web-flow/events{/privacy}", "received_events_url": "https://api.github.com/users/web-flow/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "656315c75236dee4591a0bc04850b76882556f67", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/656315c75236dee4591a0bc04850b76882556f67", "html_url": "https://github.com/Tautulli/Tautulli/commit/656315c75236dee4591a0bc04850b76882556f67"}]}, {"sha": "0afc5b7e50c4d86977bfd7fc583643e2925475cc", "node_id": "C_kwDOAducuNoAKDBhZmM1YjdlNTBjNGQ4Njk3N2JmZDdmYzU4MzY0M2UyOTI1NDc1Y2M", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-28T00:44:47Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-28T00:44:47Z"}, "message": "Fix updater unknown version check", "tree": {"sha": "764792f15dc94824d19ca253c4cef42aa1a1d305", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/764792f15dc94824d19ca253c4cef42aa1a1d305"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/0afc5b7e50c4d86977bfd7fc583643e2925475cc", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmnHJH8ACgkQm/TvTexB\nFjrKcBAAp0FLzJu6ebm8zbFkr92QjTgYLFFgFif/R/hQjzkBWsRywaxg0QxZQT7z\nOF1uekdvNe/ZxCxDpQ0rzj2G/RDvGNug9KjJv8bakeCG9kOcgQfZsUjLijJOPtS8\n4QM/4ub6sZwKoOE+Ovz7nhudMOgf6m+ZkfB6fOYxNMERUmtnWfFcu18oDy3UlV69\nUlJFrg7jyvWevgM0hvn7z2BA8VMzg1Jci5Dkj+XtkzNrDuNTWxabLg9y9uM09BjC\n28tsqt9rSOFi+RQ8glOTzYfqbCsOy1aN8TPJ9Ol9KH0Qf6HRhdEluCsGEepOca7l\nV8SQACvRwoTQBYEHCBkXKUFk4RwKx9ZlnG+AKZCjx7/iXjuXUDRkJIkkI6LDa60D\nW5kBh858V509Ki+XySjploD49ID0kfgmBmlkbmXnryVmlMc4UEq29lehS5VZX1lD\nZOMbThbCf5qhrFx1emXvk6juVb/XSh5/L1aCe3IAIftfKMFIKzLfWmTfvuw9x9Jq\nruY/0WOYentqxQb3IFnQJnd1e7LW/84XNh0K52TocjB3iSLG1D/LeKkLIP0jD1Sx\nOeJGGhu9ZtKY/VkC37cNZmKkgWYjm09EgxDmeuWMJSYdjseZBfm2ju8lTeeRLx2Z\n4D/J28ZcTqYoEisF0ZPXmHy59lDPCjUwWOHNqhi8ATe7ltENPh8=\n=DDH7\n-----END PGP SIGNATURE-----", "payload": "tree 764792f15dc94824d19ca253c4cef42aa1a1d305\nparent aac32b3b5e39579094f965c508177980a78f05a5\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774658687 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774658687 -0700\n\nFix updater unknown version check\n", "verified_at": "2026-03-28T00:47:18Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0afc5b7e50c4d86977bfd7fc583643e2925475cc", "html_url": "https://github.com/Tautulli/Tautulli/commit/0afc5b7e50c4d86977bfd7fc583643e2925475cc", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0afc5b7e50c4d86977bfd7fc583643e2925475cc/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "aac32b3b5e39579094f965c508177980a78f05a5", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/aac32b3b5e39579094f965c508177980a78f05a5", "html_url": "https://github.com/Tautulli/Tautulli/commit/aac32b3b5e39579094f965c508177980a78f05a5"}]}, {"sha": "5610c167a17b0898d93d0588a0df81c03306ac52", "node_id": "C_kwDOAducuNoAKDU2MTBjMTY3YTE3YjA4OThkOTNkMDU4OGEwZGY4MWMwMzMwNmFjNTI", "commit": {"author": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-28T00:46:41Z"}, "committer": {"name": "JonnyWong16", "email": "9099342+JonnyWong16@users.noreply.github.com", "date": "2026-03-28T00:46:41Z"}, "message": "v2.17.0", "tree": {"sha": "3d24d369133798d8343e50c6bf5143c55d09f6df", "url": "https://api.github.com/repos/Tautulli/Tautulli/git/trees/3d24d369133798d8343e50c6bf5143c55d09f6df"}, "url": "https://api.github.com/repos/Tautulli/Tautulli/git/commits/5610c167a17b0898d93d0588a0df81c03306ac52", "comment_count": 0, "verification": {"verified": true, "reason": "valid", "signature": "-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCAAdFiEE/AYfcug8u49k3WJym/TvTexBFjoFAmnHJPEACgkQm/TvTexB\nFjqH8w/+K7D3vj7S88kMkxxsrWZXpJnVPveNLVi+dYbHwfFAKhZZ6j7+gKNRl+lV\nVKGY1Qto/3VdqrDWN1yHLkYJAXXOoKaC3rON5uauKoYwGYeivaenHoCgJPtZYD1A\nYU7+exiDBfinu/qSU+PlWGhP4cgR9KrKFZBGgRGkAMGNcsKrXo7KW7BdRZbKAwSz\nUw/82Vh6YR+VAArrkGYJAveOZBRyKHBTfcYLWXlU2YXs9MljFClGoyZSA+V0349x\na79WY/VJQ/XvS0ZcLCsoMuvmEwZacB7s/L4GhDoL0cWftkR68cf0hhjxl/ALLW4b\nnMH5ggCP7HatGRGW1TPxX06FZNEz+oQtOKdCYtlTXl56/Z5GrVKv2sRUhTCRHG7R\niVRTwxmDRz3Q784MTcx1dfWAOkCVMpc5HpkErpv527ZSEnp1cLCLIwjwRqwWUCSq\nRWXwZtXLzThXANYHtFRRkF5hdEqR7jPc3B+cr+ovVp0UjuWvZDQvyboduv4ToYSo\nCW4MAaUu0uzHHScwpf6wx4c2R7tL4Pl57byWT66uwmjeUVNaWl44StBV+GPLtOF/\nVYAfMsWv0ZocTzp/PrXZXwOFoXziVMiBg6t3npJDq5IlH/NjqHxz0h2okbeyRXwr\nvuuMci6EsTkeze67C9wpFWZR9CR2WPsGQ9Wj7yZLMmvb67xLyJM=\n=gj4w\n-----END PGP SIGNATURE-----", "payload": "tree 3d24d369133798d8343e50c6bf5143c55d09f6df\nparent 0afc5b7e50c4d86977bfd7fc583643e2925475cc\nauthor JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774658801 -0700\ncommitter JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> 1774658801 -0700\n\nv2.17.0\n", "verified_at": "2026-03-28T00:47:18Z"}}, "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5610c167a17b0898d93d0588a0df81c03306ac52", "html_url": "https://github.com/Tautulli/Tautulli/commit/5610c167a17b0898d93d0588a0df81c03306ac52", "comments_url": "https://api.github.com/repos/Tautulli/Tautulli/commits/5610c167a17b0898d93d0588a0df81c03306ac52/comments", "author": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "committer": {"login": "JonnyWong16", "id": 9099342, "node_id": "MDQ6VXNlcjkwOTkzNDI=", "avatar_url": "https://avatars.githubusercontent.com/u/9099342?v=4", "gravatar_id": "", "url": "https://api.github.com/users/JonnyWong16", "html_url": "https://github.com/JonnyWong16", "followers_url": "https://api.github.com/users/JonnyWong16/followers", "following_url": "https://api.github.com/users/JonnyWong16/following{/other_user}", "gists_url": "https://api.github.com/users/JonnyWong16/gists{/gist_id}", "starred_url": "https://api.github.com/users/JonnyWong16/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/JonnyWong16/subscriptions", "organizations_url": "https://api.github.com/users/JonnyWong16/orgs", "repos_url": "https://api.github.com/users/JonnyWong16/repos", "events_url": "https://api.github.com/users/JonnyWong16/events{/privacy}", "received_events_url": "https://api.github.com/users/JonnyWong16/received_events", "type": "User", "user_view_type": "public", "site_admin": false}, "parents": [{"sha": "0afc5b7e50c4d86977bfd7fc583643e2925475cc", "url": "https://api.github.com/repos/Tautulli/Tautulli/commits/0afc5b7e50c4d86977bfd7fc583643e2925475cc", "html_url": "https://github.com/Tautulli/Tautulli/commit/0afc5b7e50c4d86977bfd7fc583643e2925475cc"}]}], "files": [{"sha": "f22818019e8003abbdc87671d770fe78544e152b", "filename": ".github/workflows/codeql.yml", "status": "modified", "additions": 3, "deletions": 3, "changes": 6, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fcodeql.yml", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fcodeql.yml", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/.github%2Fworkflows%2Fcodeql.yml?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -24,15 +24,15 @@ jobs:\n \n     steps:\n     - name: Checkout Code\n-      uses: actions/checkout@v4\n+      uses: actions/checkout@v6\n \n     - name: Initialize CodeQL\n-      uses: github/codeql-action/init@v3\n+      uses: github/codeql-action/init@v4\n       with:\n         config-file: ./.github/codeql-config.yml\n         languages: ${{ matrix.language }}\n \n     - name: Perform CodeQL Analysis\n-      uses: github/codeql-action/analyze@v3\n+      uses: github/codeql-action/analyze@v4\n       with:\n         category: \"/language:${{matrix.language}}\""}, {"sha": "5ba1fd0e43c10a19d37513dc61951b56990d266b", "filename": ".github/workflows/issues-stale.yml", "status": "modified", "additions": 2, "deletions": 2, "changes": 4, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fissues-stale.yml", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fissues-stale.yml", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/.github%2Fworkflows%2Fissues-stale.yml?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -10,7 +10,7 @@ jobs:\n     runs-on: ubuntu-latest\n     steps:\n       - name: Stale\n-        uses: actions/stale@v9\n+        uses: actions/stale@v10\n         with:\n           stale-issue-message: >\n             This issue is stale because it has been open for 30 days with no activity.\n@@ -30,7 +30,7 @@ jobs:\n           days-before-close: 5\n \n       - name: Invalid Template\n-        uses: actions/stale@v9\n+        uses: actions/stale@v10\n         with:\n           stale-issue-message: >\n             Invalid issues template."}, {"sha": "c0ab3cca1427623dfb1e7631749cf73edbd14546", "filename": ".github/workflows/issues.yml", "status": "modified", "additions": 1, "deletions": 1, "changes": 2, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fissues.yml", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fissues.yml", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/.github%2Fworkflows%2Fissues.yml?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -10,6 +10,6 @@ jobs:\n     runs-on: ubuntu-latest\n     steps:\n       - name: Label Issues\n-        uses: dessant/label-actions@v4\n+        uses: dessant/label-actions@v5\n         with:\n           github-token: ${{ github.token }}"}, {"sha": "eaf626cd2279a7d7431c36f63779114f6f730266", "filename": ".github/workflows/publish-docker.yml", "status": "modified", "additions": 9, "deletions": 17, "changes": 26, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fpublish-docker.yml", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fpublish-docker.yml", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/.github%2Fworkflows%2Fpublish-docker.yml?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -13,7 +13,7 @@ jobs:\n     if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }}\n     steps:\n       - name: Checkout Code\n-        uses: actions/checkout@v4\n+        uses: actions/checkout@v6\n \n       - name: Prepare\n         id: prepare\n@@ -37,31 +37,23 @@ jobs:\n           echo \"docker_image=${{ secrets.DOCKER_REPO }}/tautulli\" >> $GITHUB_OUTPUT\n \n       - name: Set Up QEMU\n-        uses: docker/setup-qemu-action@v3\n+        uses: docker/setup-qemu-action@v4\n \n       - name: Set up Docker Buildx\n-        uses: docker/setup-buildx-action@v3\n+        uses: docker/setup-buildx-action@v4\n         id: buildx\n         with:\n           version: latest\n \n-      - name: Cache Docker Layers\n-        uses: actions/cache@v4\n-        with:\n-          path: /tmp/.buildx-cache\n-          key: ${{ runner.os }}-buildx-${{ github.sha }}\n-          restore-keys: |\n-            ${{ runner.os }}-buildx-\n-\n       - name: Login to DockerHub\n-        uses: docker/login-action@v3\n+        uses: docker/login-action@v4\n         if: success()\n         with:\n           username: ${{ secrets.DOCKER_USERNAME }}\n           password: ${{ secrets.DOCKER_TOKEN }}\n \n       - name: Login to GitHub Container Registry\n-        uses: docker/login-action@v3\n+        uses: docker/login-action@v4\n         if: success()\n         with:\n           registry: ghcr.io\n@@ -70,12 +62,12 @@ jobs:\n \n       - name: Extract Docker Metadata\n         id: metadata\n-        uses: docker/metadata-action@v5\n+        uses: docker/metadata-action@v6\n         with:\n           images: ${{ steps.prepare.outputs.docker_image }}\n \n       - name: Docker Build and Push\n-        uses: docker/build-push-action@v6\n+        uses: docker/build-push-action@v7\n         if: success()\n         with:\n           context: .\n@@ -90,8 +82,8 @@ jobs:\n             ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }}\n             ghcr.io/${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }}\n           labels: ${{ steps.metadata.outputs.labels }}\n-          cache-from: type=local,src=/tmp/.buildx-cache\n-          cache-to: type=local,dest=/tmp/.buildx-cache\n+          cache-from: type=gha\n+          cache-to: type=gha,mode=max\n \n   discord:\n     name: Discord Notification"}, {"sha": "eb2a557d80b062d048baa8fe208756381fd6c1d1", "filename": ".github/workflows/publish-installers.yml", "status": "modified", "additions": 12, "deletions": 8, "changes": 20, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fpublish-installers.yml", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fpublish-installers.yml", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/.github%2Fworkflows%2Fpublish-installers.yml?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -31,7 +31,7 @@ jobs:\n \n     steps:\n       - name: Checkout Code\n-        uses: actions/checkout@v4\n+        uses: actions/checkout@v6\n \n       - name: Set Release Version\n         id: get_version\n@@ -59,7 +59,7 @@ jobs:\n           echo $GITHUB_SHA > version.txt\n \n       - name: Set Up Python\n-        uses: actions/setup-python@v5\n+        uses: actions/setup-python@v6\n         with:\n           python-version: ${{ env.PYTHON_VERSION }}\n           cache: pip\n@@ -74,8 +74,12 @@ jobs:\n         run: |\n           pyinstaller -y ./package/Tautulli-${{ matrix.os }}.spec\n \n+      - name: Install makensis (choco)\n+        if: matrix.os == 'windows'\n+        run: choco install nsis\n+      \n       - name: Create Windows Installer\n-        uses: joncloud/makensis-action@v4.1\n+        uses: joncloud/makensis-action@v5.0\n         if: matrix.os == 'windows'\n         with:\n           script-file: ./package/Tautulli.nsi\n@@ -95,7 +99,7 @@ jobs:\n             Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-${{ matrix.arch }}.${{ matrix.ext }}\n \n       - name: Upload Installer\n-        uses: actions/upload-artifact@v4\n+        uses: actions/upload-artifact@v7\n         with:\n           name: Tautulli-${{ matrix.os }}-installer\n           path: Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-${{ matrix.arch }}.${{ matrix.ext }}\n@@ -108,10 +112,10 @@ jobs:\n     steps:\n       - name: Download Installers\n         if: needs.build-installer.result == 'success'\n-        uses: actions/download-artifact@v4\n+        uses: actions/download-artifact@v8\n \n       - name: Upload to VirusTotal\n-        uses: crazy-max/ghaction-virustotal@v4\n+        uses: crazy-max/ghaction-virustotal@v5\n         with:\n           vt_api_key: ${{ secrets.VT_API_KEY }}\n           files: |\n@@ -125,7 +129,7 @@ jobs:\n     runs-on: ubuntu-latest\n     steps:\n       - name: Checkout Code\n-        uses: actions/checkout@v4\n+        uses: actions/checkout@v6\n \n       - name: Set Release Version\n         id: get_version\n@@ -134,7 +138,7 @@ jobs:\n \n       - name: Download Installers\n         if: needs.build-installer.result == 'success'\n-        uses: actions/download-artifact@v4\n+        uses: actions/download-artifact@v8\n \n       - name: Get Changelog\n         id: get_changelog"}, {"sha": "b69f5873947ef3daaa92005248de38abab76277b", "filename": ".github/workflows/publish-snap.yml", "status": "modified", "additions": 3, "deletions": 3, "changes": 6, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fpublish-snap.yml", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fpublish-snap.yml", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/.github%2Fworkflows%2Fpublish-snap.yml?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -20,7 +20,7 @@ jobs:\n           - armhf\n     steps:\n       - name: Checkout Code\n-        uses: actions/checkout@v4\n+        uses: actions/checkout@v6\n \n       - name: Prepare\n         id: prepare\n@@ -35,7 +35,7 @@ jobs:\n           fi\n \n       - name: Set Up QEMU\n-        uses: docker/setup-qemu-action@v3\n+        uses: docker/setup-qemu-action@v4\n \n       - name: Build Snap Package\n         uses: diddlesnaps/snapcraft-multiarch-action@master\n@@ -44,7 +44,7 @@ jobs:\n           architecture: ${{ matrix.architecture }}\n \n       - name: Upload Snap Package\n-        uses: actions/upload-artifact@v4\n+        uses: actions/upload-artifact@v7\n         with:\n           name: Tautulli-snap-package-${{ matrix.architecture }}\n           path: ${{ steps.build.outputs.snap }}"}, {"sha": "7e66aca38dd6db86bb1872b941052409ff231fe8", "filename": ".github/workflows/pull-requests.yml", "status": "modified", "additions": 2, "deletions": 2, "changes": 4, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fpull-requests.yml", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fpull-requests.yml", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/.github%2Fworkflows%2Fpull-requests.yml?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -10,10 +10,10 @@ jobs:\n     runs-on: ubuntu-latest\n     steps:\n       - name: Checkout Code\n-        uses: actions/checkout@v4\n+        uses: actions/checkout@v6\n \n       - name: Comment on Pull Request\n-        uses: mshick/add-pr-comment@v2\n+        uses: mshick/add-pr-comment@v3\n         if: github.base_ref != 'nightly'\n         with:\n           message: Pull requests must be made to the `nightly` branch. Thanks."}, {"sha": "e1e01337a02affc95bd3706e8b5ce86e400583c2", "filename": ".github/workflows/submit-winget.yml", "status": "modified", "additions": 6, "deletions": 1, "changes": 7, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fsubmit-winget.yml", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/.github%2Fworkflows%2Fsubmit-winget.yml", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/.github%2Fworkflows%2Fsubmit-winget.yml?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -11,6 +11,11 @@ jobs:\n     runs-on: windows-latest\n     if: ${{ !github.event.release.prerelease }}\n     steps:\n+      - name: Sync Winget Fork\n+        run: gh repo sync ${{ secrets.WINGET_USERNAME }}/winget-pkgs -b master\n+        env:\n+          GH_TOKEN: ${{ secrets.WINGET_TOKEN }}\n+\n       - name: Submit package to Windows Package Manager Community Repository\n         run: |\n           $wingetPackage = \"Tautulli.Tautulli\"\n@@ -29,7 +34,7 @@ jobs:\n     runs-on: ubuntu-latest\n     steps:\n       - name: Upload to VirusTotal\n-        uses: crazy-max/ghaction-virustotal@v4\n+        uses: crazy-max/ghaction-virustotal@v5\n         with:\n           vt_api_key: ${{ secrets.VT_API_KEY }}\n           github_token: ${{ secrets.GHACTIONS_TOKEN }}"}, {"sha": "03b01d62643560bbcfdb6e4ac1b3afee1eb4a218", "filename": ".gitignore", "status": "modified", "additions": 1, "deletions": 0, "changes": 1, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/.gitignore", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/.gitignore", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/.gitignore?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -18,6 +18,7 @@ version.lock\n lib/*.dist-info\n lib/*.egg-info\n lib/*.pth\n+lib/*.pyd\n lib/*/*.pyd\n lib/_distutils_hack\n lib/pkg_resources"}, {"sha": "7bf3c824125f5eefde848638157d567a981a6dc8", "filename": "CHANGELOG.md", "status": "modified", "additions": 100, "deletions": 2, "changes": 102, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/CHANGELOG.md", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/CHANGELOG.md", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/CHANGELOG.md?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,15 +1,113 @@\n # Changelog\n \n+## v2.17.0 (2026-03-27)\n+\n+* Important Note!\n+  * Several security vulnerabilities have been identified in Tautulli versions <=2.16.1. Users are strongly encouraged to update to the latest Tautulli version 2.17.x.\n+* Notifications:\n+  * Fix: Prevent RCE in notification text evaluation. (CVE-2026-28505) (Thanks @q1uf3ng)\n+* Newsletters:\n+  * Fix: Media from other video libraries using the modern Plex agents not showing up on newsletter.\n+  * Fix: Unauthenticated path traversal in /newsletter/image/images endpoint. (CVE-2026-31831) (Thanks @JakePeralta7)\n+* Exporter:\n+  * Fix: Logo images incorrectly exported as jpg instead of png.\n+  * New: Added ability to export square art images.\n+  * New: Added ability to export theme music. (#2654)\n+* Graphs:\n+  * Fix: History modal not opening when clicking on graphs. (#2652)\n+* API:\n+  * Fix: SQL injection in get_home_stats API command. (CVE-2026-31799) (Thanks @mandreko)\n+  * Fix: Unsanitized JSONP callback parameter. (CVE-2026-32275) (Thanks @mandreko)\n+  * New: Added rating to get_home_stats API command. (#2655) (Thanks @jma1ice)\n+  * Removed: get_apikey API command.\n+* Other:\n+  * Fix: Validate log path for Plex log files. (#2632)\n+  * Fix: Add authentication to /pms_image_proxy endpoint. (CVE-2026-31804) (Thanks @mandreko)\n+  * New: Updated third party donation logos. (#2646) (Thanks @aisgbnok)\n+  * New: Update Bootstrap CSS to v3.4.1 and decouple overrides (#2662) (Thanks @aisgbnok)\n+  * New: Update Bootstrap-select to v1.13.18 (#2666) (Thanks @aisgbnok)\n+\n+\n+## v2.16.1 (2026-12-15)\n+\n+* Notifications:\n+  * New: Add Tautulli Plex token expired notification trigger.\n+* Newsletters:\n+  * New: Add Ace editor for syntax highlighting and code formatting for newsletter message text (#2585) (Thanks @mcclown)\n+* Graphs:\n+  * Change: Restrict graphs to guest user.\n+* UI:\n+  * New: Add DD:HH:MM time format for home stats.\n+  * New: Add HH:MM:SS time format for activity cards.\n+  * Removed: Timezone from IP address modal.\n+* Other:\n+  * Change: Zip backup files to reduce file size.\n+\n+\n+## v2.16.0 (2025-09-08)\n+\n+* Important Note!\n+  * Several security vulnerabilities have been identified in Tautulli versions <=2.15.3. Users are strongly encouraged to update to the latest Tautulli version 2.16.x.\n+* UI:\n+  * Fix: Update poster click-through overlay to new Plex logo. (#2584) (Thanks @TheMeanCanEHdian)\n+* Other:\n+  * Fix: Race condition in image cache directory creation. (#2580) (Thanks @keithah)\n+  * Fix: Validate image path in /image endpoints. (CVE-2025-58760) (Thanks @d-xuan)\n+  * Fix: Validate image path in /pms_image_proxy endpoints. (CVE-2025-58761) (Thanks @d-xuan)\n+  * Fix: Validate image format in /pms_image_proxy endpoint. (CVE-2025-58762) (Thanks @d-xuan)\n+  * Fix: Don't run git command with shell. (CVE-2025-58763) (Thanks @d-xuan)\n+\n+\n+## v2.15.3 (2025-08-03)\n+\n+* Exporter:\n+  * New: Added hearingImpaired for subtitles and visualImpaired for audio attributes to exporter fields.\n+* Graphs:\n+  * Fix: Remove duplicate \"Total\" entry in graph tooltips. (Thanks @zdimension) (#2534)\n+* UI:\n+  * Fix: Failing to retrieve collections / playlists with over 1000 items.\n+  * Fix: Scrollbar not showing on macosx and webkit browsers. (#2221)\n+  * Fix: Incorrect rounding of minutes in global stats play duration.\n+  * Fix: Disable browser autocomplete for notification agent and newsletter agent configurations. (#2557)\n+* API:\n+  * New: Added ability to return svg files using pms_image_proxy API command.\n+* Other:\n+  * New: Added ability to set config values using environment variables. (Thanks @komuw) (#2309, #2543)\n+\n+\n+## v2.15.2 (2025-04-12)\n+\n+* Activity:\n+  * New: Added link to library by clicking media type icon.\n+  * New: Added stream count to tab title on homepage. (#2517)\n+* History:\n+  * Fix: Check stream watched status before stream stopped status. (#2506)\n+* Notifications:\n+  * Fix: ntfy notifications failing to send if provider link is blank.\n+  * Fix: Check Pushover notification attachment is under 5MB limit. (#2396)\n+  * Fix: Track URLs redirecting to the correct media page. (#2513)\n+  * New: Added audio profile notification parameters.\n+  * New: Added PATCH method for Webhook notifications.\n+* Graphs:\n+  * New: Added Total line to daily streams graph. (Thanks @zdimension) (#2497)\n+* UI:\n+  * Fix: Do not redirect API requests to the login page. (#2490)\n+  * Change: Swap source and stream columns in stream info modal.\n+* Other:\n+  * Fix: Various typos. (Thanks @luzpaz) (#2520)\n+  * Fix: CherryPy CORS response header not being set correctly. (#2279)\n+\n+\n ## v2.15.1 (2025-01-11)\n \n * Activity:\n-  * Fix: Detection of HDR transcodes. (Thanks @chrisdecker08) (#2412, #2466)\n+  * Fix: Detection of HDR transcodes. (Thanks @cdecker08) (#2412, #2466)\n * Newsletters:\n   * Fix: Disable basic authentication for /newsletter and /image endpoints. (#2472)\n * Exporter:\n   * New: Added logos to season and episode exports.\n * Other:\n-  * Fix Docker container https health check.\n+  * Fix: Docker container https health check.\n \n \n ## v2.15.0 (2024-11-24)"}, {"sha": "37829290b2718383ef0e4c8f40ab58127e22e913", "filename": "README.md", "status": "modified", "additions": 1, "deletions": 1, "changes": 2, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/README.md", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/README.md", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/README.md?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -129,7 +129,7 @@ This is free software under the GPL v3 open source license. Feel free to do with\n but any modification must be open sourced. A copy of the license is included.\n \n This software includes Highsoft software libraries which you may freely distribute for \n-non-commercial use. Commerical users must licence this software, for more information visit\n+non-commercial use. Commercial users must licence this software, for more information visit\n https://shop.highsoft.com/faq/non-commercial#non-commercial-redistribution.\n \n "}, {"sha": "d516befb04ff9517b72ec209aa0db669dd947a70", "filename": "Tautulli.py", "status": "modified", "additions": 6, "deletions": 6, "changes": 12, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/Tautulli.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/Tautulli.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/Tautulli.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -25,16 +25,16 @@\n \n \n import argparse\n-import datetime\n+from datetime import datetime\n import locale\n import platformdirs\n-import pytz\n import signal\n import shutil\n import time\n import threading\n import tzlocal\n import ctypes\n+import zoneinfo\n \n import plexpy\n from plexpy import common, config, database, helpers, logger, webstart\n@@ -129,17 +129,17 @@ def main():\n     if args.quiet:\n         plexpy.QUIET = True\n \n-    # Do an intial setup of the logger.\n+    # Do an initial setup of the logger.\n     # Require verbose for pre-initilization to see critical errors\n     logger.initLogger(console=not plexpy.QUIET, log_dir=False, verbose=True)\n \n     try:\n         plexpy.SYS_TIMEZONE = tzlocal.get_localzone()\n-    except (pytz.UnknownTimeZoneError, LookupError, ValueError) as e:\n+    except (zoneinfo.ZoneInfoNotFoundError, LookupError, ValueError) as e:\n         logger.error(\"Could not determine system timezone: %s\" % e)\n-        plexpy.SYS_TIMEZONE = pytz.UTC\n+        plexpy.SYS_TIMEZONE = zoneinfo.ZoneInfo(\"UTC\")\n \n-    plexpy.SYS_UTC_OFFSET = datetime.datetime.now(plexpy.SYS_TIMEZONE).strftime('%z')\n+    plexpy.SYS_UTC_OFFSET = datetime.now(plexpy.SYS_TIMEZONE).strftime('%z')\n \n     if helpers.bool_true(os.getenv('TAUTULLI_DOCKER', False)):\n         plexpy.DOCKER = True"}, {"sha": "274c8b4039d7ec65814126cd361c562e2efe28e6", "filename": "data/interfaces/default/base.html", "status": "modified", "additions": 64, "deletions": 18, "changes": 82, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fbase.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fbase.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fbase.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,4 +1,4 @@\n-\ufeff<%\n+<%\n     import plexpy\n     from plexpy import version\n     from plexpy.helpers import anon_url\n@@ -220,7 +220,7 @@ <h4 class=\"modal-title\">Tautulli Donation</h4>\n             </div>\n             <div class=\"modal-body\">\n                 <div class=\"row\">\n-                    <div class=\"col-md-12\" style=\"text-align: center;\">\n+                    <div class=\"col-md-12 text-center\">\n                         <h4>\n                             <strong>Thank you for supporting Tautulli!</strong>\n                         </h4>\n@@ -229,61 +229,70 @@ <h4>\n                         </p>\n                     </div>\n                 </div>\n-                <ul id=\"donation_type\" class=\"nav nav-pills\" role=\"tablist\" style=\"display: flex; justify-content: center; margin: 10px 0;\">\n+                <ul id=\"donation_type\" class=\"nav nav-pills\" role=\"tablist\">\n                     <li class=\"active\"><a href=\"#github-donation\" role=\"tab\" data-toggle=\"tab\">GitHub</a></li>\n                     <li><a href=\"#patreon-donation\" role=\"tab\" data-toggle=\"tab\">Patreon</a></li>\n                     <li><a href=\"#stripe-donation\" role=\"tab\" data-toggle=\"tab\">Stripe</a></li>\n                     <li><a href=\"#paypal-donation\" role=\"tab\" data-toggle=\"tab\">PayPal</a></li>\n-                    <li><a href=\"#crypto-donation\" role=\"tab\" data-toggle=\"tab\">Crypto</a></li>\n+                    <li><a href=\"#crypto-donation\" role=\"tab\" data-toggle=\"tab\" id=\"crypto-donation-tab\">Crypto</a></li>\n                 </ul>\n                 <div class=\"tab-content\">\n-                    <div role=\"tabpanel\" class=\"tab-pane active\" id=\"github-donation\" style=\"text-align: center\">\n+                    <div role=\"tabpanel\" class=\"tab-pane active text-center\" id=\"github-donation\">\n                         <p>\n                             Click the button below to continue to GitHub.\n                         </p>\n                         <p>\n-                            <a href=\"${anon_url('https://github.com/sponsors/JonnyWong16')}\" target=\"_blank\" rel=\"noreferrer\" class=\"btn btn-sm btn-default\" style=\"font-weight: 600;\">\n-                                <i class=\"fa fa-heart fa-sm\" style=\"color: #ea4aaa;\"></i>&nbsp; Sponsor\n+                            <a href=\"${anon_url('https://github.com/sponsors/JonnyWong16')}\" target=\"_blank\" rel=\"noreferrer\" class=\"btn btn-default donate-btn donate-btn-github\">\n+                                <img src=\"images/octicons-heart-16.svg\" alt=\"\" aria-hidden=\"true\" height=\"24\"> Sponsor\n                             </a>\n                         </p>\n                         <p class=\"small-muted\">(GitHub does not have a fee)</p>\n                     </div>\n-                    <div role=\"tabpanel\" class=\"tab-pane\" id=\"patreon-donation\" style=\"text-align: center\">\n+                    <div role=\"tabpanel\" class=\"tab-pane text-center\" id=\"patreon-donation\">\n                         <p>\n                             Click the button below to continue to Patreon.\n                         </p>\n                         <p>\n-                            <a href=\"${anon_url('https://www.patreon.com/join/tautulli')}\" target=\"_blank\" rel=\"noreferrer\">\n-                                <img src=\"images/become_a_patron_button.png\" alt=\"Become a Patron\" width=\"170\" height=\"40\">\n+                            <a href=\"${anon_url('https://www.patreon.com/join/tautulli')}\" target=\"_blank\" rel=\"noreferrer\" class=\"donate-btn donate-btn-patreon\">\n+                                <img src=\"images/patreon-lockup-horizontal.svg\" alt=\"Patreon\" height=\"36\">\n                             </a>\n                         </p>\n                         <p class=\"small-muted\">(Patreon has a fee)</p>\n                     </div>\n-                    <div role=\"tabpanel\" class=\"tab-pane\" id=\"stripe-donation\" style=\"text-align: center\">\n+                    <div role=\"tabpanel\" class=\"tab-pane text-center\" id=\"stripe-donation\">\n                         <p>\n                             Click the button below to continue to Stripe.\n                         </p>\n                         <p>\n-                            <a href=\"${anon_url('https://donate.stripe.com/5kA7vnb7dczVbxC9AA')}\" target=\"_blank\" rel=\"noreferrer\">\n-                                <img src=\"images/Stripe_wordmark_-_white_small_28px.png\" alt=\"Stripe\" style=\"background-color: #7068fe; border-radius: 3px; padding: 3px;\">\n+                            <a href=\"${anon_url('https://donate.stripe.com/5kA7vnb7dczVbxC9AA')}\" target=\"_blank\" rel=\"noreferrer\" class=\"donate-btn donate-btn-stripe\">\n+                                <img src=\"images/stripe-wordmark-white.svg\" alt=\"Stripe\" height=\"36\">\n                             </a>\n                         </p>\n                         <p class=\"small-muted\">(Stripe has a fee)</p>\n                     </div>\n-                    <div role=\"tabpanel\" class=\"tab-pane\" id=\"paypal-donation\" style=\"text-align: center\">\n+                    <div role=\"tabpanel\" class=\"tab-pane text-center\" id=\"paypal-donation\">\n                         <p>\n                             Click the button below to continue to PayPal.\n                         </p>\n                         <p>\n-                            <a href=\"${anon_url('https://www.paypal.com/donate/?hosted_button_id=CUHSQ99KAKC5Q')}\" target=\"_blank\" rel=\"noreferrer\">\n-                                <img src=\"images/gold-rect-paypal-34px.png\" alt=\"PayPal\">\n+                            <a href=\"${anon_url('https://www.paypal.com/donate/?hosted_button_id=CUHSQ99KAKC5Q')}\" target=\"_blank\" rel=\"noreferrer\" class=\"donate-btn donate-btn-paypal\">\n+                                <img src=\"images/paypal-logo-black.svg\" alt=\"PayPal\" height=\"36\">\n                             </a>\n                         </p>\n                         <p class=\"small-muted\">(PayPal has a fee)</p>\n                     </div>\n-                    <div role=\"tabpanel\" class=\"tab-pane\" id=\"crypto-donation\" style=\"text-align: center\">\n+                    <div role=\"tabpanel\" class=\"tab-pane text-center\" id=\"crypto-donation\">\n                         <p>\n-                            Click the button below to continue to Coinbase.\n+                            Select a cryptocurrency.\n+                        </p>\n+                        <select class=\"form-control\" id=\"crypto-select\"></select>\n+                        <div id=\"crypto-qrcode\"></div>\n+                        <div id=\"crypto-address\" class=\"form-group\">\n+                            <label>Address:</label>\n+                            <span class=\"inline-pre\" id=\"crypto-address-value\"></span>\n+                        </div>\n+                        <p>\n+                            Or click the button below to continue to Coinbase.\n                         </p>\n                         <a href=\"${anon_url('https://commerce.coinbase.com/checkout/8a9fa08c-8a38-409e-9220-868124c4ba0c')}\" target=\"_blank\" rel=\"noreferrer\" class=\"donate-with-crypto\">\n                             <span>Donate with Crypto</span>\n@@ -331,6 +340,7 @@ <h4 class=\"modal-title\">Confirm</h4>\n <script src=\"${http_root}js/blurhash_pure_js_port.min.js\"></script>\n <script src=\"${http_root}js/script.js${cache_param}\"></script>\n <script src=\"${http_root}js/ajaxNotifications.js\"></script>\n+<script src=\"${http_root}js/kjua.min.js\"></script>\n <script>\n     % if _session['user_group'] == 'admin':\n     $('body').on('click', '#updateDismiss', function() {\n@@ -404,6 +414,42 @@ <h4 class=\"modal-title\">Confirm</h4>\n         checkUpdate(function () { $('#nav-update').html('<i class=\"fa fa-fw fa-arrow-alt-circle-up\"></i> Check for Updates'); });\n     });\n \n+    $('#crypto-donation-tab').one('shown.bs.tab', function (e) {\n+        $.ajax({\n+            url: 'https://tautulli.com/donate/crypto-addresses.json',\n+            type: 'GET',\n+            dataType: 'json',\n+            cache: false,\n+            async: true,\n+            success: function (data) {\n+                $('#crypto-select').empty().append('<option selected disabled>Select Cryptocurrency</option>');\n+                $.each(data, function (index, crypto) {\n+                    $('<option/>', {\n+                        text: crypto.name + ' (' + crypto.symbol + ')',\n+                        value: crypto.address\n+                    }).appendTo('#crypto-select');\n+                });\n+            },\n+            error: function () {\n+                $('#crypto-select').empty().append('<option selected disabled>Error: Unable to load addresses</option>');\n+            }\n+        });\n+    });\n+\n+    $('#crypto-select').change(function() {\n+        var address = $(this).val();\n+        $('#crypto-qrcode').empty().kjua({\n+            text: address,\n+            render: 'canvas',\n+            ecLevel: 'H',\n+            size: 256,\n+            fill: '#000',\n+            back: '#eee'\n+        }).show();\n+        $('#crypto-address-value').text(address);\n+        $('#crypto-address').show();\n+    })\n+\n     % endif\n \n     $('.dropdown-toggle').click(function (e) {"}, {"sha": "8d0f049c8e29046dfd68d22d2d485b14bf1e6376", "filename": "data/interfaces/default/css/bootstrap-select.min.css", "status": "modified", "additions": 2, "deletions": 2, "changes": 4, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap-select.min.css", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap-select.min.css", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap-select.min.css?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,6 +1,6 @@\n /*!\n- * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select)\n+ * Bootstrap-select v1.13.18 (https://developer.snapappointments.com/bootstrap-select)\n  *\n  * Copyright 2012-2020 SnapAppointments, LLC\n  * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)\n- */@-webkit-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@-o-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}.bootstrap-select>select.bs-select-hidden,select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\\0;vertical-align:middle}.bootstrap-select>.dropdown-toggle{position:relative;width:100%;text-align:right;white-space:nowrap;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.bootstrap-select>.dropdown-toggle:after{margin-top:-1px}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:hover{color:rgba(255,255,255,.5)}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none;z-index:0!important}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2!important}.bootstrap-select.is-invalid .dropdown-toggle,.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle,.was-validated .bootstrap-select select:invalid+.dropdown-toggle{border-color:#b94a48}.bootstrap-select.is-valid .dropdown-toggle,.was-validated .bootstrap-select select:valid+.dropdown-toggle{border-color:#28a745}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus,.bootstrap-select>select.mobile-device:focus+.dropdown-toggle{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none;height:auto}:not(.input-group)>.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{float:none;z-index:auto}.form-inline .bootstrap-select,.form-inline .bootstrap-select.form-control:not([class*=col-]){width:auto}.bootstrap-select:not(.input-group-btn),.bootstrap-select[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.dropdown-menu-right,.bootstrap-select[class*=col-].dropdown-menu-right,.row .bootstrap-select[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select,.form-horizontal .bootstrap-select,.form-inline .bootstrap-select{margin-bottom:0}.form-group-lg .bootstrap-select.form-control,.form-group-sm .bootstrap-select.form-control{padding:0}.form-group-lg .bootstrap-select.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-lg .dropdown-toggle,.bootstrap-select.form-control-sm .dropdown-toggle{font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-sm .dropdown-toggle{padding:.25rem .5rem}.bootstrap-select.form-control-lg .dropdown-toggle{padding:.5rem 1rem}.form-inline .bootstrap-select .form-control{width:100%}.bootstrap-select.disabled,.bootstrap-select>.disabled{cursor:not-allowed}.bootstrap-select.disabled:focus,.bootstrap-select>.disabled:focus{outline:0!important}.bootstrap-select.bs-container{position:absolute;top:0;left:0;height:0!important;padding:0!important}.bootstrap-select.bs-container .dropdown-menu{z-index:1060}.bootstrap-select .dropdown-toggle .filter-option{position:static;top:0;left:0;float:left;height:100%;width:100%;text-align:left;overflow:hidden;-webkit-box-flex:0;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.bs3.bootstrap-select .dropdown-toggle .filter-option{padding-right:inherit}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option{position:absolute;padding-top:inherit;padding-bottom:inherit;padding-left:inherit;float:none}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner{padding-right:inherit}.bootstrap-select .dropdown-toggle .filter-option-inner-inner{overflow:hidden}.bootstrap-select .dropdown-toggle .filter-expand{width:0!important;float:left;opacity:0!important;overflow:hidden}.bootstrap-select .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.input-group .bootstrap-select.form-control .dropdown-toggle{border-radius:inherit}.bootstrap-select[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu>.inner:focus{outline:0!important}.bootstrap-select .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select .dropdown-menu li{position:relative}.bootstrap-select .dropdown-menu li.active small{color:rgba(255,255,255,.5)!important}.bootstrap-select .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select .dropdown-menu li a span.check-mark{display:none}.bootstrap-select .dropdown-menu li a span.text{display:inline-block}.bootstrap-select .dropdown-menu li small{padding-left:.5em}.bootstrap-select .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu .notify.fadeOut{-webkit-animation:.3s linear 750ms forwards bs-notify-fadeOut;-o-animation:.3s linear 750ms forwards bs-notify-fadeOut;animation:.3s linear 750ms forwards bs-notify-fadeOut}.bootstrap-select .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.fit-width .dropdown-toggle .filter-option{position:static;display:inline;padding:0}.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner{display:inline}.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before{content:'\\00a0'}.bootstrap-select.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark{position:absolute;display:inline-block;right:15px;top:5px}.bootstrap-select.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select .bs-ok-default:after{content:'';display:block;width:.5em;height:1em;border-style:solid;border-width:0 .26em .26em 0;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before{bottom:auto;top:-4px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after{bottom:auto;top:-4px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:before,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none}\n\\ No newline at end of file\n+ */@-webkit-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@-o-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}.bootstrap-select>select.bs-select-hidden,select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\\0;vertical-align:middle}.bootstrap-select>.dropdown-toggle{position:relative;width:100%;text-align:right;white-space:nowrap;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.bootstrap-select>.dropdown-toggle:after{margin-top:-1px}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:hover{color:rgba(255,255,255,.5)}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none;z-index:0!important}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2!important}.bootstrap-select.is-invalid .dropdown-toggle,.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle,.was-validated .bootstrap-select select:invalid+.dropdown-toggle{border-color:#b94a48}.bootstrap-select.is-valid .dropdown-toggle,.was-validated .bootstrap-select select:valid+.dropdown-toggle{border-color:#28a745}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus,.bootstrap-select>select.mobile-device:focus+.dropdown-toggle{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none;height:auto}:not(.input-group)>.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{float:none;z-index:auto}.form-inline .bootstrap-select,.form-inline .bootstrap-select.form-control:not([class*=col-]){width:auto}.bootstrap-select:not(.input-group-btn),.bootstrap-select[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.dropdown-menu-right,.bootstrap-select[class*=col-].dropdown-menu-right,.row .bootstrap-select[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select,.form-horizontal .bootstrap-select,.form-inline .bootstrap-select{margin-bottom:0}.form-group-lg .bootstrap-select.form-control,.form-group-sm .bootstrap-select.form-control{padding:0}.form-group-lg .bootstrap-select.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-lg .dropdown-toggle,.bootstrap-select.form-control-sm .dropdown-toggle{font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-sm .dropdown-toggle{padding:.25rem .5rem}.bootstrap-select.form-control-lg .dropdown-toggle{padding:.5rem 1rem}.form-inline .bootstrap-select .form-control{width:100%}.bootstrap-select.disabled,.bootstrap-select>.disabled{cursor:not-allowed}.bootstrap-select.disabled:focus,.bootstrap-select>.disabled:focus{outline:0!important}.bootstrap-select.bs-container{position:absolute;top:0;left:0;height:0!important;padding:0!important}.bootstrap-select.bs-container .dropdown-menu{z-index:1060}.bootstrap-select .dropdown-toggle .filter-option{position:static;top:0;left:0;float:left;height:100%;width:100%;text-align:left;overflow:hidden;-webkit-box-flex:0;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.bs3.bootstrap-select .dropdown-toggle .filter-option{padding-right:inherit}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option{position:absolute;padding-top:inherit;padding-bottom:inherit;padding-left:inherit;float:none}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner{padding-right:inherit}.bootstrap-select .dropdown-toggle .filter-option-inner-inner{overflow:hidden}.bootstrap-select .dropdown-toggle .filter-expand{width:0!important;float:left;opacity:0!important;overflow:hidden}.bootstrap-select .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.input-group .bootstrap-select.form-control .dropdown-toggle{border-radius:inherit}.bootstrap-select[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu>.inner:focus{outline:0!important}.bootstrap-select .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select .dropdown-menu li{position:relative}.bootstrap-select .dropdown-menu li.active small{color:rgba(255,255,255,.5)!important}.bootstrap-select .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select .dropdown-menu li a span.check-mark{display:none}.bootstrap-select .dropdown-menu li a span.text{display:inline-block}.bootstrap-select .dropdown-menu li small{padding-left:.5em}.bootstrap-select .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu .notify.fadeOut{-webkit-animation:.3s linear 750ms forwards bs-notify-fadeOut;-o-animation:.3s linear 750ms forwards bs-notify-fadeOut;animation:.3s linear 750ms forwards bs-notify-fadeOut}.bootstrap-select .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.fit-width .dropdown-toggle .filter-option{position:static;display:inline;padding:0}.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner{display:inline}.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before{content:'\\00a0'}.bootstrap-select.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark{position:absolute;display:inline-block;right:15px;top:5px}.bootstrap-select.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select .bs-ok-default:after{content:'';display:block;width:.5em;height:1em;border-style:solid;border-width:0 .26em .26em 0;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before{bottom:auto;top:-4px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after{bottom:auto;top:-4px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:before,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none}\n\\ No newline at end of file"}, {"sha": "fcab41554ad7f471346b76aff336d329fde68722", "filename": "data/interfaces/default/css/bootstrap3/bootstrap.css", "status": "modified", "additions": 562, "deletions": 576, "changes": 1138, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap3%2Fbootstrap.css", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap3%2Fbootstrap.css", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap3%2Fbootstrap.css?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,13 +1,13 @@\n /*!\n- * Bootstrap v3.3.5 (http://getbootstrap.com)\n- * Copyright 2011-2015 Twitter, Inc.\n+ * Bootstrap v3.4.1 (https://getbootstrap.com/)\n+ * Copyright 2011-2019 Twitter, Inc.\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n  */\n /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n html {\n   font-family: sans-serif;\n+  -ms-text-size-adjust: 100%;\n   -webkit-text-size-adjust: 100%;\n-      -ms-text-size-adjust: 100%;\n }\n body {\n   margin: 0;\n@@ -50,7 +50,11 @@ a:hover {\n   outline: 0;\n }\n abbr[title] {\n-  border-bottom: 1px dotted;\n+  border-bottom: none;\n+  text-decoration: underline;\n+  -webkit-text-decoration: underline dotted;\n+  -moz-text-decoration: underline dotted;\n+  text-decoration: underline dotted;\n }\n b,\n strong {\n@@ -60,28 +64,28 @@ dfn {\n   font-style: italic;\n }\n h1 {\n-  margin: .67em 0;\n   font-size: 2em;\n+  margin: 0.67em 0;\n }\n mark {\n-  color: #000;\n   background: #ff0;\n+  color: #000;\n }\n small {\n   font-size: 80%;\n }\n sub,\n sup {\n-  position: relative;\n   font-size: 75%;\n   line-height: 0;\n+  position: relative;\n   vertical-align: baseline;\n }\n sup {\n-  top: -.5em;\n+  top: -0.5em;\n }\n sub {\n-  bottom: -.25em;\n+  bottom: -0.25em;\n }\n img {\n   border: 0;\n@@ -93,10 +97,10 @@ figure {\n   margin: 1em 40px;\n }\n hr {\n-  height: 0;\n   -webkit-box-sizing: content-box;\n-     -moz-box-sizing: content-box;\n-          box-sizing: content-box;\n+  -moz-box-sizing: content-box;\n+  box-sizing: content-box;\n+  height: 0;\n }\n pre {\n   overflow: auto;\n@@ -113,9 +117,9 @@ input,\n optgroup,\n select,\n textarea {\n-  margin: 0;\n-  font: inherit;\n   color: inherit;\n+  font: inherit;\n+  margin: 0;\n }\n button {\n   overflow: visible;\n@@ -137,41 +141,41 @@ html input[disabled] {\n }\n button::-moz-focus-inner,\n input::-moz-focus-inner {\n-  padding: 0;\n   border: 0;\n+  padding: 0;\n }\n input {\n   line-height: normal;\n }\n input[type=\"checkbox\"],\n input[type=\"radio\"] {\n   -webkit-box-sizing: border-box;\n-     -moz-box-sizing: border-box;\n-          box-sizing: border-box;\n+  -moz-box-sizing: border-box;\n+  box-sizing: border-box;\n   padding: 0;\n }\n input[type=\"number\"]::-webkit-inner-spin-button,\n input[type=\"number\"]::-webkit-outer-spin-button {\n   height: auto;\n }\n input[type=\"search\"] {\n-  -webkit-box-sizing: content-box;\n-     -moz-box-sizing: content-box;\n-          box-sizing: content-box;\n   -webkit-appearance: textfield;\n+  -webkit-box-sizing: content-box;\n+  -moz-box-sizing: content-box;\n+  box-sizing: content-box;\n }\n input[type=\"search\"]::-webkit-search-cancel-button,\n input[type=\"search\"]::-webkit-search-decoration {\n   -webkit-appearance: none;\n }\n fieldset {\n-  padding: .35em .625em .75em;\n-  margin: 0 2px;\n   border: 1px solid #c0c0c0;\n+  margin: 0 2px;\n+  padding: 0.35em 0.625em 0.75em;\n }\n legend {\n-  padding: 0;\n   border: 0;\n+  padding: 0;\n }\n textarea {\n   overflow: auto;\n@@ -180,8 +184,8 @@ optgroup {\n   font-weight: bold;\n }\n table {\n-  border-spacing: 0;\n   border-collapse: collapse;\n+  border-spacing: 0;\n }\n td,\n th {\n@@ -196,7 +200,7 @@ th {\n     text-shadow: none !important;\n     background: transparent !important;\n     -webkit-box-shadow: none !important;\n-            box-shadow: none !important;\n+    box-shadow: none !important;\n   }\n   a,\n   a:visited {\n@@ -215,7 +219,6 @@ th {\n   pre,\n   blockquote {\n     border: 1px solid #999;\n-\n     page-break-inside: avoid;\n   }\n   thead {\n@@ -261,28 +264,26 @@ th {\n   }\n }\n @font-face {\n-  font-family: 'Glyphicons Halflings';\n-\n-  src: url('../../fonts/bootstrap3/glyphicons-halflings-regular.eot');\n-  src: url('../../fonts/bootstrap3/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../../fonts/bootstrap3/glyphicons-halflings-regular.woff2') format('woff2'), url('../../fonts/bootstrap3/glyphicons-halflings-regular.woff') format('woff'), url('../../fonts/bootstrap3/glyphicons-halflings-regular.ttf') format('truetype'), url('../../fonts/bootstrap3/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n+  font-family: \"Glyphicons Halflings\";\n+  src: url(\"../fonts/glyphicons-halflings-regular.eot\");\n+  src: url(\"../fonts/glyphicons-halflings-regular.eot?#iefix\") format(\"embedded-opentype\"), url(\"../fonts/glyphicons-halflings-regular.woff2\") format(\"woff2\"), url(\"../fonts/glyphicons-halflings-regular.woff\") format(\"woff\"), url(\"../fonts/glyphicons-halflings-regular.ttf\") format(\"truetype\"), url(\"../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular\") format(\"svg\");\n }\n .glyphicon {\n   position: relative;\n   top: 1px;\n   display: inline-block;\n-  font-family: 'Glyphicons Halflings';\n+  font-family: \"Glyphicons Halflings\";\n   font-style: normal;\n-  font-weight: normal;\n+  font-weight: 400;\n   line-height: 1;\n-\n   -webkit-font-smoothing: antialiased;\n   -moz-osx-font-smoothing: grayscale;\n }\n .glyphicon-asterisk:before {\n-  content: \"\\2a\";\n+  content: \"\\002a\";\n }\n .glyphicon-plus:before {\n-  content: \"\\2b\";\n+  content: \"\\002b\";\n }\n .glyphicon-euro:before,\n .glyphicon-eur:before {\n@@ -1067,26 +1068,25 @@ th {\n }\n * {\n   -webkit-box-sizing: border-box;\n-     -moz-box-sizing: border-box;\n-          box-sizing: border-box;\n+  -moz-box-sizing: border-box;\n+  box-sizing: border-box;\n }\n *:before,\n *:after {\n   -webkit-box-sizing: border-box;\n-     -moz-box-sizing: border-box;\n-          box-sizing: border-box;\n+  -moz-box-sizing: border-box;\n+  box-sizing: border-box;\n }\n html {\n   font-size: 10px;\n-\n   -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n }\n body {\n-  font-family: 'Open Sans', sans-serif;\n+  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n   font-size: 14px;\n   line-height: 1.42857143;\n-  color: #fff;\n-  background-color: #1f1f1f;\n+  color: #333333;\n+  background-color: #fff;\n }\n input,\n button,\n@@ -1096,56 +1096,8 @@ textarea {\n   font-size: inherit;\n   line-height: inherit;\n }\n-textarea,\n-input[type=\"text\"],\n-input[type=\"password\"],\n-input[type=\"datetime\"],\n-input[type=\"datetime-local\"],\n-input[type=\"date\"],\n-input[type=\"month\"],\n-input[type=\"time\"],\n-input[type=\"week\"],\n-input[type=\"number\"],\n-input[type=\"email\"],\n-input[type=\"url\"],\n-input[type=\"search\"],\n-input[type=\"tel\"],\n-input[type=\"color\"],\n-.uneditable-input {\n-\tmargin-top: 5px;\n-\tcolor: #fff;\n-\tborder: 0px solid #444;\n-\tbackground: #333;\n-\theight: 30px;\n-\tpadding: 2px 5px 2px 12px;\n-\tbackground-color: #3a3a3a;\n-\tborder-radius: 14.5px;\n-\ttransition: background-color .3s;\n-}\n-textarea:focus,\n-input[type=\"text\"]:focus,\n-input[type=\"password\"]:focus,\n-input[type=\"datetime\"]:focus,\n-input[type=\"datetime-local\"]:focus,\n-input[type=\"date\"]:focus,\n-input[type=\"month\"]:focus,\n-input[type=\"time\"]:focus,\n-input[type=\"week\"]:focus,\n-input[type=\"number\"]:focus,\n-input[type=\"email\"]:focus,\n-input[type=\"url\"]:focus,\n-input[type=\"search\"]:focus,\n-input[type=\"tel\"]:focus,\n-input[type=\"color\"]:focus,\n-.uneditable-input:focus {\n-\toutline: 0;\n-\toutline: thin dotted \\9;\n-\tcolor: #555;\n-\tbackground-color: #fff;\n-\ttransition: background-color .3s;\n-\t/* IE6-9 */\n-}\n a {\n+  color: #337ab7;\n   text-decoration: none;\n }\n a:hover,\n@@ -1154,7 +1106,6 @@ a:focus {\n   text-decoration: underline;\n }\n a:focus {\n-  outline: thin dotted;\n   outline: 5px auto -webkit-focus-ring-color;\n   outline-offset: -2px;\n }\n@@ -1177,17 +1128,17 @@ img {\n   border-radius: 6px;\n }\n .img-thumbnail {\n-  display: inline-block;\n-  max-width: 100%;\n-  height: auto;\n   padding: 4px;\n   line-height: 1.42857143;\n   background-color: #fff;\n   border: 1px solid #ddd;\n   border-radius: 4px;\n-  -webkit-transition: all .2s ease-in-out;\n-       -o-transition: all .2s ease-in-out;\n-          transition: all .2s ease-in-out;\n+  -webkit-transition: all 0.2s ease-in-out;\n+  -o-transition: all 0.2s ease-in-out;\n+  transition: all 0.2s ease-in-out;\n+  display: inline-block;\n+  max-width: 100%;\n+  height: auto;\n }\n .img-circle {\n   border-radius: 50%;\n@@ -1196,7 +1147,7 @@ hr {\n   margin-top: 20px;\n   margin-bottom: 20px;\n   border: 0;\n-  border-top: 1px solid #eee;\n+  border-top: 1px solid #eeeeee;\n }\n .sr-only {\n   position: absolute;\n@@ -1261,9 +1212,9 @@ h6 .small,\n .h4 .small,\n .h5 .small,\n .h6 .small {\n-  font-weight: normal;\n+  font-weight: 400;\n   line-height: 1;\n-  color: #777;\n+  color: #777777;\n }\n h1,\n .h1,\n@@ -1355,7 +1306,7 @@ small,\n }\n mark,\n .mark {\n-  padding: .2em;\n+  padding: 0.2em;\n   background-color: #fcf8e3;\n }\n .text-left {\n@@ -1383,7 +1334,7 @@ mark,\n   text-transform: capitalize;\n }\n .text-muted {\n-  color: #777;\n+  color: #777777;\n }\n .text-primary {\n   color: #337ab7;\n@@ -1459,7 +1410,7 @@ a.bg-danger:focus {\n .page-header {\n   padding-bottom: 9px;\n   margin: 40px 0 20px;\n-  border-bottom: 1px solid #eee;\n+  border-bottom: 1px solid #eeeeee;\n }\n ul,\n ol {\n@@ -1478,8 +1429,8 @@ ol ol {\n }\n .list-inline {\n   padding-left: 0;\n-  margin-left: -5px;\n   list-style: none;\n+  margin-left: -5px;\n }\n .list-inline > li {\n   display: inline-block;\n@@ -1495,7 +1446,7 @@ dd {\n   line-height: 1.42857143;\n }\n dt {\n-  font-weight: bold;\n+  font-weight: 700;\n }\n dd {\n   margin-left: 0;\n@@ -1504,9 +1455,9 @@ dd {\n   .dl-horizontal dt {\n     float: left;\n     width: 160px;\n-    overflow: hidden;\n     clear: left;\n     text-align: right;\n+    overflow: hidden;\n     text-overflow: ellipsis;\n     white-space: nowrap;\n   }\n@@ -1517,7 +1468,6 @@ dd {\n abbr[title],\n abbr[data-original-title] {\n   cursor: help;\n-  border-bottom: 1px dotted #777;\n }\n .initialism {\n   font-size: 90%;\n@@ -1527,7 +1477,7 @@ blockquote {\n   padding: 10px 20px;\n   margin: 0 0 20px;\n   font-size: 17.5px;\n-  border-left: 5px solid #eee;\n+  border-left: 5px solid #eeeeee;\n }\n blockquote p:last-child,\n blockquote ul:last-child,\n@@ -1540,19 +1490,19 @@ blockquote .small {\n   display: block;\n   font-size: 80%;\n   line-height: 1.42857143;\n-  color: #777;\n+  color: #777777;\n }\n blockquote footer:before,\n blockquote small:before,\n blockquote .small:before {\n-  content: '\\2014 \\00A0';\n+  content: \"\\2014 \\00A0\";\n }\n .blockquote-reverse,\n blockquote.pull-right {\n   padding-right: 15px;\n   padding-left: 0;\n   text-align: right;\n-  border-right: 5px solid #eee;\n+  border-right: 5px solid #eeeeee;\n   border-left: 0;\n }\n .blockquote-reverse footer:before,\n@@ -1561,15 +1511,15 @@ blockquote.pull-right footer:before,\n blockquote.pull-right small:before,\n .blockquote-reverse .small:before,\n blockquote.pull-right .small:before {\n-  content: '';\n+  content: \"\";\n }\n .blockquote-reverse footer:after,\n blockquote.pull-right footer:after,\n .blockquote-reverse small:after,\n blockquote.pull-right small:after,\n .blockquote-reverse .small:after,\n blockquote.pull-right .small:after {\n-  content: '\\00A0 \\2014';\n+  content: \"\\00A0 \\2014\";\n }\n address {\n   margin-bottom: 20px;\n@@ -1595,23 +1545,23 @@ kbd {\n   color: #fff;\n   background-color: #333;\n   border-radius: 3px;\n-  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);\n-          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);\n+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n }\n kbd kbd {\n   padding: 0;\n   font-size: 100%;\n-  font-weight: bold;\n+  font-weight: 700;\n   -webkit-box-shadow: none;\n-          box-shadow: none;\n+  box-shadow: none;\n }\n pre {\n   display: block;\n   padding: 9.5px;\n   margin: 0 0 10px;\n   font-size: 13px;\n   line-height: 1.42857143;\n-  color: #333;\n+  color: #333333;\n   word-break: break-all;\n   word-wrap: break-word;\n   background-color: #f5f5f5;\n@@ -1661,13 +1611,79 @@ pre code {\n   margin-right: -15px;\n   margin-left: -15px;\n }\n-.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n+.row-no-gutters {\n+  margin-right: 0;\n+  margin-left: 0;\n+}\n+.row-no-gutters [class*=\"col-\"] {\n+  padding-right: 0;\n+  padding-left: 0;\n+}\n+.col-xs-1,\n+.col-sm-1,\n+.col-md-1,\n+.col-lg-1,\n+.col-xs-2,\n+.col-sm-2,\n+.col-md-2,\n+.col-lg-2,\n+.col-xs-3,\n+.col-sm-3,\n+.col-md-3,\n+.col-lg-3,\n+.col-xs-4,\n+.col-sm-4,\n+.col-md-4,\n+.col-lg-4,\n+.col-xs-5,\n+.col-sm-5,\n+.col-md-5,\n+.col-lg-5,\n+.col-xs-6,\n+.col-sm-6,\n+.col-md-6,\n+.col-lg-6,\n+.col-xs-7,\n+.col-sm-7,\n+.col-md-7,\n+.col-lg-7,\n+.col-xs-8,\n+.col-sm-8,\n+.col-md-8,\n+.col-lg-8,\n+.col-xs-9,\n+.col-sm-9,\n+.col-md-9,\n+.col-lg-9,\n+.col-xs-10,\n+.col-sm-10,\n+.col-md-10,\n+.col-lg-10,\n+.col-xs-11,\n+.col-sm-11,\n+.col-md-11,\n+.col-lg-11,\n+.col-xs-12,\n+.col-sm-12,\n+.col-md-12,\n+.col-lg-12 {\n   position: relative;\n   min-height: 1px;\n   padding-right: 15px;\n   padding-left: 15px;\n }\n-.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n+.col-xs-1,\n+.col-xs-2,\n+.col-xs-3,\n+.col-xs-4,\n+.col-xs-5,\n+.col-xs-6,\n+.col-xs-7,\n+.col-xs-8,\n+.col-xs-9,\n+.col-xs-10,\n+.col-xs-11,\n+.col-xs-12 {\n   float: left;\n }\n .col-xs-12 {\n@@ -1821,10 +1837,21 @@ pre code {\n   margin-left: 8.33333333%;\n }\n .col-xs-offset-0 {\n-  margin-left: 0;\n+  margin-left: 0%;\n }\n @media (min-width: 768px) {\n-  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n+  .col-sm-1,\n+  .col-sm-2,\n+  .col-sm-3,\n+  .col-sm-4,\n+  .col-sm-5,\n+  .col-sm-6,\n+  .col-sm-7,\n+  .col-sm-8,\n+  .col-sm-9,\n+  .col-sm-10,\n+  .col-sm-11,\n+  .col-sm-12 {\n     float: left;\n   }\n   .col-sm-12 {\n@@ -1978,11 +2005,22 @@ pre code {\n     margin-left: 8.33333333%;\n   }\n   .col-sm-offset-0 {\n-    margin-left: 0;\n+    margin-left: 0%;\n   }\n }\n @media (min-width: 992px) {\n-  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n+  .col-md-1,\n+  .col-md-2,\n+  .col-md-3,\n+  .col-md-4,\n+  .col-md-5,\n+  .col-md-6,\n+  .col-md-7,\n+  .col-md-8,\n+  .col-md-9,\n+  .col-md-10,\n+  .col-md-11,\n+  .col-md-12 {\n     float: left;\n   }\n   .col-md-12 {\n@@ -2136,11 +2174,22 @@ pre code {\n     margin-left: 8.33333333%;\n   }\n   .col-md-offset-0 {\n-    margin-left: 0;\n+    margin-left: 0%;\n   }\n }\n @media (min-width: 1200px) {\n-  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n+  .col-lg-1,\n+  .col-lg-2,\n+  .col-lg-3,\n+  .col-lg-4,\n+  .col-lg-5,\n+  .col-lg-6,\n+  .col-lg-7,\n+  .col-lg-8,\n+  .col-lg-9,\n+  .col-lg-10,\n+  .col-lg-11,\n+  .col-lg-12 {\n     float: left;\n   }\n   .col-lg-12 {\n@@ -2294,16 +2343,27 @@ pre code {\n     margin-left: 8.33333333%;\n   }\n   .col-lg-offset-0 {\n-    margin-left: 0;\n+    margin-left: 0%;\n   }\n }\n table {\n   background-color: transparent;\n }\n+table col[class*=\"col-\"] {\n+  position: static;\n+  display: table-column;\n+  float: none;\n+}\n+table td[class*=\"col-\"],\n+table th[class*=\"col-\"] {\n+  position: static;\n+  display: table-cell;\n+  float: none;\n+}\n caption {\n   padding-top: 8px;\n   padding-bottom: 8px;\n-  color: #777;\n+  color: #777777;\n   text-align: left;\n }\n th {\n@@ -2372,17 +2432,6 @@ th {\n .table-hover > tbody > tr:hover {\n   background-color: #f5f5f5;\n }\n-table col[class*=\"col-\"] {\n-  position: static;\n-  display: table-column;\n-  float: none;\n-}\n-table td[class*=\"col-\"],\n-table th[class*=\"col-\"] {\n-  position: static;\n-  display: table-cell;\n-  float: none;\n-}\n .table > thead > tr > td.active,\n .table > tbody > tr > td.active,\n .table > tfoot > tr > td.active,\n@@ -2489,7 +2538,7 @@ table th[class*=\"col-\"] {\n   background-color: #ebcccc;\n }\n .table-responsive {\n-  min-height: .01%;\n+  min-height: 0.01%;\n   overflow-x: auto;\n }\n @media screen and (max-width: 767px) {\n@@ -2550,27 +2599,38 @@ legend {\n   margin-bottom: 20px;\n   font-size: 21px;\n   line-height: inherit;\n-  color: #333;\n+  color: #333333;\n   border: 0;\n   border-bottom: 1px solid #e5e5e5;\n }\n label {\n   display: inline-block;\n   max-width: 100%;\n   margin-bottom: 5px;\n-  font-weight: bold;\n+  font-weight: 700;\n }\n input[type=\"search\"] {\n   -webkit-box-sizing: border-box;\n-     -moz-box-sizing: border-box;\n-          box-sizing: border-box;\n+  -moz-box-sizing: border-box;\n+  box-sizing: border-box;\n+  -webkit-appearance: none;\n+  -moz-appearance: none;\n+  appearance: none;\n }\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n   margin: 4px 0 0;\n   margin-top: 1px \\9;\n   line-height: normal;\n }\n+input[type=\"radio\"][disabled],\n+input[type=\"checkbox\"][disabled],\n+input[type=\"radio\"].disabled,\n+input[type=\"checkbox\"].disabled,\n+fieldset[disabled] input[type=\"radio\"],\n+fieldset[disabled] input[type=\"checkbox\"] {\n+  cursor: not-allowed;\n+}\n input[type=\"file\"] {\n   display: block;\n }\n@@ -2585,7 +2645,6 @@ select[size] {\n input[type=\"file\"]:focus,\n input[type=\"radio\"]:focus,\n input[type=\"checkbox\"]:focus {\n-  outline: thin dotted;\n   outline: 5px auto -webkit-focus-ring-color;\n   outline-offset: -2px;\n }\n@@ -2594,7 +2653,7 @@ output {\n   padding-top: 7px;\n   font-size: 14px;\n   line-height: 1.42857143;\n-  color: #555;\n+  color: #555555;\n }\n .form-control {\n   display: block;\n@@ -2603,22 +2662,25 @@ output {\n   padding: 6px 12px;\n   font-size: 14px;\n   line-height: 1.42857143;\n-  color: #555;\n+  color: #555555;\n   background-color: #fff;\n   background-image: none;\n   border: 1px solid #ccc;\n   border-radius: 4px;\n-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n-          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n+  -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n+  -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n   -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;\n-       -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n-          transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n+  transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;\n+  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n+  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;\n }\n .form-control:focus {\n   border-color: #66afe9;\n   outline: 0;\n-  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);\n-          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);\n+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);\n+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);\n }\n .form-control::-moz-placeholder {\n   color: #999;\n@@ -2630,10 +2692,14 @@ output {\n .form-control::-webkit-input-placeholder {\n   color: #999;\n }\n+.form-control::-ms-expand {\n+  background-color: transparent;\n+  border: 0;\n+}\n .form-control[disabled],\n .form-control[readonly],\n fieldset[disabled] .form-control {\n-  background-color: #eee;\n+  background-color: #eeeeee;\n   opacity: 1;\n }\n .form-control[disabled],\n@@ -2643,9 +2709,6 @@ fieldset[disabled] .form-control {\n textarea.form-control {\n   height: auto;\n }\n-input[type=\"search\"] {\n-  -webkit-appearance: none;\n-}\n @media screen and (-webkit-min-device-pixel-ratio: 0) {\n   input[type=\"date\"].form-control,\n   input[type=\"time\"].form-control,\n@@ -2684,12 +2747,18 @@ input[type=\"search\"] {\n   margin-top: 10px;\n   margin-bottom: 10px;\n }\n+.radio.disabled label,\n+.checkbox.disabled label,\n+fieldset[disabled] .radio label,\n+fieldset[disabled] .checkbox label {\n+  cursor: not-allowed;\n+}\n .radio label,\n .checkbox label {\n   min-height: 20px;\n   padding-left: 20px;\n   margin-bottom: 0;\n-  font-weight: normal;\n+  font-weight: 400;\n   cursor: pointer;\n }\n .radio input[type=\"radio\"],\n@@ -2710,34 +2779,20 @@ input[type=\"search\"] {\n   display: inline-block;\n   padding-left: 20px;\n   margin-bottom: 0;\n-  font-weight: normal;\n+  font-weight: 400;\n   vertical-align: middle;\n   cursor: pointer;\n }\n-.radio-inline + .radio-inline,\n-.checkbox-inline + .checkbox-inline {\n-  margin-top: 0;\n-  margin-left: 10px;\n-}\n-input[type=\"radio\"][disabled],\n-input[type=\"checkbox\"][disabled],\n-input[type=\"radio\"].disabled,\n-input[type=\"checkbox\"].disabled,\n-fieldset[disabled] input[type=\"radio\"],\n-fieldset[disabled] input[type=\"checkbox\"] {\n-  cursor: not-allowed;\n-}\n .radio-inline.disabled,\n .checkbox-inline.disabled,\n fieldset[disabled] .radio-inline,\n fieldset[disabled] .checkbox-inline {\n   cursor: not-allowed;\n }\n-.radio.disabled label,\n-.checkbox.disabled label,\n-fieldset[disabled] .radio label,\n-fieldset[disabled] .checkbox label {\n-  cursor: not-allowed;\n+.radio-inline + .radio-inline,\n+.checkbox-inline + .checkbox-inline {\n+  margin-top: 0;\n+  margin-left: 10px;\n }\n .form-control-static {\n   min-height: 34px;\n@@ -2870,13 +2925,13 @@ select[multiple].input-lg {\n }\n .has-success .form-control {\n   border-color: #3c763d;\n-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n-          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n }\n .has-success .form-control:focus {\n   border-color: #2b542c;\n-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;\n-          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;\n+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n }\n .has-success .input-group-addon {\n   color: #3c763d;\n@@ -2900,13 +2955,13 @@ select[multiple].input-lg {\n }\n .has-warning .form-control {\n   border-color: #8a6d3b;\n-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n-          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n }\n .has-warning .form-control:focus {\n   border-color: #66512c;\n-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;\n-          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;\n+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n }\n .has-warning .input-group-addon {\n   color: #8a6d3b;\n@@ -2930,13 +2985,13 @@ select[multiple].input-lg {\n }\n .has-error .form-control {\n   border-color: #a94442;\n-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n-          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n }\n .has-error .form-control:focus {\n   border-color: #843534;\n-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;\n-          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;\n+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n }\n .has-error .input-group-addon {\n   color: #a94442;\n@@ -3036,7 +3091,7 @@ select[multiple].input-lg {\n }\n @media (min-width: 768px) {\n   .form-horizontal .form-group-lg .control-label {\n-    padding-top: 14.333333px;\n+    padding-top: 11px;\n     font-size: 18px;\n   }\n }\n@@ -3048,32 +3103,31 @@ select[multiple].input-lg {\n }\n .btn {\n   display: inline-block;\n-  padding: 6px 12px;\n   margin-bottom: 0;\n-  font-size: 14px;\n   font-weight: normal;\n-  line-height: 1.42857143;\n   text-align: center;\n   white-space: nowrap;\n   vertical-align: middle;\n   -ms-touch-action: manipulation;\n-      touch-action: manipulation;\n+  touch-action: manipulation;\n   cursor: pointer;\n-  -webkit-user-select: none;\n-     -moz-user-select: none;\n-      -ms-user-select: none;\n-          user-select: none;\n   background-image: none;\n   border: 1px solid transparent;\n+  padding: 6px 12px;\n+  font-size: 14px;\n+  line-height: 1.42857143;\n   border-radius: 4px;\n+  -webkit-user-select: none;\n+  -moz-user-select: none;\n+  -ms-user-select: none;\n+  user-select: none;\n }\n .btn:focus,\n .btn:active:focus,\n .btn.active:focus,\n .btn.focus,\n .btn:active.focus,\n .btn.active.focus {\n-  outline: thin dotted;\n   outline: 5px auto -webkit-focus-ring-color;\n   outline-offset: -2px;\n }\n@@ -3087,17 +3141,17 @@ select[multiple].input-lg {\n .btn.active {\n   background-image: none;\n   outline: 0;\n-  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n-          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n+  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n }\n .btn.disabled,\n .btn[disabled],\n fieldset[disabled] .btn {\n   cursor: not-allowed;\n   filter: alpha(opacity=65);\n+  opacity: 0.65;\n   -webkit-box-shadow: none;\n-          box-shadow: none;\n-  opacity: .65;\n+  box-shadow: none;\n }\n a.btn.disabled,\n fieldset[disabled] a.btn {\n@@ -3124,6 +3178,7 @@ fieldset[disabled] a.btn {\n .open > .dropdown-toggle.btn-default {\n   color: #333;\n   background-color: #e6e6e6;\n+  background-image: none;\n   border-color: #adadad;\n }\n .btn-default:active:hover,\n@@ -3139,14 +3194,6 @@ fieldset[disabled] a.btn {\n   background-color: #d4d4d4;\n   border-color: #8c8c8c;\n }\n-.btn-default:active,\n-.btn-default.active,\n-.open > .dropdown-toggle.btn-default {\n-  background-image: none;\n-}\n-.btn-default.disabled,\n-.btn-default[disabled],\n-fieldset[disabled] .btn-default,\n .btn-default.disabled:hover,\n .btn-default[disabled]:hover,\n fieldset[disabled] .btn-default:hover,\n@@ -3155,13 +3202,7 @@ fieldset[disabled] .btn-default:hover,\n fieldset[disabled] .btn-default:focus,\n .btn-default.disabled.focus,\n .btn-default[disabled].focus,\n-fieldset[disabled] .btn-default.focus,\n-.btn-default.disabled:active,\n-.btn-default[disabled]:active,\n-fieldset[disabled] .btn-default:active,\n-.btn-default.disabled.active,\n-.btn-default[disabled].active,\n-fieldset[disabled] .btn-default.active {\n+fieldset[disabled] .btn-default.focus {\n   background-color: #fff;\n   border-color: #ccc;\n }\n@@ -3190,6 +3231,7 @@ fieldset[disabled] .btn-default.active {\n .open > .dropdown-toggle.btn-primary {\n   color: #fff;\n   background-color: #286090;\n+  background-image: none;\n   border-color: #204d74;\n }\n .btn-primary:active:hover,\n@@ -3205,14 +3247,6 @@ fieldset[disabled] .btn-default.active {\n   background-color: #204d74;\n   border-color: #122b40;\n }\n-.btn-primary:active,\n-.btn-primary.active,\n-.open > .dropdown-toggle.btn-primary {\n-  background-image: none;\n-}\n-.btn-primary.disabled,\n-.btn-primary[disabled],\n-fieldset[disabled] .btn-primary,\n .btn-primary.disabled:hover,\n .btn-primary[disabled]:hover,\n fieldset[disabled] .btn-primary:hover,\n@@ -3221,13 +3255,7 @@ fieldset[disabled] .btn-primary:hover,\n fieldset[disabled] .btn-primary:focus,\n .btn-primary.disabled.focus,\n .btn-primary[disabled].focus,\n-fieldset[disabled] .btn-primary.focus,\n-.btn-primary.disabled:active,\n-.btn-primary[disabled]:active,\n-fieldset[disabled] .btn-primary:active,\n-.btn-primary.disabled.active,\n-.btn-primary[disabled].active,\n-fieldset[disabled] .btn-primary.active {\n+fieldset[disabled] .btn-primary.focus {\n   background-color: #337ab7;\n   border-color: #2e6da4;\n }\n@@ -3256,6 +3284,7 @@ fieldset[disabled] .btn-primary.active {\n .open > .dropdown-toggle.btn-success {\n   color: #fff;\n   background-color: #449d44;\n+  background-image: none;\n   border-color: #398439;\n }\n .btn-success:active:hover,\n@@ -3271,14 +3300,6 @@ fieldset[disabled] .btn-primary.active {\n   background-color: #398439;\n   border-color: #255625;\n }\n-.btn-success:active,\n-.btn-success.active,\n-.open > .dropdown-toggle.btn-success {\n-  background-image: none;\n-}\n-.btn-success.disabled,\n-.btn-success[disabled],\n-fieldset[disabled] .btn-success,\n .btn-success.disabled:hover,\n .btn-success[disabled]:hover,\n fieldset[disabled] .btn-success:hover,\n@@ -3287,13 +3308,7 @@ fieldset[disabled] .btn-success:hover,\n fieldset[disabled] .btn-success:focus,\n .btn-success.disabled.focus,\n .btn-success[disabled].focus,\n-fieldset[disabled] .btn-success.focus,\n-.btn-success.disabled:active,\n-.btn-success[disabled]:active,\n-fieldset[disabled] .btn-success:active,\n-.btn-success.disabled.active,\n-.btn-success[disabled].active,\n-fieldset[disabled] .btn-success.active {\n+fieldset[disabled] .btn-success.focus {\n   background-color: #5cb85c;\n   border-color: #4cae4c;\n }\n@@ -3322,6 +3337,7 @@ fieldset[disabled] .btn-success.active {\n .open > .dropdown-toggle.btn-info {\n   color: #fff;\n   background-color: #31b0d5;\n+  background-image: none;\n   border-color: #269abc;\n }\n .btn-info:active:hover,\n@@ -3337,14 +3353,6 @@ fieldset[disabled] .btn-success.active {\n   background-color: #269abc;\n   border-color: #1b6d85;\n }\n-.btn-info:active,\n-.btn-info.active,\n-.open > .dropdown-toggle.btn-info {\n-  background-image: none;\n-}\n-.btn-info.disabled,\n-.btn-info[disabled],\n-fieldset[disabled] .btn-info,\n .btn-info.disabled:hover,\n .btn-info[disabled]:hover,\n fieldset[disabled] .btn-info:hover,\n@@ -3353,13 +3361,7 @@ fieldset[disabled] .btn-info:hover,\n fieldset[disabled] .btn-info:focus,\n .btn-info.disabled.focus,\n .btn-info[disabled].focus,\n-fieldset[disabled] .btn-info.focus,\n-.btn-info.disabled:active,\n-.btn-info[disabled]:active,\n-fieldset[disabled] .btn-info:active,\n-.btn-info.disabled.active,\n-.btn-info[disabled].active,\n-fieldset[disabled] .btn-info.active {\n+fieldset[disabled] .btn-info.focus {\n   background-color: #5bc0de;\n   border-color: #46b8da;\n }\n@@ -3388,6 +3390,7 @@ fieldset[disabled] .btn-info.active {\n .open > .dropdown-toggle.btn-warning {\n   color: #fff;\n   background-color: #ec971f;\n+  background-image: none;\n   border-color: #d58512;\n }\n .btn-warning:active:hover,\n@@ -3403,14 +3406,6 @@ fieldset[disabled] .btn-info.active {\n   background-color: #d58512;\n   border-color: #985f0d;\n }\n-.btn-warning:active,\n-.btn-warning.active,\n-.open > .dropdown-toggle.btn-warning {\n-  background-image: none;\n-}\n-.btn-warning.disabled,\n-.btn-warning[disabled],\n-fieldset[disabled] .btn-warning,\n .btn-warning.disabled:hover,\n .btn-warning[disabled]:hover,\n fieldset[disabled] .btn-warning:hover,\n@@ -3419,13 +3414,7 @@ fieldset[disabled] .btn-warning:hover,\n fieldset[disabled] .btn-warning:focus,\n .btn-warning.disabled.focus,\n .btn-warning[disabled].focus,\n-fieldset[disabled] .btn-warning.focus,\n-.btn-warning.disabled:active,\n-.btn-warning[disabled]:active,\n-fieldset[disabled] .btn-warning:active,\n-.btn-warning.disabled.active,\n-.btn-warning[disabled].active,\n-fieldset[disabled] .btn-warning.active {\n+fieldset[disabled] .btn-warning.focus {\n   background-color: #f0ad4e;\n   border-color: #eea236;\n }\n@@ -3454,6 +3443,7 @@ fieldset[disabled] .btn-warning.active {\n .open > .dropdown-toggle.btn-danger {\n   color: #fff;\n   background-color: #c9302c;\n+  background-image: none;\n   border-color: #ac2925;\n }\n .btn-danger:active:hover,\n@@ -3469,14 +3459,6 @@ fieldset[disabled] .btn-warning.active {\n   background-color: #ac2925;\n   border-color: #761c19;\n }\n-.btn-danger:active,\n-.btn-danger.active,\n-.open > .dropdown-toggle.btn-danger {\n-  background-image: none;\n-}\n-.btn-danger.disabled,\n-.btn-danger[disabled],\n-fieldset[disabled] .btn-danger,\n .btn-danger.disabled:hover,\n .btn-danger[disabled]:hover,\n fieldset[disabled] .btn-danger:hover,\n@@ -3485,13 +3467,7 @@ fieldset[disabled] .btn-danger:hover,\n fieldset[disabled] .btn-danger:focus,\n .btn-danger.disabled.focus,\n .btn-danger[disabled].focus,\n-fieldset[disabled] .btn-danger.focus,\n-.btn-danger.disabled:active,\n-.btn-danger[disabled]:active,\n-fieldset[disabled] .btn-danger:active,\n-.btn-danger.disabled.active,\n-.btn-danger[disabled].active,\n-fieldset[disabled] .btn-danger.active {\n+fieldset[disabled] .btn-danger.focus {\n   background-color: #d9534f;\n   border-color: #d43f3a;\n }\n@@ -3500,7 +3476,7 @@ fieldset[disabled] .btn-danger.active {\n   background-color: #fff;\n }\n .btn-link {\n-  font-weight: normal;\n+  font-weight: 400;\n   color: #337ab7;\n   border-radius: 0;\n }\n@@ -3511,7 +3487,7 @@ fieldset[disabled] .btn-danger.active {\n fieldset[disabled] .btn-link {\n   background-color: transparent;\n   -webkit-box-shadow: none;\n-          box-shadow: none;\n+  box-shadow: none;\n }\n .btn-link,\n .btn-link:hover,\n@@ -3529,7 +3505,7 @@ fieldset[disabled] .btn-link {\n fieldset[disabled] .btn-link:hover,\n .btn-link[disabled]:focus,\n fieldset[disabled] .btn-link:focus {\n-  color: #777;\n+  color: #777777;\n   text-decoration: none;\n }\n .btn-lg,\n@@ -3567,9 +3543,9 @@ input[type=\"button\"].btn-block {\n }\n .fade {\n   opacity: 0;\n-  -webkit-transition: opacity .15s linear;\n-       -o-transition: opacity .15s linear;\n-          transition: opacity .15s linear;\n+  -webkit-transition: opacity 0.15s linear;\n+  -o-transition: opacity 0.15s linear;\n+  transition: opacity 0.15s linear;\n }\n .fade.in {\n   opacity: 1;\n@@ -3590,15 +3566,15 @@ tbody.collapse.in {\n   position: relative;\n   height: 0;\n   overflow: hidden;\n-  -webkit-transition-timing-function: ease;\n-       -o-transition-timing-function: ease;\n-          transition-timing-function: ease;\n-  -webkit-transition-duration: .35s;\n-       -o-transition-duration: .35s;\n-          transition-duration: .35s;\n   -webkit-transition-property: height, visibility;\n-       -o-transition-property: height, visibility;\n-          transition-property: height, visibility;\n+  -o-transition-property: height, visibility;\n+  transition-property: height, visibility;\n+  -webkit-transition-duration: 0.35s;\n+  -o-transition-duration: 0.35s;\n+  transition-duration: 0.35s;\n+  -webkit-transition-timing-function: ease;\n+  -o-transition-timing-function: ease;\n+  transition-timing-function: ease;\n }\n .caret {\n   display: inline-block;\n@@ -3632,13 +3608,12 @@ tbody.collapse.in {\n   text-align: left;\n   list-style: none;\n   background-color: #fff;\n-  -webkit-background-clip: padding-box;\n-          background-clip: padding-box;\n+  background-clip: padding-box;\n   border: 1px solid #ccc;\n-  border: 1px solid rgba(0, 0, 0, .15);\n+  border: 1px solid rgba(0, 0, 0, 0.15);\n   border-radius: 4px;\n-  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n-          box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n+  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n+  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n }\n .dropdown-menu.pull-right {\n   right: 0;\n@@ -3654,9 +3629,9 @@ tbody.collapse.in {\n   display: block;\n   padding: 3px 20px;\n   clear: both;\n-  font-weight: normal;\n+  font-weight: 400;\n   line-height: 1.42857143;\n-  color: #333;\n+  color: #333333;\n   white-space: nowrap;\n }\n .dropdown-menu > li > a:hover,\n@@ -3676,7 +3651,7 @@ tbody.collapse.in {\n .dropdown-menu > .disabled > a,\n .dropdown-menu > .disabled > a:hover,\n .dropdown-menu > .disabled > a:focus {\n-  color: #777;\n+  color: #777777;\n }\n .dropdown-menu > .disabled > a:hover,\n .dropdown-menu > .disabled > a:focus {\n@@ -3705,7 +3680,7 @@ tbody.collapse.in {\n   padding: 3px 20px;\n   font-size: 12px;\n   line-height: 1.42857143;\n-  color: #777;\n+  color: #777777;\n   white-space: nowrap;\n }\n .dropdown-backdrop {\n@@ -3826,12 +3801,12 @@ tbody.collapse.in {\n   padding-left: 12px;\n }\n .btn-group.open .dropdown-toggle {\n-  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n-          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n+  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n }\n .btn-group.open .dropdown-toggle.btn-link {\n   -webkit-box-shadow: none;\n-          box-shadow: none;\n+  box-shadow: none;\n }\n .btn .caret {\n   margin-left: 0;\n@@ -3865,13 +3840,15 @@ tbody.collapse.in {\n   border-radius: 0;\n }\n .btn-group-vertical > .btn:first-child:not(:last-child) {\n+  border-top-left-radius: 4px;\n   border-top-right-radius: 4px;\n   border-bottom-right-radius: 0;\n   border-bottom-left-radius: 0;\n }\n .btn-group-vertical > .btn:last-child:not(:first-child) {\n   border-top-left-radius: 0;\n   border-top-right-radius: 0;\n+  border-bottom-right-radius: 4px;\n   border-bottom-left-radius: 4px;\n }\n .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n@@ -3929,6 +3906,9 @@ tbody.collapse.in {\n   width: 100%;\n   margin-bottom: 0;\n }\n+.input-group .form-control:focus {\n+  z-index: 3;\n+}\n .input-group-lg > .form-control,\n .input-group-lg > .input-group-addon,\n .input-group-lg > .input-group-btn > .btn {\n@@ -3994,11 +3974,11 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n .input-group-addon {\n   padding: 6px 12px;\n   font-size: 14px;\n-  font-weight: normal;\n+  font-weight: 400;\n   line-height: 1;\n-  color: #555;\n+  color: #555555;\n   text-align: center;\n-  background-color: #eee;\n+  background-color: #eeeeee;\n   border: 1px solid #ccc;\n   border-radius: 4px;\n }\n@@ -4084,22 +4064,22 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n .nav > li > a:hover,\n .nav > li > a:focus {\n   text-decoration: none;\n-  background-color: #eee;\n+  background-color: #eeeeee;\n }\n .nav > li.disabled > a {\n-  color: #777;\n+  color: #777777;\n }\n .nav > li.disabled > a:hover,\n .nav > li.disabled > a:focus {\n-  color: #777;\n+  color: #777777;\n   text-decoration: none;\n   cursor: not-allowed;\n   background-color: transparent;\n }\n .nav .open > a,\n .nav .open > a:hover,\n .nav .open > a:focus {\n-  background-color: #eee;\n+  background-color: #eeeeee;\n   border-color: #337ab7;\n }\n .nav .nav-divider {\n@@ -4125,12 +4105,12 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n   border-radius: 4px 4px 0 0;\n }\n .nav-tabs > li > a:hover {\n-  border-color: #eee #eee #ddd;\n+  border-color: #eeeeee #eeeeee #ddd;\n }\n .nav-tabs > li.active > a,\n .nav-tabs > li.active > a:hover,\n .nav-tabs > li.active > a:focus {\n-  color: #555;\n+  color: #555555;\n   cursor: default;\n   background-color: #fff;\n   border: 1px solid #ddd;\n@@ -4279,10 +4259,10 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n   padding-right: 15px;\n   padding-left: 15px;\n   overflow-x: visible;\n-  -webkit-overflow-scrolling: touch;\n   border-top: 1px solid transparent;\n-  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);\n-          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);\n+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n+  -webkit-overflow-scrolling: touch;\n }\n .navbar-collapse.in {\n   overflow-y: auto;\n@@ -4292,7 +4272,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n     width: auto;\n     border-top: 0;\n     -webkit-box-shadow: none;\n-            box-shadow: none;\n+    box-shadow: none;\n   }\n   .navbar-collapse.collapse {\n     display: block !important;\n@@ -4310,6 +4290,13 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n     padding-left: 0;\n   }\n }\n+.navbar-fixed-top,\n+.navbar-fixed-bottom {\n+  position: fixed;\n+  right: 0;\n+  left: 0;\n+  z-index: 1030;\n+}\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n   max-height: 340px;\n@@ -4320,6 +4307,21 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n     max-height: 200px;\n   }\n }\n+@media (min-width: 768px) {\n+  .navbar-fixed-top,\n+  .navbar-fixed-bottom {\n+    border-radius: 0;\n+  }\n+}\n+.navbar-fixed-top {\n+  top: 0;\n+  border-width: 0 0 1px;\n+}\n+.navbar-fixed-bottom {\n+  bottom: 0;\n+  margin-bottom: 0;\n+  border-width: 1px 0 0;\n+}\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n@@ -4345,28 +4347,6 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n     border-radius: 0;\n   }\n }\n-.navbar-fixed-top,\n-.navbar-fixed-bottom {\n-  position: fixed;\n-  right: 0;\n-  left: 0;\n-  z-index: 1030;\n-}\n-@media (min-width: 768px) {\n-  .navbar-fixed-top,\n-  .navbar-fixed-bottom {\n-    border-radius: 0;\n-  }\n-}\n-.navbar-fixed-top {\n-  top: 0;\n-  border-width: 0 0 1px;\n-}\n-.navbar-fixed-bottom {\n-  bottom: 0;\n-  margin-bottom: 0;\n-  border-width: 1px 0 0;\n-}\n .navbar-brand {\n   float: left;\n   height: 50px;\n@@ -4391,8 +4371,8 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n   position: relative;\n   float: right;\n   padding: 9px 10px;\n-  margin-top: 8px;\n   margin-right: 15px;\n+  margin-top: 8px;\n   margin-bottom: 8px;\n   background-color: transparent;\n   background-image: none;\n@@ -4433,7 +4413,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n     background-color: transparent;\n     border: 0;\n     -webkit-box-shadow: none;\n-            box-shadow: none;\n+    box-shadow: none;\n   }\n   .navbar-nav .open .dropdown-menu > li > a,\n   .navbar-nav .open .dropdown-menu .dropdown-header {\n@@ -4462,14 +4442,14 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n }\n .navbar-form {\n   padding: 10px 15px;\n-  margin-top: 8px;\n   margin-right: -15px;\n-  margin-bottom: 8px;\n   margin-left: -15px;\n   border-top: 1px solid transparent;\n   border-bottom: 1px solid transparent;\n-  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);\n-          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);\n+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n+  margin-top: 8px;\n+  margin-bottom: 8px;\n }\n @media (min-width: 768px) {\n   .navbar-form .form-group {\n@@ -4538,7 +4518,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n     margin-left: 0;\n     border: 0;\n     -webkit-box-shadow: none;\n-            box-shadow: none;\n+    box-shadow: none;\n   }\n }\n .navbar-nav > li > .dropdown-menu {\n@@ -4623,20 +4603,6 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n   color: #ccc;\n   background-color: transparent;\n }\n-.navbar-default .navbar-toggle {\n-  border-color: #ddd;\n-}\n-.navbar-default .navbar-toggle:hover,\n-.navbar-default .navbar-toggle:focus {\n-  background-color: #ddd;\n-}\n-.navbar-default .navbar-toggle .icon-bar {\n-  background-color: #888;\n-}\n-.navbar-default .navbar-collapse,\n-.navbar-default .navbar-form {\n-  border-color: #e7e7e7;\n-}\n .navbar-default .navbar-nav > .open > a,\n .navbar-default .navbar-nav > .open > a:hover,\n .navbar-default .navbar-nav > .open > a:focus {\n@@ -4665,6 +4631,20 @@ select[multiple].input-group-sm > .input-group-btn > .btn {\n     background-color: transparent;\n   }\n }\n+.navbar-default .navbar-toggle {\n+  border-color: #ddd;\n+}\n+.navbar-default .navbar-toggle:hover,\n+.navbar-default .navbar-toggle:focus {\n+  background-color: #ddd;\n+}\n+.navbar-default .navbar-toggle .icon-bar {\n+  background-color: #888;\n+}\n+.navbar-default .navbar-collapse,\n+.navbar-default .navbar-form {\n+  border-color: #e7e7e7;\n+}\n .navbar-default .navbar-link {\n   color: #777;\n }\n@@ -4719,20 +4699,6 @@ fieldset[disabled] .navbar-default .btn-link:focus {\n   color: #444;\n   background-color: transparent;\n }\n-.navbar-inverse .navbar-toggle {\n-  border-color: #333;\n-}\n-.navbar-inverse .navbar-toggle:hover,\n-.navbar-inverse .navbar-toggle:focus {\n-  background-color: #333;\n-}\n-.navbar-inverse .navbar-toggle .icon-bar {\n-  background-color: #fff;\n-}\n-.navbar-inverse .navbar-collapse,\n-.navbar-inverse .navbar-form {\n-  border-color: #101010;\n-}\n .navbar-inverse .navbar-nav > .open > a,\n .navbar-inverse .navbar-nav > .open > a:hover,\n .navbar-inverse .navbar-nav > .open > a:focus {\n@@ -4767,6 +4733,20 @@ fieldset[disabled] .navbar-default .btn-link:focus {\n     background-color: transparent;\n   }\n }\n+.navbar-inverse .navbar-toggle {\n+  border-color: #333;\n+}\n+.navbar-inverse .navbar-toggle:hover,\n+.navbar-inverse .navbar-toggle:focus {\n+  background-color: #333;\n+}\n+.navbar-inverse .navbar-toggle .icon-bar {\n+  background-color: #fff;\n+}\n+.navbar-inverse .navbar-collapse,\n+.navbar-inverse .navbar-form {\n+  border-color: #101010;\n+}\n .navbar-inverse .navbar-link {\n   color: #9d9d9d;\n }\n@@ -4802,7 +4782,7 @@ fieldset[disabled] .navbar-inverse .btn-link:focus {\n   content: \"/\\00a0\";\n }\n .breadcrumb > .active {\n-  color: #777;\n+  color: #777777;\n }\n .pagination {\n   display: inline-block;\n@@ -4825,6 +4805,15 @@ fieldset[disabled] .navbar-inverse .btn-link:focus {\n   background-color: #fff;\n   border: 1px solid #ddd;\n }\n+.pagination > li > a:hover,\n+.pagination > li > span:hover,\n+.pagination > li > a:focus,\n+.pagination > li > span:focus {\n+  z-index: 2;\n+  color: #23527c;\n+  background-color: #eeeeee;\n+  border-color: #ddd;\n+}\n .pagination > li:first-child > a,\n .pagination > li:first-child > span {\n   margin-left: 0;\n@@ -4836,22 +4825,13 @@ fieldset[disabled] .navbar-inverse .btn-link:focus {\n   border-top-right-radius: 4px;\n   border-bottom-right-radius: 4px;\n }\n-.pagination > li > a:hover,\n-.pagination > li > span:hover,\n-.pagination > li > a:focus,\n-.pagination > li > span:focus {\n-  z-index: 3;\n-  color: #23527c;\n-  background-color: #eee;\n-  border-color: #ddd;\n-}\n .pagination > .active > a,\n .pagination > .active > span,\n .pagination > .active > a:hover,\n .pagination > .active > span:hover,\n .pagination > .active > a:focus,\n .pagination > .active > span:focus {\n-  z-index: 2;\n+  z-index: 3;\n   color: #fff;\n   cursor: default;\n   background-color: #337ab7;\n@@ -4863,7 +4843,7 @@ fieldset[disabled] .navbar-inverse .btn-link:focus {\n .pagination > .disabled > a,\n .pagination > .disabled > a:hover,\n .pagination > .disabled > a:focus {\n-  color: #777;\n+  color: #777777;\n   cursor: not-allowed;\n   background-color: #fff;\n   border-color: #ddd;\n@@ -4920,7 +4900,7 @@ fieldset[disabled] .navbar-inverse .btn-link:focus {\n .pager li > a:hover,\n .pager li > a:focus {\n   text-decoration: none;\n-  background-color: #eee;\n+  background-color: #eeeeee;\n }\n .pager .next > a,\n .pager .next > span {\n@@ -4934,21 +4914,21 @@ fieldset[disabled] .navbar-inverse .btn-link:focus {\n .pager .disabled > a:hover,\n .pager .disabled > a:focus,\n .pager .disabled > span {\n-  color: #777;\n+  color: #777777;\n   cursor: not-allowed;\n   background-color: #fff;\n }\n .label {\n   display: inline;\n-  padding: .2em .6em .3em;\n+  padding: 0.2em 0.6em 0.3em;\n   font-size: 75%;\n-  font-weight: bold;\n+  font-weight: 700;\n   line-height: 1;\n   color: #fff;\n   text-align: center;\n   white-space: nowrap;\n   vertical-align: baseline;\n-  border-radius: .25em;\n+  border-radius: 0.25em;\n }\n a.label:hover,\n a.label:focus {\n@@ -4964,7 +4944,7 @@ a.label:focus {\n   top: -1px;\n }\n .label-default {\n-  background-color: #777;\n+  background-color: #777777;\n }\n .label-default[href]:hover,\n .label-default[href]:focus {\n@@ -5016,7 +4996,7 @@ a.label:focus {\n   text-align: center;\n   white-space: nowrap;\n   vertical-align: middle;\n-  background-color: #777;\n+  background-color: #777777;\n   border-radius: 10px;\n }\n .badge:empty {\n@@ -5056,7 +5036,7 @@ a.badge:focus {\n   padding-bottom: 30px;\n   margin-bottom: 30px;\n   color: inherit;\n-  background-color: #eee;\n+  background-color: #eeeeee;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n@@ -5072,6 +5052,8 @@ a.badge:focus {\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n+  padding-right: 15px;\n+  padding-left: 15px;\n   border-radius: 6px;\n }\n .jumbotron .container {\n@@ -5100,9 +5082,9 @@ a.badge:focus {\n   background-color: #fff;\n   border: 1px solid #ddd;\n   border-radius: 4px;\n-  -webkit-transition: border .2s ease-in-out;\n-       -o-transition: border .2s ease-in-out;\n-          transition: border .2s ease-in-out;\n+  -webkit-transition: border 0.2s ease-in-out;\n+  -o-transition: border 0.2s ease-in-out;\n+  transition: border 0.2s ease-in-out;\n }\n .thumbnail > img,\n .thumbnail a > img {\n@@ -5116,7 +5098,7 @@ a.thumbnail.active {\n }\n .thumbnail .caption {\n   padding: 9px;\n-  color: #333;\n+  color: #333333;\n }\n .alert {\n   padding: 15px;\n@@ -5223,69 +5205,69 @@ a.thumbnail.active {\n   overflow: hidden;\n   background-color: #f5f5f5;\n   border-radius: 4px;\n-  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n-          box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n }\n .progress-bar {\n   float: left;\n-  width: 0;\n+  width: 0%;\n   height: 100%;\n   font-size: 12px;\n   line-height: 20px;\n   color: #fff;\n   text-align: center;\n   background-color: #337ab7;\n-  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);\n-          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);\n-  -webkit-transition: width .6s ease;\n-       -o-transition: width .6s ease;\n-          transition: width .6s ease;\n+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n+  -webkit-transition: width 0.6s ease;\n+  -o-transition: width 0.6s ease;\n+  transition: width 0.6s ease;\n }\n .progress-striped .progress-bar,\n .progress-bar-striped {\n-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n   -webkit-background-size: 40px 40px;\n-          background-size: 40px 40px;\n+  background-size: 40px 40px;\n }\n .progress.active .progress-bar,\n .progress-bar.active {\n   -webkit-animation: progress-bar-stripes 2s linear infinite;\n-       -o-animation: progress-bar-stripes 2s linear infinite;\n-          animation: progress-bar-stripes 2s linear infinite;\n+  -o-animation: progress-bar-stripes 2s linear infinite;\n+  animation: progress-bar-stripes 2s linear infinite;\n }\n .progress-bar-success {\n   background-color: #5cb85c;\n }\n .progress-striped .progress-bar-success {\n-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n }\n .progress-bar-info {\n   background-color: #5bc0de;\n }\n .progress-striped .progress-bar-info {\n-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n }\n .progress-bar-warning {\n   background-color: #f0ad4e;\n }\n .progress-striped .progress-bar-warning {\n-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n }\n .progress-bar-danger {\n   background-color: #d9534f;\n }\n .progress-striped .progress-bar-danger {\n-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n-  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n }\n .media {\n   margin-top: 15px;\n@@ -5356,32 +5338,12 @@ a.thumbnail.active {\n   border-bottom-right-radius: 4px;\n   border-bottom-left-radius: 4px;\n }\n-a.list-group-item,\n-button.list-group-item {\n-  color: #555;\n-}\n-a.list-group-item .list-group-item-heading,\n-button.list-group-item .list-group-item-heading {\n-  color: #333;\n-}\n-a.list-group-item:hover,\n-button.list-group-item:hover,\n-a.list-group-item:focus,\n-button.list-group-item:focus {\n-  color: #555;\n-  text-decoration: none;\n-  background-color: #f5f5f5;\n-}\n-button.list-group-item {\n-  width: 100%;\n-  text-align: left;\n-}\n .list-group-item.disabled,\n .list-group-item.disabled:hover,\n .list-group-item.disabled:focus {\n-  color: #777;\n+  color: #777777;\n   cursor: not-allowed;\n-  background-color: #eee;\n+  background-color: #eeeeee;\n }\n .list-group-item.disabled .list-group-item-heading,\n .list-group-item.disabled:hover .list-group-item-heading,\n@@ -5391,7 +5353,7 @@ button.list-group-item {\n .list-group-item.disabled .list-group-item-text,\n .list-group-item.disabled:hover .list-group-item-text,\n .list-group-item.disabled:focus .list-group-item-text {\n-  color: #777;\n+  color: #777777;\n }\n .list-group-item.active,\n .list-group-item.active:hover,\n@@ -5417,6 +5379,26 @@ button.list-group-item {\n .list-group-item.active:focus .list-group-item-text {\n   color: #c7ddef;\n }\n+a.list-group-item,\n+button.list-group-item {\n+  color: #555;\n+}\n+a.list-group-item .list-group-item-heading,\n+button.list-group-item .list-group-item-heading {\n+  color: #333;\n+}\n+a.list-group-item:hover,\n+button.list-group-item:hover,\n+a.list-group-item:focus,\n+button.list-group-item:focus {\n+  color: #555;\n+  text-decoration: none;\n+  background-color: #f5f5f5;\n+}\n+button.list-group-item {\n+  width: 100%;\n+  text-align: left;\n+}\n .list-group-item-success {\n   color: #3c763d;\n   background-color: #dff0d8;\n@@ -5546,8 +5528,8 @@ button.list-group-item-danger.active:focus {\n   background-color: #fff;\n   border: 1px solid transparent;\n   border-radius: 4px;\n-  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);\n-          box-shadow: 0 1px 1px rgba(0, 0, 0, .05);\n+  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n }\n .panel-body {\n   padding: 15px;\n@@ -5780,7 +5762,7 @@ button.list-group-item-danger.active:focus {\n   border-color: #ddd;\n }\n .panel-default > .panel-heading {\n-  color: #333;\n+  color: #333333;\n   background-color: #f5f5f5;\n   border-color: #ddd;\n }\n@@ -5789,7 +5771,7 @@ button.list-group-item-danger.active:focus {\n }\n .panel-default > .panel-heading .badge {\n   color: #f5f5f5;\n-  background-color: #333;\n+  background-color: #333333;\n }\n .panel-default > .panel-footer + .panel-collapse > .panel-body {\n   border-bottom-color: #ddd;\n@@ -5917,12 +5899,12 @@ button.list-group-item-danger.active:focus {\n   background-color: #f5f5f5;\n   border: 1px solid #e3e3e3;\n   border-radius: 4px;\n-  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);\n-          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);\n+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n }\n .well blockquote {\n   border-color: #ddd;\n-  border-color: rgba(0, 0, 0, .15);\n+  border-color: rgba(0, 0, 0, 0.15);\n }\n .well-lg {\n   padding: 24px;\n@@ -5940,22 +5922,24 @@ button.list-group-item-danger.active:focus {\n   color: #000;\n   text-shadow: 0 1px 0 #fff;\n   filter: alpha(opacity=20);\n-  opacity: .2;\n+  opacity: 0.2;\n }\n .close:hover,\n .close:focus {\n   color: #000;\n   text-decoration: none;\n   cursor: pointer;\n   filter: alpha(opacity=50);\n-  opacity: .5;\n+  opacity: 0.5;\n }\n button.close {\n-  -webkit-appearance: none;\n   padding: 0;\n   cursor: pointer;\n   background: transparent;\n   border: 0;\n+  -webkit-appearance: none;\n+  -moz-appearance: none;\n+  appearance: none;\n }\n .modal-open {\n   overflow: hidden;\n@@ -5973,19 +5957,21 @@ button.close {\n   outline: 0;\n }\n .modal.fade .modal-dialog {\n-  -webkit-transition: -webkit-transform .3s ease-out;\n-       -o-transition:      -o-transform .3s ease-out;\n-          transition:         transform .3s ease-out;\n   -webkit-transform: translate(0, -25%);\n-      -ms-transform: translate(0, -25%);\n-       -o-transform: translate(0, -25%);\n-          transform: translate(0, -25%);\n+  -ms-transform: translate(0, -25%);\n+  -o-transform: translate(0, -25%);\n+  transform: translate(0, -25%);\n+  -webkit-transition: -webkit-transform 0.3s ease-out;\n+  -o-transition: -o-transform 0.3s ease-out;\n+  transition: -webkit-transform 0.3s ease-out;\n+  transition: transform 0.3s ease-out;\n+  transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out, -o-transform 0.3s ease-out;\n }\n .modal.in .modal-dialog {\n   -webkit-transform: translate(0, 0);\n-      -ms-transform: translate(0, 0);\n-       -o-transform: translate(0, 0);\n-          transform: translate(0, 0);\n+  -ms-transform: translate(0, 0);\n+  -o-transform: translate(0, 0);\n+  transform: translate(0, 0);\n }\n .modal-open .modal {\n   overflow-x: hidden;\n@@ -5999,14 +5985,13 @@ button.close {\n .modal-content {\n   position: relative;\n   background-color: #fff;\n-  -webkit-background-clip: padding-box;\n-          background-clip: padding-box;\n+  background-clip: padding-box;\n   border: 1px solid #999;\n-  border: 1px solid rgba(0, 0, 0, .2);\n+  border: 1px solid rgba(0, 0, 0, 0.2);\n   border-radius: 6px;\n+  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n+  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n   outline: 0;\n-  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);\n-          box-shadow: 0 3px 9px rgba(0, 0, 0, .5);\n }\n .modal-backdrop {\n   position: fixed;\n@@ -6023,10 +6008,9 @@ button.close {\n }\n .modal-backdrop.in {\n   filter: alpha(opacity=50);\n-  opacity: .5;\n+  opacity: 0.5;\n }\n .modal-header {\n-  min-height: 16.42857143px;\n   padding: 15px;\n   border-bottom: 1px solid #e5e5e5;\n }\n@@ -6069,8 +6053,8 @@ button.close {\n     margin: 30px auto;\n   }\n   .modal-content {\n-    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);\n-            box-shadow: 0 5px 15px rgba(0, 0, 0, .5);\n+    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n+    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n   }\n   .modal-sm {\n     width: 300px;\n@@ -6086,10 +6070,10 @@ button.close {\n   z-index: 1070;\n   display: block;\n   font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n-  font-size: 12px;\n   font-style: normal;\n-  font-weight: normal;\n+  font-weight: 400;\n   line-height: 1.42857143;\n+  line-break: auto;\n   text-align: left;\n   text-align: start;\n   text-decoration: none;\n@@ -6100,14 +6084,13 @@ button.close {\n   word-spacing: normal;\n   word-wrap: normal;\n   white-space: normal;\n+  font-size: 12px;\n   filter: alpha(opacity=0);\n   opacity: 0;\n-\n-  line-break: auto;\n }\n .tooltip.in {\n   filter: alpha(opacity=90);\n-  opacity: .9;\n+  opacity: 0.9;\n }\n .tooltip.top {\n   padding: 5px 0;\n@@ -6125,21 +6108,6 @@ button.close {\n   padding: 0 5px;\n   margin-left: -3px;\n }\n-.tooltip-inner {\n-  max-width: 200px;\n-  padding: 3px 8px;\n-  color: #fff;\n-  text-align: center;\n-  background-color: #000;\n-  border-radius: 4px;\n-}\n-.tooltip-arrow {\n-  position: absolute;\n-  width: 0;\n-  height: 0;\n-  border-color: transparent;\n-  border-style: solid;\n-}\n .tooltip.top .tooltip-arrow {\n   bottom: 0;\n   left: 50%;\n@@ -6196,6 +6164,21 @@ button.close {\n   border-width: 0 5px 5px;\n   border-bottom-color: #000;\n }\n+.tooltip-inner {\n+  max-width: 200px;\n+  padding: 3px 8px;\n+  color: #fff;\n+  text-align: center;\n+  background-color: #000;\n+  border-radius: 4px;\n+}\n+.tooltip-arrow {\n+  position: absolute;\n+  width: 0;\n+  height: 0;\n+  border-color: transparent;\n+  border-style: solid;\n+}\n .popover {\n   position: absolute;\n   top: 0;\n@@ -6205,10 +6188,10 @@ button.close {\n   max-width: 276px;\n   padding: 1px;\n   font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n-  font-size: 14px;\n   font-style: normal;\n-  font-weight: normal;\n+  font-weight: 400;\n   line-height: 1.42857143;\n+  line-break: auto;\n   text-align: left;\n   text-align: start;\n   text-decoration: none;\n@@ -6219,16 +6202,14 @@ button.close {\n   word-spacing: normal;\n   word-wrap: normal;\n   white-space: normal;\n+  font-size: 14px;\n   background-color: #fff;\n-  -webkit-background-clip: padding-box;\n-          background-clip: padding-box;\n+  background-clip: padding-box;\n   border: 1px solid #ccc;\n-  border: 1px solid rgba(0, 0, 0, .2);\n+  border: 1px solid rgba(0, 0, 0, 0.2);\n   border-radius: 6px;\n-  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);\n-          box-shadow: 0 5px 10px rgba(0, 0, 0, .2);\n-\n-  line-break: auto;\n+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n+  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n }\n .popover.top {\n   margin-top: -10px;\n@@ -6242,16 +6223,8 @@ button.close {\n .popover.left {\n   margin-left: -10px;\n }\n-.popover-title {\n-  padding: 8px 14px;\n-  margin: 0;\n-  font-size: 14px;\n-  background-color: #f7f7f7;\n-  border-bottom: 1px solid #ebebeb;\n-  border-radius: 5px 5px 0 0;\n-}\n-.popover-content {\n-  padding: 9px 14px;\n+.popover > .arrow {\n+  border-width: 11px;\n }\n .popover > .arrow,\n .popover > .arrow:after {\n@@ -6262,9 +6235,6 @@ button.close {\n   border-color: transparent;\n   border-style: solid;\n }\n-.popover > .arrow {\n-  border-width: 11px;\n-}\n .popover > .arrow:after {\n   content: \"\";\n   border-width: 10px;\n@@ -6273,8 +6243,8 @@ button.close {\n   bottom: -11px;\n   left: 50%;\n   margin-left: -11px;\n-  border-top-color: #999;\n-  border-top-color: rgba(0, 0, 0, .25);\n+  border-top-color: #999999;\n+  border-top-color: rgba(0, 0, 0, 0.25);\n   border-bottom-width: 0;\n }\n .popover.top > .arrow:after {\n@@ -6288,8 +6258,8 @@ button.close {\n   top: 50%;\n   left: -11px;\n   margin-top: -11px;\n-  border-right-color: #999;\n-  border-right-color: rgba(0, 0, 0, .25);\n+  border-right-color: #999999;\n+  border-right-color: rgba(0, 0, 0, 0.25);\n   border-left-width: 0;\n }\n .popover.right > .arrow:after {\n@@ -6304,8 +6274,8 @@ button.close {\n   left: 50%;\n   margin-left: -11px;\n   border-top-width: 0;\n-  border-bottom-color: #999;\n-  border-bottom-color: rgba(0, 0, 0, .25);\n+  border-bottom-color: #999999;\n+  border-bottom-color: rgba(0, 0, 0, 0.25);\n }\n .popover.bottom > .arrow:after {\n   top: 1px;\n@@ -6319,8 +6289,8 @@ button.close {\n   right: -11px;\n   margin-top: -11px;\n   border-right-width: 0;\n-  border-left-color: #999;\n-  border-left-color: rgba(0, 0, 0, .25);\n+  border-left-color: #999999;\n+  border-left-color: rgba(0, 0, 0, 0.25);\n }\n .popover.left > .arrow:after {\n   right: 1px;\n@@ -6329,6 +6299,17 @@ button.close {\n   border-right-width: 0;\n   border-left-color: #fff;\n }\n+.popover-title {\n+  padding: 8px 14px;\n+  margin: 0;\n+  font-size: 14px;\n+  background-color: #f7f7f7;\n+  border-bottom: 1px solid #ebebeb;\n+  border-radius: 5px 5px 0 0;\n+}\n+.popover-content {\n+  padding: 9px 14px;\n+}\n .carousel {\n   position: relative;\n }\n@@ -6340,43 +6321,44 @@ button.close {\n .carousel-inner > .item {\n   position: relative;\n   display: none;\n-  -webkit-transition: .6s ease-in-out left;\n-       -o-transition: .6s ease-in-out left;\n-          transition: .6s ease-in-out left;\n+  -webkit-transition: 0.6s ease-in-out left;\n+  -o-transition: 0.6s ease-in-out left;\n+  transition: 0.6s ease-in-out left;\n }\n .carousel-inner > .item > img,\n .carousel-inner > .item > a > img {\n   line-height: 1;\n }\n @media all and (transform-3d), (-webkit-transform-3d) {\n   .carousel-inner > .item {\n-    -webkit-transition: -webkit-transform .6s ease-in-out;\n-         -o-transition:      -o-transform .6s ease-in-out;\n-            transition:         transform .6s ease-in-out;\n-\n+    -webkit-transition: -webkit-transform 0.6s ease-in-out;\n+    -o-transition: -o-transform 0.6s ease-in-out;\n+    transition: -webkit-transform 0.6s ease-in-out;\n+    transition: transform 0.6s ease-in-out;\n+    transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out, -o-transform 0.6s ease-in-out;\n     -webkit-backface-visibility: hidden;\n-            backface-visibility: hidden;\n+    backface-visibility: hidden;\n     -webkit-perspective: 1000px;\n-            perspective: 1000px;\n+    perspective: 1000px;\n   }\n   .carousel-inner > .item.next,\n   .carousel-inner > .item.active.right {\n-    left: 0;\n     -webkit-transform: translate3d(100%, 0, 0);\n-            transform: translate3d(100%, 0, 0);\n+    transform: translate3d(100%, 0, 0);\n+    left: 0;\n   }\n   .carousel-inner > .item.prev,\n   .carousel-inner > .item.active.left {\n-    left: 0;\n     -webkit-transform: translate3d(-100%, 0, 0);\n-            transform: translate3d(-100%, 0, 0);\n+    transform: translate3d(-100%, 0, 0);\n+    left: 0;\n   }\n   .carousel-inner > .item.next.left,\n   .carousel-inner > .item.prev.right,\n   .carousel-inner > .item.active {\n-    left: 0;\n     -webkit-transform: translate3d(0, 0, 0);\n-            transform: translate3d(0, 0, 0);\n+    transform: translate3d(0, 0, 0);\n+    left: 0;\n   }\n }\n .carousel-inner > .active,\n@@ -6418,35 +6400,36 @@ button.close {\n   font-size: 20px;\n   color: #fff;\n   text-align: center;\n-  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);\n+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n+  background-color: rgba(0, 0, 0, 0);\n   filter: alpha(opacity=50);\n-  opacity: .5;\n+  opacity: 0.5;\n }\n .carousel-control.left {\n-  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);\n-  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);\n-  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001)));\n-  background-image:         linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);\n+  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n+  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n+  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001)));\n+  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n   filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n   background-repeat: repeat-x;\n }\n .carousel-control.right {\n   right: 0;\n   left: auto;\n-  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);\n-  background-image:      -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);\n-  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5)));\n-  background-image:         linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);\n+  background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n+  background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n+  background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5)));\n+  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n   filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n   background-repeat: repeat-x;\n }\n .carousel-control:hover,\n .carousel-control:focus {\n   color: #fff;\n   text-decoration: none;\n-  filter: alpha(opacity=90);\n   outline: 0;\n-  opacity: .9;\n+  filter: alpha(opacity=90);\n+  opacity: 0.9;\n }\n .carousel-control .icon-prev,\n .carousel-control .icon-next,\n@@ -6476,10 +6459,10 @@ button.close {\n   line-height: 1;\n }\n .carousel-control .icon-prev:before {\n-  content: '\\2039';\n+  content: \"\\2039\";\n }\n .carousel-control .icon-next:before {\n-  content: '\\203a';\n+  content: \"\\203a\";\n }\n .carousel-indicators {\n   position: absolute;\n@@ -6520,7 +6503,7 @@ button.close {\n   padding-bottom: 20px;\n   color: #fff;\n   text-align: center;\n-  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);\n+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n }\n .carousel-caption .btn {\n   text-shadow: none;\n@@ -6532,16 +6515,16 @@ button.close {\n   .carousel-control .icon-next {\n     width: 30px;\n     height: 30px;\n-    margin-top: -15px;\n+    margin-top: -10px;\n     font-size: 30px;\n   }\n   .carousel-control .glyphicon-chevron-left,\n   .carousel-control .icon-prev {\n-    margin-left: -15px;\n+    margin-left: -10px;\n   }\n   .carousel-control .glyphicon-chevron-right,\n   .carousel-control .icon-next {\n-    margin-right: -15px;\n+    margin-right: -10px;\n   }\n   .carousel-caption {\n     right: 20%;\n@@ -6580,6 +6563,8 @@ button.close {\n .pager:after,\n .panel-body:before,\n .panel-body:after,\n+.modal-header:before,\n+.modal-header:after,\n .modal-footer:before,\n .modal-footer:after {\n   display: table;\n@@ -6599,6 +6584,7 @@ button.close {\n .navbar-collapse:after,\n .pager:after,\n .panel-body:after,\n+.modal-header:after,\n .modal-footer:after {\n   clear: both;\n }\n@@ -6845,4 +6831,4 @@ button.close {\n     display: none !important;\n   }\n }\n-/*# sourceMappingURL=bootstrap.css.map */\n+/*# sourceMappingURL=bootstrap.css.map */\n\\ No newline at end of file"}, {"sha": "9f60ed2b1bd4ed75d3b2861366a82bd678404963", "filename": "data/interfaces/default/css/bootstrap3/bootstrap.css.map", "status": "removed", "additions": 0, "deletions": 1, "changes": 1, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap3%2Fbootstrap.css.map", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap3%2Fbootstrap.css.map", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap3%2Fbootstrap.css.map?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "5b96335ff6a02021199d731eaa19ccadd1dc8af8", "filename": "data/interfaces/default/css/bootstrap3/bootstrap.min.css", "status": "modified", "additions": 4, "deletions": 3, "changes": 7, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap3%2Fbootstrap.min.css", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap3%2Fbootstrap.min.css", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fcss%2Fbootstrap3%2Fbootstrap.min.css?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "81a16d3cdb4a011c2cb9658a3b6c830969f6dad2", "filename": "data/interfaces/default/css/tautulli.css", "status": "modified", "additions": 136, "deletions": 3, "changes": 139, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcss%2Ftautulli.css", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcss%2Ftautulli.css", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fcss%2Ftautulli.css?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,6 +1,14 @@\n+/* Override Bootstrap Glyphicons Font Path */\n+@font-face {\n+    font-family: \"Glyphicons Halflings\";\n+    src: url(\"../fonts/bootstrap3/glyphicons-halflings-regular.eot\");\n+    src: url(\"../fonts/bootstrap3/glyphicons-halflings-regular.eot?#iefix\") format(\"embedded-opentype\"), url(\"../fonts/bootstrap3/glyphicons-halflings-regular.woff2\") format(\"woff2\"), url(\"../fonts/bootstrap3/glyphicons-halflings-regular.woff\") format(\"woff\"), url(\"../fonts/bootstrap3/glyphicons-halflings-regular.ttf\") format(\"truetype\"), url(\"../fonts/bootstrap3/glyphicons-halflings-regular.svg#glyphicons_halflingsregular\") format(\"svg\");\n+}\n+\n body {\n     font-family: 'Open Sans', Arial, sans-serif;\n     color: #eee;\n+    background-color: #1f1f1f;\n     margin-top: 50px;\n     overflow: hidden;\n }\n@@ -559,6 +567,44 @@ fieldset[disabled] .btn-bright.active {\n     border-radius: 0 0 3px 3px;\n     border-top: 1px solid #1C1C1C;\n }\n+#donation_type {\n+    display: -webkit-flex;\n+    display: flex;\n+    -webkit-justify-content: center;\n+    justify-content: center;\n+    margin: 10px 0;\n+}\n+.donate-btn {\n+    display: -webkit-inline-flex;\n+    display: inline-flex;\n+    -webkit-align-items: center;\n+    align-items: center;\n+    border-radius: 4px;\n+    padding: 8px 12px;\n+}\n+.donate-btn-github {\n+    border: none;\n+    padding: 0 16px;\n+    font-weight: 600;\n+    height: 52px;\n+    font-size: 16px;\n+}\n+.donate-btn-github img {\n+    margin-right: 8px;\n+}\n+.donate-btn-patreon {\n+    background-color: #fff;\n+}\n+.donate-btn-stripe {\n+    background-color: #533AFD;\n+}\n+.donate-btn-paypal {\n+    background-color: #60cdff;\n+    padding: 10px 12px;\n+}\n+.donate-btn-paypal img {\n+    margin-bottom: -4px;\n+}\n textarea,\n input[type=\"text\"],\n input[type=\"password\"],\n@@ -585,6 +631,28 @@ input[type=\"color\"],\n     border-radius: 3px;\n     transition: background-color .3s;\n }\n+textarea:focus,\n+input[type=\"text\"]:focus,\n+input[type=\"password\"]:focus,\n+input[type=\"datetime\"]:focus,\n+input[type=\"datetime-local\"]:focus,\n+input[type=\"date\"]:focus,\n+input[type=\"month\"]:focus,\n+input[type=\"time\"]:focus,\n+input[type=\"week\"]:focus,\n+input[type=\"number\"]:focus,\n+input[type=\"email\"]:focus,\n+input[type=\"url\"]:focus,\n+input[type=\"search\"]:focus,\n+input[type=\"tel\"]:focus,\n+input[type=\"color\"]:focus,\n+.uneditable-input:focus {\n+    outline: 0;\n+    outline: thin dotted \\9;\n+    color: #555;\n+    background-color: #fff;\n+    transition: background-color .3s;\n+}\n textarea.form-control {\n     height: initial;\n     margin: 5px 0 5px 0;\n@@ -1798,7 +1866,7 @@ a:hover .dashboard-recent-media-cover {\n     text-align: center;\n     display: block;\n     position: absolute;\n-    top: calc(50% - 34px);\n+    top: calc(50% - 42px);\n     width: 100%;\n }\n a:hover .summary-poster-face .summary-poster-face-overlay,\n@@ -4325,6 +4393,10 @@ a:hover .overlay-refresh-image:hover {\n .stream-info tr:nth-child(even) td {\n     background-color: rgba(255,255,255,0.010);\n }\n+.stream-info td:nth-child(3),\n+.stream-info th:nth-child(3) {\n+    width: 25px;\n+}\n .number-input {\n     margin: 0 !important;\n     width: 55px !important;\n@@ -4571,12 +4643,32 @@ a.donate-with-crypto::after {\n     top: 0;\n     left: 0;\n }\n+#crypto-select {\n+    width: 280px;\n+    margin: 15px auto;\n+}\n+#crypto-qrcode {\n+    width: 258px;\n+    padding: 0;\n+    margin: 15px auto;\n+    line-height: 0;\n+    text-align: center;\n+    background-color: #eee;\n+    border: 1px solid #ccc;\n+    border-radius: 4px;\n+    display: none;\n+}\n+#crypto-address {\n+    margin: 15px auto;\n+    text-align: center;\n+    display: none;\n+}\n \n #api_qr_code {\n     width: 100%;\n     padding: 0;\n     margin: 0 0 10px;\n-    line-height: 1;\n+    line-height: 0;\n     text-align: center;\n     background-color: #eee;\n     border: 1px solid #ccc;\n@@ -4586,4 +4678,45 @@ a.donate-with-crypto::after {\n .docker-mount {\n     margin: 20px auto 0 auto;\n     position: relative;\n-}\n\\ No newline at end of file\n+}\n+\n+/* Ace Editor Styles */\n+#message-editor {\n+    width: 100%;\n+    height: 300px;\n+}\n+/* Custom CSS to reduce Ace editor gutter width */\n+#message-editor .ace_gutter-cell {\n+    padding-left: 8px;\n+    padding-right: 8px;\n+}\n+.editor-controls {\n+    padding: 3px;\n+    border-bottom: 1px solid #444; \n+}\n+.editor-container {\n+    background-color: #282828;\n+    border: 1px solid #444;\n+    border-radius: 3px;\n+}\n+.editor-control-btn {\n+    margin-right: 1px;\n+    width: 32px;\n+    height: 32px;\n+    padding: 6px;\n+}\n+.editor-control-btn:hover {\n+    background-color: #555;\n+    border-color: #777;\n+}\n+/* Responsive design for editor controls */\n+@media (max-width: 768px) {\n+    .editor-controls .text-left {\n+        text-align: left !important;\n+    }\n+    .editor-control-btn {\n+        margin-right: 6px;\n+        width: 32px;\n+        height: 32px;\n+    }\n+}"}, {"sha": "a29c821e3848382f749e1c41c7126aa18776a749", "filename": "data/interfaces/default/current_activity_instance.html", "status": "modified", "additions": 22, "deletions": 15, "changes": 37, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcurrent_activity_instance.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fcurrent_activity_instance.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fcurrent_activity_instance.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -74,6 +74,7 @@\n     parent_href = page('info', data['parent_rating_key'])\n     grandparent_href = page('info', data['grandparent_rating_key'])\n     user_href = page('user', data['user_id']) if data['user_id'] else '#'\n+    library_href = page('library', data['section_id']) if data['section_id'] else '#'\n     season = short_season(data['parent_title'])\n %>\n <div class=\"dashboard-activity-instance\" id=\"activity-instance-${sk}\" data-key=\"${sk}\" data-id=\"${data['session_id']}\"\n@@ -378,17 +379,17 @@\n                         </script>\n                     </span><br /><span class=\"progress_time_offset\" id=\"stream-view-offset-${sk}\" data-last_view_offset=\"${data['view_offset']}\" data-view_offset=\"${data['view_offset']}\" data-stream_duration=\"${data['stream_duration']}\" data-state=\"${data['state']}\">\n                         <script>\n-                            $(\"#stream-view-offset-${sk}\").html(millisecondsToMinutes(parseInt(\"${data['view_offset']}\"), false));\n+                            $(\"#stream-view-offset-${sk}\").html(millisecondsToHoursMinutes(parseInt(\"${data['view_offset']}\")));\n                         </script>\n                     </span> / <span class=\"progress_time_total\" id=\"stream-duration-${sk}\">\n                         <script>\n-                            $(\"#stream-duration-${sk}\").html(millisecondsToMinutes(parseInt(\"${data['stream_duration']}\"), false));\n+                            $(\"#stream-duration-${sk}\").html(millisecondsToHoursMinutes(parseInt(\"${data['stream_duration']}\")));\n                         </script>\n                     </span>\n                     % else:\n                     ETA: Unknown<br />0:00 / <span class=\"progress_time_total\" id=\"stream-duration-${sk}\">\n                         <script>\n-                            $(\"#stream-duration-${sk}\").html(millisecondsToMinutes(parseInt(\"${data['stream_duration']}\"), false));\n+                            $(\"#stream-duration-${sk}\").html(millisecondsToHoursMinutes(parseInt(\"${data['stream_duration']}\")));\n                         </script>\n                     </span>\n                     % endif\n@@ -463,21 +464,27 @@\n         <div class=\"dashboard-activity-metadata-subtitle-container\">\n             % if data['live']:\n             <div id=\"media-type-${sk}\" class=\"dashboard-activity-metadata-media_type-icon\" title=\"Live TV\">\n-                <i class=\"fa fa-fw fa-broadcast-tower\"></i>&nbsp;\n+                <a href=\"${library_href}\">\n+                    <i class=\"fa fa-fw fa-broadcast-tower\"></i>\n+                </a>&nbsp;\n             </div>\n             % elif data['channel_stream'] == 0:\n             <div id=\"media-type-${sk}\" class=\"dashboard-activity-metadata-media_type-icon\" title=\"${data['media_type'].capitalize()}\">\n-                % if data['media_type'] == 'movie':\n-                <i class=\"fa fa-fw fa-film\"></i>&nbsp;\n-                % elif data['media_type'] == 'episode':\n-                <i class=\"fa fa-fw fa-television\"></i>&nbsp;\n-                % elif data['media_type'] == 'track':\n-                <i class=\"fa fa-fw fa-music\"></i>&nbsp;\n-                % elif data['media_type'] == 'photo':\n-                <i class=\"fa fa-fw fa-picture-o\"></i>&nbsp;\n-                % elif data['media_type'] == 'clip':\n-                <i class=\"fa fa-fw fa-video-camera\"></i>&nbsp;\n-                % endif\n+                <a href=\"${library_href}\">\n+                    % if data['media_type'] == 'movie':\n+                    <i class=\"fa fa-fw fa-film\"></i>\n+                    % elif data['media_type'] == 'episode':\n+                    <i class=\"fa fa-fw fa-television\"></i>\n+                    % elif data['media_type'] == 'track':\n+                    <i class=\"fa fa-fw fa-music\"></i>\n+                    % elif data['media_type'] == 'photo':\n+                    <i class=\"fa fa-fw fa-picture-o\"></i>\n+                    % elif data['media_type'] == 'clip':\n+                    <i class=\"fa fa-fw fa-video-camera\"></i>\n+                    % else:\n+                    <i class=\"fa fa-fw fa-question-circle\"></i>\n+                    % endif\n+                </a>&nbsp;\n             </div>\n             % else:\n             <div id=\"media-type-${sk}\" class=\"dashboard-activity-metadata-media_type-icon\" title=\"Channel\">"}, {"sha": "fd621d0bff70275bca732d82a8196d8cfa075282", "filename": "data/interfaces/default/export_modal.html", "status": "modified", "additions": 43, "deletions": 1, "changes": 44, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fexport_modal.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fexport_modal.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fexport_modal.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -21,6 +21,8 @@\n     thumb_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[0]])\n     art_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[1]])\n     logo_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[2]])\n+    squareArt_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[3]])\n+    theme_media_types = ', '.join([export.PLURAL_MEDIA_TYPES[k] for k, v in export.MEDIA_TYPES.items() if v[4]])\n %>\n <div class=\"modal-dialog\" role=\"document\">\n     <div class=\"modal-content\">\n@@ -161,8 +163,40 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                         Select the level to export logo image files.<br>Note: Only applies to ${logo_media_types}.\n                     </p>\n                 </div>\n+                <div class=\"form-group\">\n+                    <label for=\"export_squareArt_level\">Square Art Image Export Level</label>\n+                    <div class=\"row\">\n+                        <div class=\"col-md-12\">\n+                            <select class=\"form-control\" id=\"export_squareArt_level\" name=\"export_squareArt_level\">\n+                                <option value=\"0\" selected>Level 0 - None / Custom</option>\n+                                <option value=\"1\">Level 1 - Uploaded and Selected Square Art Only</option>\n+                                <option value=\"2\">Level 2 - Selected and Locked Square Art Only</option>\n+                                <option value=\"9\">Level 9 - All Selected Square Art</option>\n+                            </select>\n+                        </div>\n+                    </div>\n+                    <p class=\"help-block\">\n+                        Select the level to export square art image files.<br>Note: Only applies to ${squareArt_media_types}.\n+                    </p>\n+                </div>\n+                <div class=\"form-group\">\n+                    <label for=\"export_theme_level\">Theme Music Export Level</label>\n+                    <div class=\"row\">\n+                        <div class=\"col-md-12\">\n+                            <select class=\"form-control\" id=\"export_theme_level\" name=\"export_theme_level\">\n+                                <option value=\"0\" selected>Level 0 - None / Custom</option>\n+                                <option value=\"1\">Level 1 - Uploaded and Selected Themes Only</option>\n+                                <option value=\"2\">Level 2 - Selected and Locked Themes Only</option>\n+                                <option value=\"9\">Level 9 - All Selected Themes</option>\n+                            </select>\n+                        </div>\n+                    </div>\n+                    <p class=\"help-block\">\n+                        Select the level to export theme music audio files.<br>Note: Only applies to ${theme_media_types}.\n+                    </p>\n+                </div>\n                 <p class=\"help-block\">\n-                    Warning: Exporting images may take a long time! Images will be saved to a folder alongside the data file.\n+                    Warning: Exporting images/audio may take a long time! Images/audio will be saved to a folder alongside the data file.\n                 </p>\n             </form>\n         </div>\n@@ -249,6 +283,8 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n             $(\"#export_thumb_level\").prop('disabled', true);\n             $(\"#export_art_level\").prop('disabled', true);\n             $(\"#export_logo_level\").prop('disabled', true);\n+            $(\"#export_squareArt_level\").prop('disabled', true);\n+            $(\"#export_theme_level\").prop('disabled', true);\n             export_custom_metadata_fields.disable();\n             export_custom_media_info_fields.disable();\n         } else {\n@@ -257,6 +293,8 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n             $(\"#export_thumb_level\").prop('disabled', false);\n             $(\"#export_art_level\").prop('disabled', false);\n             $(\"#export_logo_level\").prop('disabled', false);\n+            $(\"#export_squareArt_level\").prop('disabled', false);\n+            $(\"#export_theme_level\").prop('disabled', false);\n             export_custom_metadata_fields.enable();\n             export_custom_media_info_fields.enable();\n         }\n@@ -272,6 +310,8 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n         var thumb_level = $(\"#export_thumb_level option:selected\").val();\n         var art_level = $(\"#export_art_level option:selected\").val();\n         var logo_level = $(\"#export_logo_level option:selected\").val();\n+        var squareArt_level = $(\"#export_squareArt_level option:selected\").val();\n+        var theme_level = $(\"#export_theme_level option:selected\").val();\n         var custom_fields = [\n             $('#export_custom_metadata_fields').val(),\n             $('#export_custom_media_info_fields').val()\n@@ -291,6 +331,8 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                 thumb_level: thumb_level,\n                 art_level: art_level,\n                 logo_level: logo_level,\n+                squareArt_level: squareArt_level,\n+                theme_level: theme_level,\n                 custom_fields: custom_fields,\n                 export_type: export_type,\n                 individual_files: individual_files"}, {"sha": "5b41792198336207296388853591c3fe24d820ae", "filename": "data/interfaces/default/graphs.html", "status": "modified", "additions": 20, "deletions": 7, "changes": 27, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fgraphs.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fgraphs.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fgraphs.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -13,12 +13,14 @@\n             <span><i class=\"fa fa-bar-chart\"></i> Graphs</span>\n         </div>\n         <div class=\"button-bar\">\n+            % if _session['user_group'] == 'admin':\n             <div class=\"btn-group\" id=\"user-selection\">\n                 <label>\n                     <select name=\"graph-user\" id=\"graph-user\" multiple>\n                     </select>\n                 </label>\n             </div>\n+            % endif\n             <div class=\"btn-group\" style=\"margin-right: 2px;\" data-toggle=\"buttons\" id=\"yaxis-selection\">\n                 <label class=\"btn btn-dark btn-filter\">\n                     <input type=\"radio\" name=\"yaxis-options\" id=\"yaxis-plays\" value=\"plays\" autocomplete=\"off\"> Play Count\n@@ -245,7 +247,7 @@ <h4><i class=\"fa fa-calendar\"></i> Total <span class=\"yaxis-text\" style=\"text-tr\n <script src=\"${http_root}js/dataTables.bootstrap.pagination.js\"></script>\n \n <script>\n-    var selected_user_id = null;\n+    var selected_user_id = \"${_session['user_group']}\" == \"admin\" ? null : \"${_session['user_id']}\";\n \n     // Modal popup dialog\n     function selectHandler(selectedDate, selectedSeries) {\n@@ -301,6 +303,10 @@ <h4><i class=\"fa fa-calendar\"></i> Total <span class=\"yaxis-text\" style=\"text-tr\n             return obj;\n         }, {});\n \n+        if (!(\"Total\" in chart_visibility)) {\n+            chart_visibility[\"Total\"] = false;\n+        }\n+\n         return data_series.map(function(s) {\n             var obj = Object.assign({}, s);\n             obj.visible = (chart_visibility[s.name] !== false);\n@@ -327,7 +333,8 @@ <h4><i class=\"fa fa-calendar\"></i> Total <span class=\"yaxis-text\" style=\"text-tr\n             'Direct Play': '#E5A00D',\n             'Direct Stream': '#FFFFFF',\n             'Transcode': '#F06464',\n-            'Max. Concurrent Streams': '#96C83C'\n+            'Max. Concurrent Streams': '#96C83C',\n+            'Total': '#96C83C'\n         };\n         var series_colors = [];\n         $.each(data_series, function(index, series) {\n@@ -350,7 +357,6 @@ <h4><i class=\"fa fa-calendar\"></i> Total <span class=\"yaxis-text\" style=\"text-tr\n <script src=\"${http_root}js/graphs/plays_by_month.js${cache_param}\"></script>\n <script>\n     $(document).ready(function () {\n-\n         // Initial values for graph from local storage\n         var yaxis = getLocalStorage('graph_type', 'plays');\n         var current_day_range = getLocalStorage('graph_days', 30);\n@@ -390,15 +396,15 @@ <h4><i class=\"fa fa-calendar\"></i> Total <span class=\"yaxis-text\" style=\"text-tr\n         // Load user ids and names (for the selector)\n         $.ajax({\n             url: 'get_user_names',\n-            type: 'get',\n-            dataType: \"json\",\n+            type: 'GET',\n+            dataType: 'json',\n             success: function (data) {\n                 let select = $('#graph-user');\n                 let by_id = {};\n-                data.sort(function(a, b) {\n+                data.sort(function (a, b) {\n                     return a.friendly_name.localeCompare(b.friendly_name);\n                 });\n-                data.forEach(function(item) {\n+                data.forEach(function (item) {\n                     select.append('<option value=\"' + item.user_id + '\">' +\n                         item.friendly_name + '</option>');\n                     by_id[item.user_id] = item.friendly_name;\n@@ -755,6 +761,7 @@ <h4><i class=\"fa fa-calendar\"></i> Total <span class=\"yaxis-text\" style=\"text-tr\n                     if (this.points.length > 1) {\n                         var total = 0;\n                         $.each(this.points, function(i, point) {\n+                            if (point.series.name === 'Total') return;\n                             s += '<br/>'+point.series.name+': '+point.y;\n                             total += point.y;\n                         });\n@@ -781,6 +788,7 @@ <h4><i class=\"fa fa-calendar\"></i> Total <span class=\"yaxis-text\" style=\"text-tr\n                     if (this.points.length > 1) {\n                         var total = 0;\n                         $.each(this.points, function(i, point) {\n+                            if (point.series.name === 'Total') return;\n                             s += '<br/>'+point.series.name+': '+moment.duration(point.y, 'hours').format('D [days] H [hrs] m [mins]');\n                             total += point.y;\n                         });\n@@ -830,6 +838,11 @@ <h4><i class=\"fa fa-calendar\"></i> Total <span class=\"yaxis-text\" style=\"text-tr\n             hc_plays_by_month_options.tooltip.formatter = tooltip_format;\n             hc_plays_by_month_options.yAxis.stackLabels.formatter = stack_labels_format;\n         }\n+\n+\n+        if (current_tab === '#tabs-plays') { loadGraphsTab1(current_day_range, yaxis); }\n+        if (current_tab === '#tabs-stream') { loadGraphsTab2(current_day_range, yaxis); }\n+        if (current_tab === '#tabs-total') { loadGraphsTab3(current_month_range, yaxis); }\n     });\n </script>\n </%def>\n\\ No newline at end of file"}, {"sha": "808de456009420b8f7cb12d6e2995e8d8123215b", "filename": "data/interfaces/default/history.html", "status": "modified", "additions": 2, "deletions": 2, "changes": 4, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fhistory.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fhistory.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fhistory.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -128,6 +128,8 @@ <h4 class=\"modal-title\" id=\"myModalLabel\">Confirm Delete</h4>\n <script src=\"${http_root}js/tables/history_table.js${cache_param}\"></script>\n <script>\n     $(document).ready(function () {\n+        var selected_user_id = \"${_session['user_group']}\" == \"admin\" ? null : \"${_session['user_id']}\";\n+\n         // Load user ids and names (for the selector)\n         $.ajax({\n             url: 'get_user_names',\n@@ -224,8 +226,6 @@ <h4 class=\"modal-title\" id=\"myModalLabel\">Confirm Delete</h4>\n             });\n         }\n \n-        var selected_user_id = \"${_session['user_group']}\" == \"admin\" ? null : \"${_session['user_id']}\";\n-\n         var media_type = getLocalStorage('history_media_type', 'all');\n         $.each(media_type.split(','), function (i, item) {\n             var history_media_type = $('#history-media_type-' + item);"}, {"sha": "f2f56f067b117b34b3bc30672bbcad4dacd74357", "filename": "data/interfaces/default/home_stats.html", "status": "modified", "additions": 4, "deletions": 1, "changes": 5, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fhome_stats.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fhome_stats.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fhome_stats.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -61,6 +61,9 @@\n         if s > 30:\n             m += 1\n         h, m = divmod(m, 60)\n+        d, h = divmod(h, 24)\n+        if d > 0:\n+            return str(d).zfill(1) + ':' + str(h).zfill(2) + ':' + str(m).zfill(2)\n         return str(h).zfill(1) + ':' + str(m).zfill(2)\n %>\n \n@@ -131,7 +134,7 @@ <h4>${top_stat['stat_title']}</h4>\n                     % if stat_id.startswith('top') and top_stat.get('stat_type') == 'total_plays':\n                     <span class=\"dashboard-stats-stats-units\">plays</span>\n                     % elif stat_id.startswith('top') and top_stat.get('stat_type') == 'total_duration':\n-                    <span class=\"dashboard-stats-stats-units\">hh:mm</span>\n+                    <span class=\"dashboard-stats-stats-units\">${'dd:hh:mm' if any(cast_to_int(row['total_duration']) >= 86400 for row in top_stat['rows']) else 'hh:mm'}</span>\n                     % elif stat_id.startswith('popular'):\n                     <span class=\"dashboard-stats-stats-units\">users</span>\n                     % elif stat_id == 'last_watched':"}, {"sha": "960f32ee64596a7e63f6e7ec3be15fe380263cf4", "filename": "data/interfaces/default/images/Stripe_wordmark_-_white_small_28px.png", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/data%2Finterfaces%2Fdefault%2Fimages%2FStripe_wordmark_-_white_small_28px.png", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/data%2Finterfaces%2Fdefault%2Fimages%2FStripe_wordmark_-_white_small_28px.png", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2FStripe_wordmark_-_white_small_28px.png?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "291b7bcf265521a3d062024bc4f7df927ae60e8f", "filename": "data/interfaces/default/images/become_a_patron_button.png", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/data%2Finterfaces%2Fdefault%2Fimages%2Fbecome_a_patron_button.png", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/data%2Finterfaces%2Fdefault%2Fimages%2Fbecome_a_patron_button.png", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2Fbecome_a_patron_button.png?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "a6ab3cce95fc7de7ca2dd62ce097781b7969220b", "filename": "data/interfaces/default/images/gold-rect-paypal-34px.png", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/data%2Finterfaces%2Fdefault%2Fimages%2Fgold-rect-paypal-34px.png", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/data%2Finterfaces%2Fdefault%2Fimages%2Fgold-rect-paypal-34px.png", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2Fgold-rect-paypal-34px.png?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "cc15f9bc33d7a1dd5fa8dbcc6f3234ef04b27aad", "filename": "data/interfaces/default/images/google-play-badge.svg", "status": "modified", "additions": 55, "deletions": 113, "changes": 168, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fgoogle-play-badge.svg", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fgoogle-play-badge.svg", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2Fgoogle-play-badge.svg?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,113 +1,55 @@\n-<?xml version=\"1.0\" encoding=\"utf-8\"?>\n-<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\n-<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" [\n-\t<!ENTITY ns_extend \"http://ns.adobe.com/Extensibility/1.0/\">\n-\t<!ENTITY ns_ai \"http://ns.adobe.com/AdobeIllustrator/10.0/\">\n-\t<!ENTITY ns_graphs \"http://ns.adobe.com/Graphs/1.0/\">\n-\t<!ENTITY ns_vars \"http://ns.adobe.com/Variables/1.0/\">\n-\t<!ENTITY ns_imrep \"http://ns.adobe.com/ImageReplacement/1.0/\">\n-\t<!ENTITY ns_sfw \"http://ns.adobe.com/SaveForWeb/1.0/\">\n-\t<!ENTITY ns_custom \"http://ns.adobe.com/GenericCustomNamespace/1.0/\">\n-\t<!ENTITY ns_adobe_xpath \"http://ns.adobe.com/XPath/1.0/\">\n-]>\n-<svg version=\"1.1\" id=\"Livello_1\" xmlns:x=\"&ns_extend;\" xmlns:i=\"&ns_ai;\" xmlns:graph=\"&ns_graphs;\"\n-\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"0 0 5435.8 1604\"\n-\t style=\"enable-background:new 0 0 5435.8 1604;\" xml:space=\"preserve\">\n-<style type=\"text/css\">\n-\t.st0{fill-rule:evenodd;clip-rule:evenodd;}\n-\t.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#A6A6A6;}\n-\t.st2{fill:#FFFFFF;}\n-\t.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}\n-\t.st4{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_1_);}\n-\t.st5{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_2_);}\n-\t.st6{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_3_);}\n-\t.st7{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_4_);}\n-\t.st8{opacity:0.2;fill-rule:evenodd;clip-rule:evenodd;enable-background:new    ;}\n-\t.st9{opacity:0.12;fill-rule:evenodd;clip-rule:evenodd;enable-background:new    ;}\n-\t.st10{opacity:0.25;fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;enable-background:new    ;}\n-</style>\n-<metadata>\n-\t<sfw  xmlns=\"&ns_sfw;\">\n-\t\t<slices></slices>\n-\t\t<sliceSourceBounds  bottomLeftOrigin=\"true\" height=\"1604\" width=\"5435.8\" x=\"-2656.9\" y=\"-784\"></sliceSourceBounds>\n-\t</sfw>\n-</metadata>\n-<path class=\"st0\" d=\"M5234.4,1604h-5033C90.4,1604,0,1513.6,0,1403.5v-1203C0,90,90.4,0,201.4,0h5033c110.9,0,201.4,90,201.4,200.5\n-\tv1203C5435.8,1513.6,5345.3,1604,5234.4,1604z\"/>\n-<path class=\"st1\" d=\"M5234.4,32.1c93.1,0,169.3,75.7,169.3,168.4v1203c0,92.7-75.7,168.4-169.3,168.4h-5033\n-\tc-93.1,0-169.3-75.7-169.3-168.4v-1203c0-92.7,75.7-168.4,169.3-168.4C201.4,32.1,5234.4,32.1,5234.4,32.1z M5234.4,0h-5033\n-\tC90.4,0,0,90.4,0,200.5v1203C0,1514,90.4,1604,201.4,1604h5033c110.9,0,201.4-90,201.4-200.5v-1203C5435.8,90.4,5345.3,0,5234.4,0z\"\n-\t/>\n-<path class=\"st2\" d=\"M2863.6,530.6c-36.3,0-66.9-12.7-91.1-37.7c-24-24.3-37.4-57.8-36.8-92c0-36.5,12.4-67.4,36.8-91.9\n-\tc24.1-25,54.7-37.7,91-37.7c35.9,0,66.5,12.7,91.1,37.7c24.4,25.3,36.8,56.2,36.8,91.9c-0.4,36.6-12.8,67.5-36.8,91.9\n-\tC2930.5,518,2899.9,530.6,2863.6,530.6z M1783.5,530.6c-35.5,0-66.3-12.5-91.5-37.2c-25-24.6-37.7-55.7-37.7-92.4\n-\ts12.7-67.8,37.7-92.4c24.7-24.7,55.5-37.2,91.5-37.2c17.6,0,34.7,3.5,51.1,10.6c16.1,6.9,29.2,16.3,38.9,27.8l2.4,2.9l-27.1,26.6\n-\tl-2.8-3.3c-15.3-18.2-35.8-27.1-62.9-27.1c-24.2,0-45.3,8.6-62.7,25.6c-17.5,17.1-26.4,39.5-26.4,66.6s8.9,49.5,26.4,66.6\n-\tc17.4,17,38.5,25.6,62.7,25.6c25.8,0,47.5-8.6,64.4-25.6c10-10,16.2-24,18.4-41.7h-86.9v-37.4h124.2l0.5,3.4\n-\tc0.9,6.3,1.8,12.8,1.8,18.8c0,34.5-10.4,62.4-31,83C1851.1,518.2,1820.5,530.6,1783.5,530.6z M3219.6,525.3h-38.3L3064,337.6l1,33.8\n-\tv153.8h-38.3V276.7h43.7l1.2,1.9l110.3,176.8l-1-33.7V276.7h38.7V525.3z M2575.8,525.3H2537V314.1h-67.3v-37.4H2643v37.4h-67.3\n-\tV525.3z M2438.1,525.3h-38.7V276.7h38.7V525.3z M2220.6,525.3h-38.7V314.1h-67.3v-37.4h173.3v37.4h-67.3V525.3z M2090.1,524.9\n-\th-148.4V276.7h148.4v37.4h-109.6v68.2h98.9v37h-98.9v68.2h109.6V524.9z M2800.9,467.2c17.3,17.3,38.3,26,62.7,26\n-\tc25.1,0,45.6-8.5,62.7-26c17-17,25.6-39.3,25.6-66.2s-8.6-49.3-25.5-66.2c-17.3-17.3-38.4-26-62.7-26c-25.1,0-45.6,8.5-62.6,26\n-\tc-17,17-25.6,39.3-25.6,66.2S2784,450.3,2800.9,467.2L2800.9,467.2z\"/>\n-<path class=\"st3\" d=\"M2732.1,872.4c-94.5,0-171.1,71.7-171.1,170.6c0,98,77.1,170.6,171.1,170.6c94.5,0,171.1-72.2,171.1-170.6\n-\tC2903.2,944.1,2826.6,872.4,2732.1,872.4z M2732.1,1146c-51.7,0-96.2-42.8-96.2-103.4c0-61.5,44.6-103.4,96.2-103.4\n-\tc51.7,0,96.2,41.9,96.2,103.4C2828.4,1103.6,2783.8,1146,2732.1,1146z M2358.8,872.4c-94.5,0-171.1,71.7-171.1,170.6\n-\tc0,98,77.1,170.6,171.1,170.6c94.5,0,171.1-72.2,171.1-170.6C2529.9,944.1,2453.2,872.4,2358.8,872.4z M2358.8,1146\n-\tc-51.7,0-96.2-42.8-96.2-103.4c0-61.5,44.6-103.4,96.2-103.4c51.7,0,96.2,41.9,96.2,103.4C2455,1103.6,2410.5,1146,2358.8,1146z\n-\t M1914.6,924.5v72.2h173.3c-5.3,40.5-18.7,70.4-39.2,90.9c-25.4,25.4-64.6,53-133.7,53c-106.5,0-189.8-86-189.8-192.5\n-\ts83.3-192.5,189.8-192.5c57.5,0,99.4,22.7,130.5,51.7l51.2-51.2c-43.2-41.4-100.7-73.1-181.3-73.1c-146.1,0-268.7,119-268.7,264.7\n-\tc0,146.1,122.5,264.7,268.7,264.7c78.9,0,138.1-25.8,184.9-74.4c47.7-47.7,62.8-115,62.8-169.3c0-16.9-1.3-32.1-4-45h-244.6\n-\tC1914.6,923.6,1914.6,924.5,1914.6,924.5z M3731.5,980.7c-14.3-38.3-57.5-108.7-146.1-108.7c-87.8,0-160.8,69.1-160.8,170.6\n-\tc0,95.8,72.2,170.6,169.3,170.6c78,0,123.4-47.7,142.1-75.7l-57.9-38.8c-19.2,28.5-45.9,47.2-83.8,47.2c-38.3,0-65.1-17.4-82.9-51.7\n-\tl228.1-94.5C3739.5,999.8,3731.5,980.7,3731.5,980.7z M3498.9,1037.7c-1.8-65.9,51.2-99.4,89.1-99.4c29.9,0,54.8,14.7,63.3,36.1\n-\tL3498.9,1037.7z M3313.6,1203h74.9V701.8h-74.9V1203z M3190.6,910.3h-2.7c-16.9-20.1-49-38.3-90-38.3\n-\tc-85.1,0-163.5,74.9-163.5,171.1c0,95.8,78,169.8,163.5,169.8c40.5,0,73.1-18.3,90-38.8h2.7v24.5c0,65.1-34.8,100.2-90.9,100.2\n-\tc-45.9,0-74.4-33-86-60.6l-65.1,27.2c18.7,45,68.6,100.7,151,100.7c87.8,0,162.2-51.7,162.2-177.8V882.2h-70.8v28.1\n-\tC3191.1,910.3,3190.6,910.3,3190.6,910.3z M3104.6,1146c-51.7,0-94.9-43.2-94.9-102.9c0-60.2,43.2-103.8,94.9-103.8\n-\tc51.2,0,90.9,44.1,90.9,103.8C3196,1102.8,3155.9,1146,3104.6,1146z M4082.2,701.8h-179.1V1203h74.9v-189.8h104.3\n-\tc82.9,0,164.4-60.1,164.4-155.5S4165.5,701.8,4082.2,701.8z M4084.4,943.2h-106.5v-172h106.5c56.1,0,87.8,46.3,87.8,86\n-\tC4172.2,896.5,4140.1,943.2,4084.4,943.2z M4546.9,871.5c-54.4,0-110.5,24.1-133.7,76.6l66.4,27.6c14.3-27.6,40.5-37,68.2-37\n-\tc38.8,0,78,23.2,78.9,64.6v5.3c-13.4-7.6-42.8-19.2-78-19.2c-71.7,0-144.4,39.2-144.4,112.7c0,67.3,58.8,110.5,124.3,110.5\n-\tc50.3,0,78-22.7,95.3-49h2.7v38.8h72.2v-192C4698.8,921,4632.4,871.5,4546.9,871.5z M4537.5,1146c-24.5,0-58.8-12-58.8-42.8\n-\tc0-38.8,42.8-53.5,79.3-53.5c33,0,48.6,7.1,68.2,16.9C4620.8,1111.6,4582.8,1145.6,4537.5,1146z M4962.2,882.2l-86,217.4h-2.7\n-\tl-89.1-217.4h-80.6l133.7,303.9l-76.2,168.9h78L5045,882.2C5045,882.2,4962.2,882.2,4962.2,882.2z M4288,1203h74.9V701.8H4288V1203z\n-\t\"/>\n-<linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"1682.1077\" y1=\"1339.4783\" x2=\"1624.2924\" y2=\"1309.6345\" gradientTransform=\"matrix(11.64 0 0 -22.55 -18705.5957 30554.3691)\">\n-\t<stop  offset=\"0\" style=\"stop-color:#00A0FF\"/>\n-\t<stop  offset=\"6.569999e-03\" style=\"stop-color:#00A1FF\"/>\n-\t<stop  offset=\"0.2601\" style=\"stop-color:#00BEFF\"/>\n-\t<stop  offset=\"0.5122\" style=\"stop-color:#00D2FF\"/>\n-\t<stop  offset=\"0.7604\" style=\"stop-color:#00DFFF\"/>\n-\t<stop  offset=\"1\" style=\"stop-color:#00E3FF\"/>\n-</linearGradient>\n-<path class=\"st4\" d=\"M418.4,302.1c-11.6,12.5-18.3,31.6-18.3,56.6v886.7c0,25,6.7,44.1,18.7,56.1l3.1,2.7l496.8-496.8v-11.1\n-\tL421.5,299.4C421.5,299.4,418.4,302.1,418.4,302.1z\"/>\n-<linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"1712.6624\" y1=\"1274.8376\" x2=\"1606.5613\" y2=\"1274.8376\" gradientTransform=\"matrix(9.145 0 0 -7.7 -14305.5381 10618.251)\">\n-\t<stop  offset=\"0\" style=\"stop-color:#FFE000\"/>\n-\t<stop  offset=\"0.4087\" style=\"stop-color:#FFBD00\"/>\n-\t<stop  offset=\"0.7754\" style=\"stop-color:#FFA500\"/>\n-\t<stop  offset=\"1\" style=\"stop-color:#FF9C00\"/>\n-</linearGradient>\n-<path class=\"st5\" d=\"M1084,973.5L918.3,807.8v-11.6L1084,630.5l3.6,2.2l196,111.4c56.1,31.6,56.1,83.8,0,115.8l-196,111.4\n-\tC1087.6,971.3,1084,973.5,1084,973.5z\"/>\n-<linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"1707.4414\" y1=\"1290.0475\" x2=\"1646.682\" y2=\"1211.2225\" gradientTransform=\"matrix(15.02 0 0 -11.5775 -24650.2285 15829.6484)\">\n-\t<stop  offset=\"0\" style=\"stop-color:#FF3A44\"/>\n-\t<stop  offset=\"1\" style=\"stop-color:#C31162\"/>\n-</linearGradient>\n-<path class=\"st6\" d=\"M1087.6,971.3L918.3,802l-499.9,499.9c18.3,19.6,49,21.8,83.3,2.7L1087.6,971.3\"/>\n-<linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"1660.6357\" y1=\"1365.6676\" x2=\"1687.767\" y2=\"1330.4501\" gradientTransform=\"matrix(15.02 0 0 -11.5715 -24650.2285 15809.9922)\">\n-\t<stop  offset=\"0\" style=\"stop-color:#32A071\"/>\n-\t<stop  offset=\"6.850000e-02\" style=\"stop-color:#2DA771\"/>\n-\t<stop  offset=\"0.4762\" style=\"stop-color:#15CF74\"/>\n-\t<stop  offset=\"0.8009\" style=\"stop-color:#06E775\"/>\n-\t<stop  offset=\"1\" style=\"stop-color:#00F076\"/>\n-</linearGradient>\n-<path class=\"st7\" d=\"M1087.6,632.7L501.7,299.9c-34.3-19.6-65.1-16.9-83.3,2.7L918.3,802L1087.6,632.7z\"/>\n-<path class=\"st8\" d=\"M1084,967.7l-581.9,330.6c-32.5,18.7-61.5,17.4-80.2,0.4l-3.1,3.1l3.1,2.7c18.7,16.9,47.7,18.3,80.2-0.4\n-\tL1088,971.3C1088,971.3,1084,967.7,1084,967.7z\"/>\n-<path class=\"st9\" d=\"M1283.6,854.1l-200.1,113.6l3.6,3.6l196-111.4c28.1-16,41.9-37,41.9-57.9C1323.3,821.2,1309,839.4,1283.6,854.1\n-\tz\"/>\n-<path class=\"st10\" d=\"M501.7,305.7l781.9,444.2c25.4,14.3,39.7,33,41.9,52.1c0-20.9-13.8-41.9-41.9-57.9L501.7,299.9\n-\tc-56.1-32.1-101.6-5.3-101.6,58.8v5.8C400.1,300.3,445.6,274,501.7,305.7z\"/>\n-</svg>\n+<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n+<svg id=\"artwork\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 238.96 70.87\">\n+  <!-- Generator: Adobe Illustrator 29.8.3, SVG Export Plug-In . SVG Version: 2.1.1 Build 3)  -->\n+  <defs>\n+    <style>\n+      .st0 {\n+        fill: #4285f4;\n+      }\n+\n+      .st1 {\n+        fill: #a6a6a6;\n+      }\n+\n+      .st2 {\n+        stroke: #fff;\n+        stroke-miterlimit: 10;\n+        stroke-width: .2px;\n+      }\n+\n+      .st2, .st3 {\n+        fill: #fff;\n+      }\n+\n+      .st4 {\n+        fill: #34a853;\n+      }\n+\n+      .st5 {\n+        fill: #fbbc04;\n+      }\n+\n+      .st6 {\n+        fill: #ea4335;\n+      }\n+    </style>\n+  </defs>\n+  <rect x=\"-.11\" width=\"239.17\" height=\"70.87\" rx=\"8.86\" ry=\"8.86\"/>\n+  <path class=\"st1\" d=\"M230.21,1.42c4.1,0,7.44,3.34,7.44,7.44v53.15c0,4.1-3.34,7.44-7.44,7.44H8.75c-4.1,0-7.44-3.34-7.44-7.44V8.86C1.31,4.76,4.65,1.42,8.75,1.42h221.46M230.21,0H8.75C3.88,0-.11,3.99-.11,8.86v53.15c0,4.87,3.99,8.86,8.86,8.86h221.46c4.87,0,8.86-3.99,8.86-8.86V8.86c0-4.87-3.99-8.86-8.86-8.86h0Z\"/>\n+  <g>\n+    <path class=\"st2\" d=\"M83.9,18.15c0,1.48-.44,2.67-1.32,3.55-1,1.05-2.3,1.57-3.9,1.57s-2.84-.53-3.91-1.59c-1.07-1.06-1.61-2.38-1.61-3.96s.54-2.89,1.61-3.96c1.07-1.06,2.38-1.6,3.91-1.6.76,0,1.49.15,2.18.44.69.3,1.25.69,1.66,1.19l-.93.94c-.7-.84-1.67-1.26-2.91-1.26-1.12,0-2.09.39-2.9,1.18s-1.22,1.81-1.22,3.06.41,2.28,1.22,3.06c.82.79,1.78,1.18,2.9,1.18,1.19,0,2.18-.4,2.97-1.19.51-.52.81-1.23.89-2.15h-3.86v-1.28h5.15c.05.28.07.54.07.8Z\"/>\n+    <path class=\"st2\" d=\"M92.07,13.71h-4.84v3.37h4.37v1.28h-4.37v3.37h4.84v1.31h-6.21v-10.63h6.21v1.31h0Z\"/>\n+    <path class=\"st2\" d=\"M97.83,23.03h-1.37v-9.32h-2.97v-1.31h7.3v1.31h-2.97s0,9.32,0,9.32Z\"/>\n+    <path class=\"st2\" d=\"M106.08,23.03v-10.63h1.36v10.63h-1.36Z\"/>\n+    <path class=\"st2\" d=\"M113.51,23.03h-1.37v-9.32h-2.97v-1.31h7.3v1.31h-2.97s0,9.32,0,9.32Z\"/>\n+    <path class=\"st2\" d=\"M130.3,21.66c-1.05,1.08-2.34,1.61-3.9,1.61s-2.85-.54-3.9-1.61c-1.05-1.07-1.57-2.39-1.57-3.94s.52-2.87,1.57-3.94c1.04-1.08,2.34-1.61,3.9-1.61s2.84.54,3.89,1.62c1.05,1.08,1.57,2.39,1.57,3.93s-.52,2.87-1.57,3.94ZM123.52,20.77c.79.8,1.75,1.19,2.89,1.19s2.1-.4,2.89-1.19c.79-.8,1.18-1.81,1.18-3.05s-.4-2.26-1.18-3.05c-.78-.8-1.75-1.19-2.89-1.19s-2.1.4-2.89,1.19c-.78.8-1.18,1.81-1.18,3.05s.4,2.26,1.18,3.05Z\"/>\n+    <path class=\"st2\" d=\"M133.79,23.03v-10.63h1.66l5.17,8.27h.06l-.06-2.05v-6.22h1.37v10.63h-1.43l-5.41-8.67h-.06l.06,2.05v6.62h-1.37,0Z\"/>\n+  </g>\n+  <path class=\"st3\" d=\"M120.61,38.54c-4.17,0-7.56,3.17-7.56,7.53s3.4,7.53,7.56,7.53,7.56-3.2,7.56-7.53-3.4-7.53-7.56-7.53ZM120.61,50.64c-2.28,0-4.25-1.88-4.25-4.57s1.97-4.57,4.25-4.57,4.25,1.85,4.25,4.57-1.97,4.57-4.25,4.57ZM104.11,38.54c-4.17,0-7.56,3.17-7.56,7.53s3.4,7.53,7.56,7.53,7.56-3.2,7.56-7.53-3.4-7.53-7.56-7.53ZM104.11,50.64c-2.28,0-4.25-1.88-4.25-4.57s1.97-4.57,4.25-4.57,4.25,1.85,4.25,4.57-1.97,4.57-4.25,4.57ZM84.48,40.85v3.2h7.65c-.23,1.8-.83,3.11-1.74,4.03-1.11,1.11-2.85,2.34-5.91,2.34-4.71,0-8.39-3.8-8.39-8.51s3.68-8.51,8.39-8.51c2.54,0,4.4,1,5.76,2.28l2.26-2.26c-1.91-1.83-4.45-3.22-8.02-3.22-6.45,0-11.87,5.25-11.87,11.7s5.42,11.7,11.87,11.7c3.48,0,6.11-1.14,8.16-3.28,2.11-2.11,2.77-5.08,2.77-7.48,0-.74-.06-1.43-.17-2h-10.76,0ZM164.75,43.33c-.63-1.68-2.54-4.8-6.45-4.8s-7.11,3.05-7.11,7.53c0,4.22,3.2,7.53,7.48,7.53,3.45,0,5.45-2.11,6.28-3.34l-2.57-1.71c-.86,1.26-2.03,2.08-3.71,2.08s-2.88-.77-3.65-2.28l10.08-4.17-.34-.86h0ZM154.47,45.84c-.09-2.91,2.26-4.4,3.94-4.4,1.31,0,2.43.66,2.8,1.6l-6.74,2.8ZM146.28,53.15h3.31v-22.15h-3.31v22.15ZM140.86,40.22h-.11c-.74-.88-2.17-1.68-3.97-1.68-3.77,0-7.22,3.31-7.22,7.56s3.45,7.51,7.22,7.51c1.8,0,3.22-.8,3.97-1.71h.11v1.08c0,2.88-1.54,4.42-4.03,4.42-2.03,0-3.28-1.46-3.8-2.68l-2.88,1.2c.83,2,3.03,4.45,6.68,4.45,3.88,0,7.16-2.28,7.16-7.85v-13.53h-3.14v1.23ZM137.06,50.64c-2.28,0-4.2-1.91-4.2-4.54s1.91-4.6,4.2-4.6,4.03,1.94,4.03,4.6-1.77,4.54-4.03,4.54ZM180.26,31h-7.92v22.15h3.31v-8.39h4.62c3.66,0,7.27-2.65,7.27-6.88s-3.6-6.88-7.27-6.88ZM180.34,41.68h-4.7v-7.59h4.7c2.47,0,3.87,2.05,3.87,3.8s-1.4,3.8-3.87,3.8h0ZM200.77,38.5c-2.39,0-4.87,1.05-5.9,3.39l2.94,1.23c.63-1.23,1.79-1.62,3.02-1.62,1.71,0,3.45,1.03,3.48,2.85v.23c-.6-.34-1.88-.86-3.45-.86-3.16,0-6.38,1.74-6.38,4.99,0,2.96,2.59,4.87,5.5,4.87,2.22,0,3.45-1,4.22-2.16h.11v1.71h3.19v-8.49c0-3.93-2.94-6.13-6.73-6.13h0ZM200.38,50.63c-1.08,0-2.59-.54-2.59-1.88,0-1.71,1.88-2.37,3.5-2.37,1.45,0,2.14.31,3.02.74-.26,2.05-2.02,3.5-3.93,3.5ZM219.12,38.98l-3.79,9.6h-.11l-3.93-9.6h-3.56l5.9,13.42-3.36,7.47h3.45l9.09-20.89h-3.68ZM189.35,53.15h3.31v-22.15h-3.31v22.15Z\"/>\n+  <g>\n+    <path class=\"st6\" d=\"M36.6,34.41l-18.86,20.02s0,0,0,.01c.58,2.17,2.56,3.77,4.92,3.77.94,0,1.83-.26,2.58-.7l.06-.04,21.23-12.25-9.94-10.82Z\"/>\n+    <path class=\"st5\" d=\"M55.68,31h-.02s-9.17-5.33-9.17-5.33l-10.33,9.19,10.36,10.36,9.12-5.26c1.6-.86,2.68-2.55,2.68-4.49s-1.07-3.61-2.65-4.47h0Z\"/>\n+    <path class=\"st0\" d=\"M17.73,16.44c-.11.42-.17.86-.17,1.31v35.38c0,.45.06.89.17,1.31l19.51-19.51-19.51-18.49Z\"/>\n+    <path class=\"st4\" d=\"M36.74,35.43l9.76-9.76-21.21-12.3c-.77-.46-1.67-.73-2.63-.73-2.36,0-4.34,1.6-4.92,3.78h0s19,19,19,19h0Z\"/>\n+  </g>\n+</svg>\n\\ No newline at end of file"}, {"sha": "e75b99858c528ae1d2feb5815561b5254ed6acc4", "filename": "data/interfaces/default/images/octicons-heart-16.svg", "status": "added", "additions": 1, "deletions": 0, "changes": 1, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Focticons-heart-16.svg", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Focticons-heart-16.svg", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2Focticons-heart-16.svg?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1 @@\n+<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill=\"#bf3989\" d=\"m8 14.25.345.666a.75.75 0 0 1-.69 0l-.008-.004-.018-.01a7.152 7.152 0 0 1-.31-.17 22.055 22.055 0 0 1-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.066 22.066 0 0 1-3.744 2.584l-.018.01-.006.003h-.002ZM4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.58 20.58 0 0 0 8 13.393a20.58 20.58 0 0 0 3.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.749.749 0 0 1-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5Z\"></path></svg>\n\\ No newline at end of file"}, {"sha": "382f716437ddfc30c399940dcda9503e8e617dc8", "filename": "data/interfaces/default/images/patreon-lockup-horizontal.svg", "status": "added", "additions": 5, "deletions": 0, "changes": 5, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fpatreon-lockup-horizontal.svg", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fpatreon-lockup-horizontal.svg", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2Fpatreon-lockup-horizontal.svg?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,5 @@\n+<svg width=\"1937\" height=\"390\" viewBox=\"0 0 1937 390\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n+<rect width=\"1936.55\" height=\"389.623\"/>\n+<path d=\"M355.75 117.05C355.681 67.3013 316.935 26.5278 271.476 11.8159C215.023 -6.45309 140.569 -3.80509 86.6641 21.6287C21.3299 52.4595 0.806127 119.994 0.0413094 187.349C-0.586418 242.726 4.94047 388.579 87.2088 389.618C148.336 390.393 157.439 311.628 185.722 273.694C205.846 246.705 231.756 239.082 263.651 231.189C318.469 217.62 355.829 174.358 355.75 117.05Z\" fill=\"black\"/>\n+<path d=\"M514.75 98.6117C514.75 88.6117 522.15 81.8117 533.75 81.8117H589.25C639.55 81.8117 673.75 109.512 673.75 150.212C673.75 190.212 639.85 219.912 589.25 219.912H581.55C562.15 219.912 552.15 229.912 552.15 246.012V291.612C552.15 304.512 544.75 312.612 533.45 312.612C522.15 312.612 514.75 304.512 514.75 291.612V98.6117ZM552.15 156.412C552.15 176.712 562.45 186.412 582.15 186.412H586.95C614.65 186.412 635.05 175.112 635.05 150.312C635.05 125.512 614.75 114.212 586.95 114.212H582.25C562.55 114.212 552.25 123.912 552.25 144.212V156.412H552.15ZM673.85 294.812C673.85 305.412 681.25 312.512 692.55 312.512C699.95 312.512 706.75 307.712 710.65 297.712L717.45 279.612C722.95 265.112 732.65 258.312 743.25 258.312H804.55C815.15 258.312 824.85 265.112 830.35 279.612L837.15 297.712C841.05 307.712 847.75 312.512 855.25 312.512C866.55 312.512 873.95 305.412 873.95 294.812C873.95 291.912 873.35 288.312 872.05 284.812L798.85 94.4117C794.35 82.8117 784.05 77.0117 774.05 77.0117C764.05 77.0117 753.75 82.8117 749.25 94.4117L676.05 284.812C674.45 288.412 673.85 291.912 673.85 294.812ZM745.15 208.312C745.15 204.812 746.15 201.512 747.75 196.712L761.65 158.312C764.25 150.912 768.75 147.312 773.95 147.312C779.15 147.312 783.65 150.812 786.25 158.312L800.15 196.712C801.75 201.512 802.75 204.812 802.75 208.312C802.75 218.012 797.25 224.812 782.75 224.812H765.35C750.65 224.812 745.15 218.012 745.15 208.312ZM861.65 99.3117C861.65 89.0117 869.05 81.9117 881.05 81.9117H1029.45C1041.35 81.9117 1048.85 89.0117 1048.85 99.3117C1048.85 109.612 1041.45 116.712 1029.45 116.712H1004.65C984.95 116.712 974.35 126.712 974.35 149.612V291.312C974.35 304.512 966.95 312.612 955.35 312.612C943.75 312.612 936.35 304.512 936.35 291.312V149.612C936.35 126.712 925.75 116.712 906.05 116.712H881.25C869.05 116.712 861.65 109.612 861.65 99.3117ZM1083.65 291.612C1083.65 304.512 1091.05 312.612 1102.35 312.612C1113.65 312.612 1121.05 304.512 1121.05 291.612V240.312C1121.05 225.812 1129.45 219.612 1139.75 219.612H1142.35C1149.15 219.612 1155.95 223.812 1160.05 229.912L1209.05 301.912C1213.55 308.712 1219.35 312.512 1226.75 312.512C1236.45 312.512 1244.15 304.412 1244.15 294.812C1244.15 290.912 1242.85 286.712 1239.95 282.512L1207.35 236.712C1203.45 231.212 1201.85 226.712 1201.85 222.812C1201.85 214.712 1208.95 208.912 1217.35 202.812C1232.55 191.512 1248.95 176.712 1248.95 148.312C1248.95 108.612 1217.95 81.8117 1166.95 81.8117H1102.05C1090.45 81.8117 1083.35 88.6117 1083.35 98.6117V291.612H1083.65ZM1121.05 153.512V143.812C1121.05 122.812 1132.05 114.112 1148.75 114.112H1164.85C1192.55 114.112 1210.35 124.412 1210.35 148.312C1210.35 172.212 1191.65 183.112 1163.85 183.112H1148.65C1132.05 183.212 1121.05 174.512 1121.05 153.512ZM1296.95 290.912V98.6117C1296.95 88.6117 1304.05 81.8117 1315.65 81.8117H1437.65C1449.25 81.8117 1456.35 88.6117 1456.35 98.6117C1456.35 108.612 1449.25 115.412 1437.65 115.412H1360.55C1345.35 115.412 1334.45 124.412 1334.45 141.512V148.612C1334.45 165.712 1345.45 174.712 1360.55 174.712H1420.25C1431.85 174.712 1438.95 181.512 1438.95 191.512C1438.95 201.512 1431.85 208.312 1420.25 208.312H1362.85C1347.65 208.312 1334.45 217.712 1334.45 236.712V245.712C1334.45 264.712 1347.65 274.112 1362.85 274.112H1437.75C1449.35 274.112 1456.45 280.912 1456.45 290.912C1456.45 300.912 1449.35 307.712 1437.75 307.712H1315.75C1304.05 307.712 1296.95 300.912 1296.95 290.912ZM1478.55 194.812C1478.55 125.112 1530.85 77.0117 1592.15 77.0117C1653.45 77.0117 1705.75 125.112 1705.75 194.812C1705.75 264.512 1653.45 312.612 1592.15 312.612C1530.85 312.612 1478.55 264.512 1478.55 194.812ZM1521.15 194.812C1521.15 243.812 1550.15 275.112 1592.15 275.112C1634.05 275.112 1663.15 243.812 1663.15 194.812C1663.15 145.412 1634.15 114.512 1592.15 114.512C1550.25 114.412 1521.15 145.412 1521.15 194.812ZM1750.55 291.612C1750.55 304.512 1757.95 312.612 1769.25 312.612C1780.55 312.612 1787.95 304.512 1787.95 291.612V192.912C1787.95 181.012 1795.05 175.212 1802.45 175.212C1808.25 175.212 1813.05 178.412 1816.65 184.212L1878.55 287.812C1886.95 302.012 1894.65 312.612 1910.45 312.612C1925.65 312.612 1936.55 301.612 1936.55 283.912V98.0117C1936.55 85.1117 1929.15 77.0117 1917.85 77.0117C1906.55 77.0117 1899.15 85.1117 1899.15 98.0117V196.712C1899.15 208.612 1892.05 214.412 1884.65 214.412C1878.85 214.412 1873.95 211.212 1870.45 205.412L1808.55 101.812C1800.15 87.6117 1792.45 77.0117 1776.65 77.0117C1761.45 77.0117 1750.55 88.0117 1750.55 105.712V291.612Z\" fill=\"black\"/>\n+</svg>"}, {"sha": "9e1a3321687a11bfb39dbddbf55e60973f26728e", "filename": "data/interfaces/default/images/paypal-logo-black.svg", "status": "added", "additions": 1, "deletions": 0, "changes": 1, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fpaypal-logo-black.svg", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fpaypal-logo-black.svg", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2Fpaypal-logo-black.svg?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1 @@\n+<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"2 2.02 131 43.96\"><path fill=\"#000\" d=\"M133 2.02v32.83h-7.47V2.02H133zM122.78 13.1v21.81h-6.64v-1.88c-.84.82-1.8 1.44-2.86 1.88-1.07.46-2.23.7-3.47.7-1.56 0-3.01-.29-4.34-.87-1.33-.61-2.49-1.44-3.47-2.48-.99-1.05-1.77-2.27-2.35-3.66-.55-1.42-.82-2.95-.82-4.58s.27-3.14.82-4.53c.58-1.42 1.36-2.66 2.35-3.71a10.45 10.45 0 0 1 3.47-2.44c1.33-.61 2.78-.92 4.34-.92 1.24 0 2.4.23 3.47.7 1.07.44 2.03 1.06 2.86 1.88v-1.88h6.64v-.02zm-11.77 15.87c1.36 0 2.47-.46 3.34-1.4.9-.93 1.35-2.12 1.35-3.57 0-1.45-.45-2.65-1.35-3.57-.87-.93-1.99-1.4-3.34-1.4-1.35 0-2.49.46-3.39 1.4-.87.93-1.3 2.12-1.3 3.57 0 1.45.44 2.65 1.3 3.57.9.93 2.03 1.4 3.39 1.4zM88.17 2.02c1.97 0 3.65.28 5.04.83 1.39.55 2.56 1.32 3.52 2.31.98 1.02 1.75 2.18 2.3 3.49.55 1.31.82 2.72.82 4.23 0 1.51-.27 2.92-.82 4.23a11.15 11.15 0 0 1-2.3 3.49c-.95.99-2.13 1.76-3.52 2.31-1.39.55-3.07.83-5.04.83h-3.6V34.9h-7.6V2.02h11.2zm-1.09 14.96c1.02 0 1.79-.1 2.35-.31.58-.23 1.06-.53 1.43-.87.78-.73 1.17-1.7 1.17-2.92s-.39-2.19-1.17-2.92c-.38-.35-.85-.63-1.43-.83-.55-.23-1.33-.35-2.35-.35h-2.52v8.2h2.52zM48.97 13.1h8.25l5.6 10.46h.09L67.9 13.1h7.64L59.17 45.98h-7.6l7.47-15.04L48.97 13.1zm-1.47 0v21.81h-6.64v-1.88c-.84.82-1.8 1.44-2.86 1.88-1.07.46-2.23.7-3.47.7-1.56 0-3.01-.29-4.34-.87-1.33-.61-2.49-1.44-3.47-2.48-.98-1.05-1.77-2.27-2.35-3.66-.55-1.42-.82-2.95-.82-4.58s.27-3.14.82-4.53c.58-1.42 1.36-2.66 2.35-3.71a10.35 10.35 0 0 1 3.47-2.44c1.33-.61 2.78-.92 4.34-.92 1.24 0 2.4.23 3.47.7 1.07.44 2.03 1.06 2.86 1.88v-1.88h6.64v-.02zM35.73 28.97c1.36 0 2.47-.46 3.35-1.4.9-.93 1.35-2.12 1.35-3.57 0-1.45-.45-2.65-1.35-3.57-.87-.93-1.99-1.4-3.35-1.4s-2.49.46-3.39 1.4c-.87.93-1.3 2.12-1.3 3.57 0 1.45.44 2.65 1.3 3.57.9.93 2.03 1.4 3.39 1.4zM13.2 2.02c1.97 0 3.65.28 5.04.83 1.39.55 2.56 1.32 3.52 2.31.98 1.02 1.75 2.18 2.3 3.49.55 1.31.82 2.72.82 4.23 0 1.51-.27 2.92-.82 4.23a11.15 11.15 0 0 1-2.3 3.49c-.95.99-2.13 1.76-3.52 2.31-1.39.55-3.07.83-5.04.83H9.6V34.9H2V2.02h11.2zm-1.08 14.96c1.02 0 1.79-.1 2.35-.31.58-.23 1.06-.53 1.43-.87.78-.73 1.17-1.7 1.17-2.92s-.39-2.19-1.17-2.92c-.38-.35-.85-.63-1.43-.83-.55-.23-1.33-.35-2.35-.35H9.6v8.2h2.52z\"/></svg>\n\\ No newline at end of file"}, {"sha": "1fd094f80f21d6b8b9a532ac20b88ae696c1bca7", "filename": "data/interfaces/default/images/plex-logo-light-small.png", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fplex-logo-light-small.png", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fplex-logo-light-small.png", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2Fplex-logo-light-small.png?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "d881e835fb592cb2aac1b5f29b31506a7acde4e8", "filename": "data/interfaces/default/images/plex-logo-light.svg", "status": "modified", "additions": 22, "deletions": 26, "changes": 48, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fplex-logo-light.svg", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fplex-logo-light.svg", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2Fplex-logo-light.svg?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,26 +1,22 @@\n-<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n-<svg width=\"3086px\" height=\"1000px\" viewBox=\"0 0 3086 1000\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n-    <!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->\n-    <title>plex-logo-light</title>\n-    <desc>Created with Sketch.</desc>\n-    <defs>\n-        <radialGradient cx=\"89.2670157%\" cy=\"49.76%\" fx=\"89.2670157%\" fy=\"49.76%\" r=\"92.4996161%\" id=\"radialGradient-1\">\n-            <stop stop-color=\"#F9BE03\" offset=\"0%\"></stop>\n-            <stop stop-color=\"#CC7C19\" offset=\"100%\"></stop>\n-        </radialGradient>\n-    </defs>\n-    <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n-        <g id=\"plex-logo-light\" sketch:type=\"MSArtboardGroup\">\n-            <g sketch:type=\"MSLayerGroup\">\n-                <path d=\"M3085.99,0 L2795.989,0 L2505.99,500 L2795.989,1000 L3085.737,1000 L2795.989,500.25 L3085.99,0\" id=\"X\" fill=\"#FFFFFF\" sketch:type=\"MSShapeGroup\"></path>\n-                <path d=\"M2186,0 L2476.00071,0 L2796,500.25 L2476.00071,1000.5 L2186,1000.5 L2505.99929,500.25 L2186,0\" id=\"chevron\" fill=\"url(#radialGradient-1)\" sketch:type=\"MSShapeGroup\"></path>\n-                <path d=\"M2085.947,1000 L1508.874,1000 L1508.874,0 L2085.947,0 L2085.947,173.737 L1721.339,173.737 L1721.339,393.299 L2060.594,393.299 L2060.594,567.03 L1721.339,567.03 L1721.339,824.895 L2085.947,824.895 L2085.947,1000\" id=\"E\" fill=\"#FFFFFF\" sketch:type=\"MSShapeGroup\"></path>\n-                <path d=\"M791.276,1000 L791.276,0 L1003.316,0 L1003.316,824.895 L1408.925,824.895 L1408.925,1000 L791.276,1000\" id=\"L\" fill=\"#FFFFFF\" sketch:type=\"MSShapeGroup\"></path>\n-                <g id=\"P\" fill=\"#FFFFFF\" sketch:type=\"MSShapeGroup\">\n-                    <path d=\"M589.947,558.824 C522.679,615.831 427.037,644.325 303.009,644.325 L212.04,644.325 L212.04,1000 L0,1000 L0,643.947829 L0,470.337388 L290,470.697418 C467.563171,468.627777 476.842468,359.878967 476.842468,322.200012 C476.842468,287.221283 476.842468,175.445374 319,173.699997 L0,173.703242 L0,0 L319.424,0 C440.717,0 532.939,26.107 596.101,78.321 C659.253,130.534 690.834,208.392 690.834,311.902 C690.834,419.527 657.202,501.83 589.947,558.824 Z\" id=\"Path\"></path>\n-                    <rect id=\"Path\" x=\"0\" y=\"110\" width=\"212.2\" height=\"429\"></rect>\n-                </g>\n-            </g>\n-        </g>\n-    </g>\n-</svg>\n\\ No newline at end of file\n+<?xml version=\"1.0\" encoding=\"utf-8\"?>\n+<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\n+<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\"\n+\t viewBox=\"0 0 970 451\" style=\"enable-background:new 0 0 970 451;\" xml:space=\"preserve\">\n+<style type=\"text/css\">\n+\t.st0{fill:#FFFBF8;}\n+\t.st1{fill:#EBAF00;}\n+</style>\n+<path class=\"st0\" d=\"M159.4,83C121,83,96,94.2,74.6,120.3V91.5H0v355c0,0,1.5,0.7,5.1,1.5c5.5,1.2,32.8,7.5,53.2-10\n+\tC75.9,422.9,80,405.5,80,385.4V334c21.3,22.9,45.8,32.5,80,32.5c73.6,0,130.1-59.7,130.1-139.1C290,142.7,235.6,83,159.4,83\n+\tL159.4,83z M144.8,299.7c-41,0-73.9-34-73.9-75.6c0-41.6,38.4-73.3,73.9-73.3c41.6,0,73.9,32.2,73.9,73.9\n+\tC218.7,266.3,186.5,299.7,144.8,299.7z\"/>\n+<path class=\"st0\" d=\"M396.1,219.7c0,30.7,3.4,68,33.7,108.8c0.6,0.8,2.2,2.8,2.2,2.8c-12.3,21-27.7,35.2-47.9,35.2\n+\tc-16,0-31.5-8.4-44.5-23.1c-13.9-15.7-20.5-36.3-20.5-57.6V0.5h77L396.1,219.7L396.1,219.7z\"/>\n+<path class=\"st1\" d=\"M775.1,358h-92.6l89.9-133.2L682.6,91.5h92.6L865,224.8L775.1,358z\"/>\n+<path class=\"st0\" d=\"M889.7,209.7L970,91.5h-92.9l-33.9,49.3L889.7,209.7z\"/>\n+<path class=\"st0\" d=\"M843.2,309.4c0,0.1,15.8,21.9,15.8,21.9c15.2,23.8,34.7,35.1,57.9,35.2c24.1-0.4,41.2-21.8,47.5-29.3\n+\tc0,0-11.9-10.5-26.6-28c-20.1-23.8-47.1-67.1-48.1-69L843.2,309.4L843.2,309.4z\"/>\n+<path class=\"st0\" d=\"M613.4,281.4c-15.5,14.3-26,22.5-48,22.5c-39,0-60.8-27.2-64-57.6h205.3c1.3-4.2,2.1-9.3,2.1-17.9\n+\tC708.7,145.3,648,83,568,83s-138.6,63.4-138.6,142.9s62.4,140.7,140.2,140.7c54.3,0,101.3-31.1,127.1-85.2L613.4,281.4L613.4,281.4z\n+\t M568.5,145.9c34,0,59.4,22.4,65.3,51.7h-130C510.8,166.6,534.5,145.9,568.5,145.9L568.5,145.9z\"/>\n+</svg>"}, {"sha": "d11194cdc1a2745bcb6eb60b9057d8e7ae6191f6", "filename": "data/interfaces/default/images/stripe-wordmark-white.svg", "status": "added", "additions": 9, "deletions": 0, "changes": 9, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fstripe-wordmark-white.svg", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fimages%2Fstripe-wordmark-white.svg", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fimages%2Fstripe-wordmark-white.svg?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,9 @@\n+<svg width=\"360\" height=\"151\" viewBox=\"0 0 360 151\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n+<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M360 78.0002C360 52.4002 347.6 32.2002 323.9 32.2002C300.1 32.2002 285.7 52.4002 285.7 77.8002C285.7 107.9 302.7 123.1 327.1 123.1C339 123.1 348 120.4 354.8 116.6V96.6002C348 100 340.2 102.1 330.3 102.1C320.6 102.1 312 98.7002 310.9 86.9002H359.8C359.8 85.6002 360 80.4002 360 78.0002ZM310.6 68.5002C310.6 57.2002 317.5 52.5002 323.8 52.5002C329.9 52.5002 336.4 57.2002 336.4 68.5002H310.6Z\" fill=\"white\"/>\n+<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M247.1 32.2002C237.3 32.2002 231 36.8002 227.5 40.0002L226.2 33.8002H204.2V150.4L229.2 145.1L229.3 116.8C232.9 119.4 238.2 123.1 247 123.1C264.9 123.1 281.2 108.7 281.2 77.0002C281.1 48.0002 264.6 32.2002 247.1 32.2002ZM241.1 101.1C235.2 101.1 231.7 99.0002 229.3 96.4002L229.2 59.3002C231.8 56.4002 235.4 54.4002 241.1 54.4002C250.2 54.4002 256.5 64.6002 256.5 77.7002C256.5 91.1002 250.3 101.1 241.1 101.1Z\" fill=\"white\"/>\n+<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M169.8 26.3001L194.9 20.9001V0.600098L169.8 5.9001V26.3001Z\" fill=\"white\"/>\n+<path d=\"M194.9 33.9001H169.8V121.4H194.9V33.9001Z\" fill=\"white\"/>\n+<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M142.9 41.3001L141.3 33.9001H119.7V121.4H144.7V62.1001C150.6 54.4001 160.6 55.8001 163.7 56.9001V33.9001C160.5 32.7001 148.8 30.5001 142.9 41.3001Z\" fill=\"white\"/>\n+<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M92.8999 12.2002L68.4999 17.4002L68.3999 97.5002C68.3999 112.3 79.4999 123.2 94.2999 123.2C102.5 123.2 108.5 121.7 111.8 119.9V99.6002C108.6 100.9 92.7999 105.5 92.7999 90.7002V55.2002H111.8V33.9002H92.7999L92.8999 12.2002Z\" fill=\"white\"/>\n+<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M25.3 59.3002C25.3 55.4002 28.5 53.9002 33.8 53.9002C41.4 53.9002 51 56.2002 58.6 60.3002V36.8002C50.3 33.5002 42.1 32.2002 33.8 32.2002C13.5 32.2002 0 42.8002 0 60.5002C0 88.1002 38 83.7002 38 95.6002C38 100.2 34 101.7 28.4 101.7C20.1 101.7 9.5 98.3002 1.1 93.7002V117.5C10.4 121.5 19.8 123.2 28.4 123.2C49.2 123.2 63.5 112.9 63.5 95.0002C63.4 65.2002 25.3 70.5002 25.3 59.3002Z\" fill=\"white\"/>\n+</svg>"}, {"sha": "a12c504488d3cf7747d5281951239464fe7f2ecf", "filename": "data/interfaces/default/index.html", "status": "modified", "additions": 8, "deletions": 2, "changes": 10, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Findex.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Findex.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Findex.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -298,6 +298,8 @@ <h4 class=\"modal-title\">Raw Stream Info: <strong><span id=\"raw-stream-info-title\n \n     $('#currentActivityHeader-bandwidth-tooltip').tooltip({ container: 'body', placement: 'right', delay: 50 });\n \n+    var title = document.title;\n+\n     function getCurrentActivity() {\n         activity_ready = false;\n \n@@ -368,6 +370,8 @@ <h4 class=\"modal-title\">Raw Stream Info: <strong><span id=\"raw-stream-info-title\n \n                     $('#currentActivityHeader').show();\n \n+                    document.title = stream_count + ' stream' + (stream_count > 1 ? 's' : '') + ' | ' + title;\n+\n                     sessions.forEach(function (session) {\n                         var s = (typeof Proxy === \"function\") ? new Proxy(session, defaultHandler) : session;\n                         var key = s.session_key;\n@@ -562,7 +566,7 @@ <h4 class=\"modal-title\">Raw Stream Info: <strong><span id=\"raw-stream-info-title\n \n                         // Update the stream progress times\n                         $('#stream-eta-' + key).html(moment().add(parseInt(s.duration) - parseInt(s.view_offset), 'milliseconds').format(time_format));\n-                        $('#stream-duration-' + key).html(millisecondsToMinutes(parseInt(s.stream_duration), false));\n+                        $('#stream-duration-' + key).html(millisecondsToHoursMinutes(parseInt(s.stream_duration)));\n                         var stream_view_offset = $('#stream-view-offset-' + key);\n                         stream_view_offset.data('state', s.state);\n                         if (stream_view_offset.data('last_view_offset') !== s.view_offset) {\n@@ -600,6 +604,8 @@ <h4 class=\"modal-title\">Raw Stream Info: <strong><span id=\"raw-stream-info-title\n                 } else {\n                     $('#currentActivityHeader').hide();\n                     $('#currentActivity').html('<div id=\"dashboard-no-activity\" class=\"text-muted\">Nothing is currently being played.</div>');\n+\n+                    document.title = title;\n                 }\n \n                 activity_ready = true;\n@@ -663,7 +669,7 @@ <h4 class=\"modal-title\">Raw Stream Info: <strong><span id=\"raw-stream-info-title\n                 if ($(this).data('state') === 'playing' && $(this).data('view_offset') >= 0) {\n                     var view_offset = parseInt($(this).data('view_offset'));\n                     var stream_duration = parseInt($(this).data('stream_duration'));\n-                    var timestamp = millisecondsToMinutes(Math.min(view_offset, stream_duration), false);\n+                    var timestamp = millisecondsToHoursMinutes(Math.min(view_offset, stream_duration));\n                     $(this).html(timestamp).data('view_offset', Math.min(view_offset + 1000, stream_duration))\n                 }\n             });"}, {"sha": "bba442e529ec420942a23551b6b6c9f5f8d69291", "filename": "data/interfaces/default/ip_address_modal.html", "status": "modified", "additions": 0, "deletions": 1, "changes": 1, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fip_address_modal.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fip_address_modal.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fip_address_modal.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -49,7 +49,6 @@ <h4>\n             </div>\n             <div class=\"col-sm-6\">\n                 <ul class=\"list-unstyled\">\n-                    <li>Timezone: <strong><span id=\"timezone\"></span></strong></li>\n                     <li>Latitude: <strong><span id=\"latitude\"></span></strong></li>\n                     <li>Longitude: <strong><span id=\"longitude\"></span></strong></li>\n                 </ul>"}, {"sha": "597858adcb1d4e41604b0d70e1953a65ae762565", "filename": "data/interfaces/default/js/ace/ace.js", "status": "added", "additions": 23, "deletions": 0, "changes": 23, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Face.js", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Face.js", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Face.js?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "5498e281cb9bd7645ff89067c1e8737742722b83", "filename": "data/interfaces/default/js/ace/mode-html.js", "status": "added", "additions": 8, "deletions": 0, "changes": 8, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Fmode-html.js", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Fmode-html.js", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Fmode-html.js?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,8 @@\n+define(\"ace/mode/jsdoc_comment_highlight_rules\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text_highlight_rules\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"./text_highlight_rules\").TextHighlightRules,s=function(){this.$rules={start:[{token:[\"comment.doc.tag\",\"comment.doc.text\",\"lparen.doc\"],regex:\"(@(?:param|member|typedef|property|namespace|var|const|callback))(\\\\s*)({)\",push:[{token:\"lparen.doc\",regex:\"{\",push:[{include:\"doc-syntax\"},{token:\"rparen.doc\",regex:\"}|(?=$)\",next:\"pop\"}]},{token:[\"rparen.doc\",\"text.doc\",\"variable.parameter.doc\",\"lparen.doc\",\"variable.parameter.doc\",\"rparen.doc\"],regex:/(})(\\s*)(?:([\\w=:\\/\\.]+)|(?:(\\[)([\\w=:\\/\\.\\-\\'\\\" ]+)(\\])))/,next:\"pop\"},{token:\"rparen.doc\",regex:\"}|(?=$)\",next:\"pop\"},{include:\"doc-syntax\"},{defaultToken:\"text.doc\"}]},{token:[\"comment.doc.tag\",\"text.doc\",\"lparen.doc\"],regex:\"(@(?:returns?|yields|type|this|suppress|public|protected|private|package|modifies|implements|external|exception|throws|enum|define|extends))(\\\\s*)({)\",push:[{token:\"lparen.doc\",regex:\"{\",push:[{include:\"doc-syntax\"},{token:\"rparen.doc\",regex:\"}|(?=$)\",next:\"pop\"}]},{token:\"rparen.doc\",regex:\"}|(?=$)\",next:\"pop\"},{include:\"doc-syntax\"},{defaultToken:\"text.doc\"}]},{token:[\"comment.doc.tag\",\"text.doc\",\"variable.parameter.doc\"],regex:'(@(?:alias|memberof|instance|module|name|lends|namespace|external|this|template|requires|param|implements|function|extends|typedef|mixes|constructor|var|memberof\\\\!|event|listens|exports|class|constructs|interface|emits|fires|throws|const|callback|borrows|augments))(\\\\s+)(\\\\w[\\\\w#.:/~\"\\\\-]*)?'},{token:[\"comment.doc.tag\",\"text.doc\",\"variable.parameter.doc\"],regex:\"(@method)(\\\\s+)(\\\\w[\\\\w.\\\\(\\\\)]*)\"},{token:\"comment.doc.tag\",regex:\"@access\\\\s+(?:private|public|protected)\"},{token:\"comment.doc.tag\",regex:\"@kind\\\\s+(?:class|constant|event|external|file|function|member|mixin|module|namespace|typedef)\"},{token:\"comment.doc.tag\",regex:\"@\\\\w+(?=\\\\s|$)\"},s.getTagRule(),{defaultToken:\"comment.doc.body\",caseInsensitive:!0}],\"doc-syntax\":[{token:\"operator.doc\",regex:/[|:]/},{token:\"paren.doc\",regex:/[\\[\\]]/}]},this.normalizeRules()};r.inherits(s,i),s.getTagRule=function(e){return{token:\"comment.doc.tag.storage.type\",regex:\"\\\\b(?:TODO|FIXME|XXX|HACK)\\\\b\"}},s.getStartRule=function(e){return{token:\"comment.doc\",regex:/\\/\\*\\*(?!\\/)/,next:e}},s.getEndRule=function(e){return{token:\"comment.doc\",regex:\"\\\\*\\\\/\",next:e}},t.JsDocCommentHighlightRules=s}),define(\"ace/mode/javascript_highlight_rules\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/jsdoc_comment_highlight_rules\",\"ace/mode/text_highlight_rules\"],function(e,t,n){\"use strict\";function a(){var e=o.replace(\"\\\\d\",\"\\\\d\\\\-\"),t={onMatch:function(e,t,n){var r=e.charAt(1)==\"/\"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:\"meta.tag.punctuation.\"+(r==1?\"\":\"end-\")+\"tag-open.xml\",value:e.slice(0,r)},{type:\"meta.tag.tag-name.xml\",value:e.substr(r)}]},regex:\"</?(?:\"+e+\"|(?=>))\",next:\"jsxAttributes\",nextState:\"jsx\"};this.$rules.start.unshift(t);var n={regex:\"{\",token:\"paren.quasi.start\",push:\"start\"};this.$rules.jsx=[n,t,{include:\"reference\"},{defaultToken:\"string.xml\"}],this.$rules.jsxAttributes=[{token:\"meta.tag.punctuation.tag-close.xml\",regex:\"/?>\",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||\"start\",[{type:this.token,value:e}]},nextState:\"jsx\"},n,f(\"jsxAttributes\"),{token:\"entity.other.attribute-name.xml\",regex:e},{token:\"keyword.operator.attribute-equals.xml\",regex:\"=\"},{token:\"text.tag-whitespace.xml\",regex:\"\\\\s+\"},{token:\"string.attribute-value.xml\",regex:\"'\",stateName:\"jsx_attr_q\",push:[{token:\"string.attribute-value.xml\",regex:\"'\",next:\"pop\"},{include:\"reference\"},{defaultToken:\"string.attribute-value.xml\"}]},{token:\"string.attribute-value.xml\",regex:'\"',stateName:\"jsx_attr_qq\",push:[{token:\"string.attribute-value.xml\",regex:'\"',next:\"pop\"},{include:\"reference\"},{defaultToken:\"string.attribute-value.xml\"}]},t],this.$rules.reference=[{token:\"constant.language.escape.reference.xml\",regex:\"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\\\.-]+;)\"}]}function f(e){return[{token:\"comment\",regex:/\\/\\*/,next:[i.getTagRule(),{token:\"comment\",regex:\"\\\\*\\\\/\",next:e||\"pop\"},{defaultToken:\"comment\",caseInsensitive:!0}]},{token:\"comment\",regex:\"\\\\/\\\\/\",next:[i.getTagRule(),{token:\"comment\",regex:\"$|^\",next:e||\"pop\"},{defaultToken:\"comment\",caseInsensitive:!0}]}]}var r=e(\"../lib/oop\"),i=e(\"./jsdoc_comment_highlight_rules\").JsDocCommentHighlightRules,s=e(\"./text_highlight_rules\").TextHighlightRules,o=\"[a-zA-Z\\\\$_\\u00a1-\\uffff][a-zA-Z\\\\d\\\\$_\\u00a1-\\uffff]*\",u=function(e){var t={\"variable.language\":\"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Symbol|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document\",keyword:\"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static|constructor\",\"storage.type\":\"const|let|var|function\",\"constant.language\":\"null|Infinity|NaN|undefined\",\"support.function\":\"alert\",\"constant.language.boolean\":\"true|false\"},n=this.createKeywordMapper(t,\"identifier\"),r=\"case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void\",s=\"\\\\\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)\",u=\"(function)(\\\\s*)(\\\\*?)\",l={token:[\"identifier\",\"text\",\"paren.lparen\"],regex:\"(\\\\b(?!\"+Object.values(t).join(\"|\")+\"\\\\b)\"+o+\")(\\\\s*)(\\\\()\"};this.$rules={no_regex:[i.getStartRule(\"doc-start\"),f(\"no_regex\"),l,{token:\"string\",regex:\"'(?=.)\",next:\"qstring\"},{token:\"string\",regex:'\"(?=.)',next:\"qqstring\"},{token:\"constant.numeric\",regex:/0(?:[xX][0-9a-fA-F]+|[oO][0-7]+|[bB][01]+)\\b/},{token:\"constant.numeric\",regex:/(?:\\d\\d*(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+\\b)?/},{token:[\"entity.name.function\",\"text\",\"keyword.operator\",\"text\",\"storage.type\",\"text\",\"storage.type\",\"text\",\"paren.lparen\"],regex:\"(\"+o+\")(\\\\s*)(=)(\\\\s*)\"+u+\"(\\\\s*)(\\\\()\",next:\"function_arguments\"},{token:[\"storage.type\",\"text\",\"storage.type\",\"text\",\"text\",\"entity.name.function\",\"text\",\"paren.lparen\"],regex:\"(function)(?:(?:(\\\\s*)(\\\\*)(\\\\s*))|(\\\\s+))(\"+o+\")(\\\\s*)(\\\\()\",next:\"function_arguments\"},{token:[\"entity.name.function\",\"text\",\"punctuation.operator\",\"text\",\"storage.type\",\"text\",\"storage.type\",\"text\",\"paren.lparen\"],regex:\"(\"+o+\")(\\\\s*)(:)(\\\\s*)\"+u+\"(\\\\s*)(\\\\()\",next:\"function_arguments\"},{token:[\"text\",\"text\",\"storage.type\",\"text\",\"storage.type\",\"text\",\"paren.lparen\"],regex:\"(:)(\\\\s*)\"+u+\"(\\\\s*)(\\\\()\",next:\"function_arguments\"},{token:\"keyword\",regex:\"from(?=\\\\s*('|\\\"))\"},{token:\"keyword\",regex:\"(?:\"+r+\")\\\\b\",next:\"start\"},{token:\"support.constant\",regex:/that\\b/},{token:[\"storage.type\",\"punctuation.operator\",\"support.function.firebug\"],regex:/(console)(\\.)(warn|info|log|error|debug|time|trace|timeEnd|assert)\\b/},{token:n,regex:o},{token:\"punctuation.operator\",regex:/[.](?![.])/,next:\"property\"},{token:\"storage.type\",regex:/=>/,next:\"start\"},{token:\"keyword.operator\",regex:/--|\\+\\+|\\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\\|\\||\\?:|[!$%&*+\\-~\\/^]=?/,next:\"start\"},{token:\"punctuation.operator\",regex:/[?:,;.]/,next:\"start\"},{token:\"paren.lparen\",regex:/[\\[({]/,next:\"start\"},{token:\"paren.rparen\",regex:/[\\])}]/},{token:\"comment\",regex:/^#!.*$/}],property:[{token:\"text\",regex:\"\\\\s+\"},{token:\"keyword.operator\",regex:/=/},{token:[\"storage.type\",\"text\",\"storage.type\",\"text\",\"paren.lparen\"],regex:u+\"(\\\\s*)(\\\\()\",next:\"function_arguments\"},{token:[\"storage.type\",\"text\",\"storage.type\",\"text\",\"text\",\"entity.name.function\",\"text\",\"paren.lparen\"],regex:\"(function)(?:(?:(\\\\s*)(\\\\*)(\\\\s*))|(\\\\s+))(\\\\w+)(\\\\s*)(\\\\()\",next:\"function_arguments\"},{token:\"punctuation.operator\",regex:/[.](?![.])/},{token:\"support.function\",regex:\"prototype\"},{token:\"support.function\",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|lter|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward|rEach)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\\b(?=\\()/},{token:\"support.function.dom\",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\\b(?=\\()/},{token:\"support.constant\",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\\b/},{token:\"identifier\",regex:o},{regex:\"\",token:\"empty\",next:\"no_regex\"}],start:[i.getStartRule(\"doc-start\"),f(\"start\"),{token:\"string.regexp\",regex:\"\\\\/\",next:\"regex\"},{token:\"text\",regex:\"\\\\s+|^$\",next:\"start\"},{token:\"empty\",regex:\"\",next:\"no_regex\"}],regex:[{token:\"regexp.keyword.operator\",regex:\"\\\\\\\\(?:u[\\\\da-fA-F]{4}|x[\\\\da-fA-F]{2}|.)\"},{token:\"string.regexp\",regex:\"/[sxngimy]*\",next:\"no_regex\"},{token:\"invalid\",regex:/\\{\\d+\\b,?\\d*\\}[+*]|[+*$^?][+*]|[$^][?]|\\?{3,}/},{token:\"constant.language.escape\",regex:/\\(\\?[:=!]|\\)|\\{\\d+\\b,?\\d*\\}|[+*]\\?|[()$^+*?.]/},{token:\"constant.language.delimiter\",regex:/\\|/},{token:\"constant.language.escape\",regex:/\\[\\^?/,next:\"regex_character_class\"},{token:\"empty\",regex:\"$\",next:\"no_regex\"},{defaultToken:\"string.regexp\"}],regex_character_class:[{token:\"regexp.charclass.keyword.operator\",regex:\"\\\\\\\\(?:u[\\\\da-fA-F]{4}|x[\\\\da-fA-F]{2}|.)\"},{token:\"constant.language.escape\",regex:\"]\",next:\"regex\"},{token:\"constant.language.escape\",regex:\"-\"},{token:\"empty\",regex:\"$\",next:\"no_regex\"},{defaultToken:\"string.regexp.charachterclass\"}],default_parameter:[{token:\"string\",regex:\"'(?=.)\",push:[{token:\"string\",regex:\"'|$\",next:\"pop\"},{include:\"qstring\"}]},{token:\"string\",regex:'\"(?=.)',push:[{token:\"string\",regex:'\"|$',next:\"pop\"},{include:\"qqstring\"}]},{token:\"constant.language\",regex:\"null|Infinity|NaN|undefined\"},{token:\"constant.numeric\",regex:/0(?:[xX][0-9a-fA-F]+|[oO][0-7]+|[bB][01]+)\\b/},{token:\"constant.numeric\",regex:/(?:\\d\\d*(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+\\b)?/},{token:\"punctuation.operator\",regex:\",\",next:\"function_arguments\"},{token:\"text\",regex:\"\\\\s+\"},{token:\"punctuation.operator\",regex:\"$\"},{token:\"empty\",regex:\"\",next:\"no_regex\"}],function_arguments:[f(\"function_arguments\"),{token:\"variable.parameter\",regex:o},{token:\"punctuation.operator\",regex:\",\"},{token:\"text\",regex:\"\\\\s+\"},{token:\"punctuation.operator\",regex:\"$\"},{token:\"empty\",regex:\"\",next:\"no_regex\"}],qqstring:[{token:\"constant.language.escape\",regex:s},{token:\"string\",regex:\"\\\\\\\\$\",consumeLineEnd:!0},{token:\"string\",regex:'\"|$',next:\"no_regex\"},{defaultToken:\"string\"}],qstring:[{token:\"constant.language.escape\",regex:s},{token:\"string\",regex:\"\\\\\\\\$\",consumeLineEnd:!0},{token:\"string\",regex:\"'|$\",next:\"no_regex\"},{defaultToken:\"string\"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:\"[{}]\",onMatch:function(e,t,n){this.next=e==\"{\"?this.nextState:\"\";if(e==\"{\"&&n.length)n.unshift(\"start\",t);else if(e==\"}\"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf(\"string\")!=-1||this.next.indexOf(\"jsx\")!=-1)return\"paren.quasi.end\"}return e==\"{\"?\"paren.lparen\":\"paren.rparen\"},nextState:\"start\"},{token:\"string.quasi.start\",regex:/`/,push:[{token:\"constant.language.escape\",regex:s},{token:\"paren.quasi.start\",regex:/\\${/,push:\"start\"},{token:\"string.quasi.end\",regex:/`/,next:\"pop\"},{defaultToken:\"string.quasi\"}]},{token:[\"variable.parameter\",\"text\"],regex:\"(\"+o+\")(\\\\s*)(?=\\\\=>)\"},{token:\"paren.lparen\",regex:\"(\\\\()(?=[^\\\\(]+\\\\s*=>)\",next:\"function_arguments\"},{token:\"variable.language\",regex:\"(?:(?:(?:Weak)?(?:Set|Map))|Promise)\\\\b\"}),this.$rules.function_arguments.unshift({token:\"keyword.operator\",regex:\"=\",next:\"default_parameter\"},{token:\"keyword.operator\",regex:\"\\\\.{3}\"}),this.$rules.property.unshift({token:\"support.function\",regex:\"(findIndex|repeat|startsWith|endsWith|includes|isSafeInteger|trunc|cbrt|log2|log10|sign|then|catch|finally|resolve|reject|race|any|all|allSettled|keys|entries|isInteger)\\\\b(?=\\\\()\"},{token:\"constant.language\",regex:\"(?:MAX_SAFE_INTEGER|MIN_SAFE_INTEGER|EPSILON)\\\\b\"}),(!e||e.jsx!=0)&&a.call(this);this.embedRules(i,\"doc-\",[i.getEndRule(\"no_regex\")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),define(\"ace/mode/matching_brace_outdent\",[\"require\",\"exports\",\"module\",\"ace/range\"],function(e,t,n){\"use strict\";var r=e(\"../range\").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\\s+$/.test(e)?/^\\s*\\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\\s*\\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),define(\"ace/mode/behaviour/xml\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/behaviour\",\"ace/token_iterator\"],function(e,t,n){\"use strict\";function o(e,t){return e&&e.type.lastIndexOf(t+\".xml\")>-1}var r=e(\"../../lib/oop\"),i=e(\"../behaviour\").Behaviour,s=e(\"../../token_iterator\").TokenIterator,u=function(){this.add(\"string_dquotes\",\"insertion\",function(e,t,n,r,i){if(i=='\"'||i==\"'\"){var u=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==\"\"&&a!==\"'\"&&a!='\"'&&n.getWrapBehavioursEnabled())return{text:u+a+u,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==u&&(o(p,\"attribute-value\")||o(p,\"string\")))return{text:\"\",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(o(p,\"tag-whitespace\")||o(p,\"whitespace\"))p=h.stepBackward();var d=!c||c.match(/\\s/);if(o(p,\"attribute-equals\")&&(d||c==\">\")||o(p,\"decl-attribute-equals\")&&(d||c==\"?\"))return{text:u+u,selection:[1,1]}}}),this.add(\"string_dquotes\",\"deletion\",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='\"'||s==\"'\")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add(\"autoclosing\",\"insertion\",function(e,t,n,r,i){if(i==\">\"){var u=n.getSelectionRange().start,a=new s(r,u.row,u.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(o(f,\"tag-name\")||o(f,\"tag-whitespace\")||o(f,\"attribute-name\")||o(f,\"attribute-equals\")||o(f,\"attribute-value\")))return;if(o(f,\"reference.attribute-value\"))return;if(o(f,\"attribute-value\")){var l=a.getCurrentTokenColumn()+f.value.length;if(u.column<l)return;if(u.column==l){var c=a.stepForward();if(c&&o(c,\"attribute-value\"))return;a.stepBackward()}}if(/^\\s*>/.test(r.getLine(u.row).slice(u.column)))return;while(!o(f,\"tag-name\")){f=a.stepBackward();if(f.value==\"<\"){f=a.stepForward();break}}var h=a.getCurrentTokenRow(),p=a.getCurrentTokenColumn();if(o(a.stepBackward(),\"end-tag-open\"))return;var d=f.value;h==u.row&&(d=d.substring(0,u.column-p));if(this.voidElements&&this.voidElements.hasOwnProperty(d.toLowerCase()))return;return{text:\"></\"+d+\">\",selection:[1,1]}}}),this.add(\"autoindent\",\"insertion\",function(e,t,n,r,i){if(i==\"\\n\"){var u=n.getCursorPosition(),a=r.getLine(u.row),f=new s(r,u.row,u.column),l=f.getCurrentToken();if(o(l,\"\")&&l.type.indexOf(\"tag-close\")!==-1){if(l.value==\"/>\")return;while(l&&l.type.indexOf(\"tag-name\")===-1)l=f.stepBackward();if(!l)return;var c=l.value,h=f.getCurrentTokenRow();l=f.stepBackward();if(!l||l.type.indexOf(\"end-tag\")!==-1)return;if(this.voidElements&&!this.voidElements[c]||!this.voidElements){var p=r.getTokenAt(u.row,u.column+1),a=r.getLine(h),d=this.$getIndent(a),v=d+r.getTabString();return p&&p.value===\"</\"?{text:\"\\n\"+v+\"\\n\"+d,selection:[1,v.length,1,v.length]}:{text:\"\\n\"+v}}}}})};r.inherits(u,i),t.XmlBehaviour=u}),define(\"ace/mode/behaviour/javascript\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/token_iterator\",\"ace/mode/behaviour/cstyle\",\"ace/mode/behaviour/xml\"],function(e,t,n){\"use strict\";var r=e(\"../../lib/oop\"),i=e(\"../../token_iterator\").TokenIterator,s=e(\"../behaviour/cstyle\").CstyleBehaviour,o=e(\"../behaviour/xml\").XmlBehaviour,u=function(){var e=(new o({closeCurlyBraces:!0})).getBehaviours();this.addBehaviours(e),this.inherit(s),this.add(\"autoclosing-fragment\",\"insertion\",function(e,t,n,r,s){if(s==\">\"){var o=n.getSelectionRange().start,u=new i(r,o.row,o.column),a=u.getCurrentToken()||u.stepBackward();if(!a)return;if(a.value==\"<\")return{text:\"></>\",selection:[1,1]}}})};r.inherits(u,s),t.JavaScriptBehaviour=u}),define(\"ace/mode/folding/xml\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/range\",\"ace/mode/folding/fold_mode\"],function(e,t,n){\"use strict\";function a(e,t){return e&&e.type&&e.type.lastIndexOf(t+\".xml\")>-1}var r=e(\"../../lib/oop\"),i=e(\"../../range\").Range,s=e(\"./fold_mode\").FoldMode,o=t.FoldMode=function(e,t){s.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(o,s);var u=function(){this.tagName=\"\",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t===\"markbeginend\"?\"end\":\"\":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?\"\":this._findEndTagInLine(e,n,r.tagName,r.end.column)?\"\":\"start\":this.getCommentFoldWidget(e,n)},this.getCommentFoldWidget=function(e,t){return/comment/.test(e.getState(t))&&/<!-/.test(e.getLine(t))?\"start\":\"\"},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new u;for(var i=0;i<n.length;i++){var s=n[i];if(a(s,\"tag-open\")){r.end.column=r.start.column+s.value.length,r.closing=a(s,\"end-tag-open\"),s=n[++i];if(!s)return null;r.tagName=s.value;if(s.value===\"\"){s=n[++i];if(!s)return null;r.tagName=s.value}r.end.column+=s.value.length;for(i++;i<n.length;i++){s=n[i],r.end.column+=s.value.length;if(a(s,\"tag-close\")){r.selfClosing=s.value==\"/>\";break}}return r}if(a(s,\"tag-close\"))return r.selfClosing=s.value==\"/>\",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o<i.length;o++){var u=i[o];s+=u.value.length;if(s<r-1)continue;if(a(u,\"end-tag-open\")){u=i[o+1],a(u,\"tag-name\")&&u.value===\"\"&&(u=i[o+2]);if(u&&u.value==n)return!0}}return!1},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return this.getCommentFoldWidget(e,n)&&e.getCommentFoldRange(n,e.getLine(n).length);var s=e.getMatchingTags({row:n,column:0});if(s)return new i(s.openTag.end.row,s.openTag.end.column,s.closeTag.start.row,s.closeTag.start.column)}}).call(o.prototype)}),define(\"ace/mode/folding/cstyle\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/range\",\"ace/mode/folding/fold_mode\"],function(e,t,n){\"use strict\";var r=e(\"../../lib/oop\"),i=e(\"../../range\").Range,s=e(\"./fold_mode\").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\\|[^|]*?$/,\"|\"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\\|[^|]*?$/,\"|\"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/([\\{\\[\\(])[^\\}\\]\\)]*$|^\\s*(\\/\\*)/,this.foldingStopMarker=/^[^\\[\\{\\(]*([\\}\\]\\)])|^[\\s\\*]*(\\*\\/)/,this.singleLineBlockCommentRe=/^\\s*(\\/\\*).*\\*\\/\\s*$/,this.tripleStarBlockCommentRe=/^\\s*(\\/\\*\\*\\*).*\\*\\/\\s*$/,this.startRegionRe=/^\\s*(\\/\\*|\\/\\/)#?region\\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return\"\";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?\"start\":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!=\"all\"&&(u=null)),u}if(t===\"markbegin\")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++t<a){n=e.getLine(t);var f=n.search(/\\S/);if(f===-1)continue;if(r>f)break;var l=this.getFoldWidgetRange(e,\"all\",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\\s*$/),s=e.getLength(),o=n,u=/^\\s*(?:\\/\\*|\\/\\/|--)#?(end)?region\\b/,a=1;while(++n<s){t=e.getLine(n);var f=u.exec(t);if(!f)continue;f[1]?a--:a++;if(!a)break}var l=n;if(l>o)return new i(o,r,l,t.length)}}.call(o.prototype)}),define(\"ace/mode/folding/javascript\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/folding/xml\",\"ace/mode/folding/cstyle\"],function(e,t,n){\"use strict\";var r=e(\"../../lib/oop\"),i=e(\"./xml\").FoldMode,s=e(\"./cstyle\").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\\|[^|]*?$/,\"|\"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\\|[^|]*?$/,\"|\"+e.end))),this.xmlFoldMode=new i};r.inherits(o,s),function(){this.getFoldWidgetRangeBase=this.getFoldWidgetRange,this.getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=this.getFoldWidgetBase(e,t,n);return r?r:this.xmlFoldMode.getFoldWidget(e,t,n)},this.getFoldWidgetRange=function(e,t,n,r){var i=this.getFoldWidgetRangeBase(e,t,n,r);return i?i:this.xmlFoldMode.getFoldWidgetRange(e,t,n)}}.call(o.prototype)}),define(\"ace/mode/javascript\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/mode/javascript_highlight_rules\",\"ace/mode/matching_brace_outdent\",\"ace/worker/worker_client\",\"ace/mode/behaviour/javascript\",\"ace/mode/folding/javascript\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"./text\").Mode,s=e(\"./javascript_highlight_rules\").JavaScriptHighlightRules,o=e(\"./matching_brace_outdent\").MatchingBraceOutdent,u=e(\"../worker/worker_client\").WorkerClient,a=e(\"./behaviour/javascript\").JavaScriptBehaviour,f=e(\"./folding/javascript\").FoldMode,l=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new a,this.foldingRules=new f};r.inherits(l,i),function(){this.lineCommentStart=\"//\",this.blockComment={start:\"/*\",end:\"*/\"},this.$quotes={'\"':'\"',\"'\":\"'\",\"`\":\"`\"},this.$pairQuotesAfter={\"`\":/\\w/},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type==\"comment\")return r;if(e==\"start\"||e==\"no_regex\"){var u=t.match(/^.*(?:\\bcase\\b.*:|[\\{\\(\\[])\\s*$/);u&&(r+=n)}else if(e==\"doc-start\")if(o==\"start\"||o==\"no_regex\")return\"\";return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new u([\"ace\"],\"ace/mode/javascript_worker\",\"JavaScriptWorker\");return t.attachToDocument(e.getDocument()),t.on(\"annotate\",function(t){e.setAnnotations(t.data)}),t.on(\"terminate\",function(){e.clearAnnotations()}),t},this.$id=\"ace/mode/javascript\",this.snippetFileId=\"ace/snippets/javascript\"}.call(l.prototype),t.Mode=l}),define(\"ace/mode/css_highlight_rules\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/lang\",\"ace/mode/text_highlight_rules\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"../lib/lang\"),s=e(\"./text_highlight_rules\").TextHighlightRules,o=t.supportType=\"align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|max-zoom|min-height|min-width|min-zoom|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|user-select|user-zoom|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index\",u=t.supportFunction=\"rgb|rgba|url|attr|counter|counters\",a=t.supportConstant=\"absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|flex-end|flex-start|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero|zoom\",f=t.supportConstantColor=\"aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|rebeccapurple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen\",l=t.supportConstantFonts=\"arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace\",c=t.numRe=\"\\\\-?(?:(?:[0-9]+(?:\\\\.[0-9]+)?)|(?:\\\\.[0-9]+))\",h=t.pseudoElements=\"(\\\\:+)\\\\b(after|before|first-letter|first-line|moz-selection|selection)\\\\b\",p=t.pseudoClasses=\"(:)\\\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\\\b\",d=function(){var e=this.createKeywordMapper({\"support.function\":u,\"support.constant\":a,\"support.type\":o,\"support.constant.color\":f,\"support.constant.fonts\":l},\"text\",!0);this.$rules={start:[{include:[\"strings\",\"url\",\"comments\"]},{token:\"paren.lparen\",regex:\"\\\\{\",next:\"ruleset\"},{token:\"paren.rparen\",regex:\"\\\\}\"},{token:\"string\",regex:\"@(?!viewport)\",next:\"media\"},{token:\"keyword\",regex:\"#[a-z0-9-_]+\"},{token:\"keyword\",regex:\"%\"},{token:\"variable\",regex:\"\\\\.[a-z0-9-_]+\"},{token:\"string\",regex:\":[a-z0-9-_]+\"},{token:\"constant.numeric\",regex:c},{token:\"constant\",regex:\"[a-z0-9-_]+\"},{caseInsensitive:!0}],media:[{include:[\"strings\",\"url\",\"comments\"]},{token:\"paren.lparen\",regex:\"\\\\{\",next:\"start\"},{token:\"paren.rparen\",regex:\"\\\\}\",next:\"start\"},{token:\"string\",regex:\";\",next:\"start\"},{token:\"keyword\",regex:\"(?:media|supports|document|charset|import|namespace|media|supports|document|page|font|keyframes|viewport|counter-style|font-feature-values|swash|ornaments|annotation|stylistic|styleset|character-variant)\"}],comments:[{token:\"comment\",regex:\"\\\\/\\\\*\",push:[{token:\"comment\",regex:\"\\\\*\\\\/\",next:\"pop\"},{defaultToken:\"comment\"}]}],ruleset:[{regex:\"-(webkit|ms|moz|o)-\",token:\"text\"},{token:\"punctuation.operator\",regex:\"[:;]\"},{token:\"paren.rparen\",regex:\"\\\\}\",next:\"start\"},{include:[\"strings\",\"url\",\"comments\"]},{token:[\"constant.numeric\",\"keyword\"],regex:\"(\"+c+\")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vmax|vmin|vm|vw|%)\"},{token:\"constant.numeric\",regex:c},{token:\"constant.numeric\",regex:\"#[a-f0-9]{6}\"},{token:\"constant.numeric\",regex:\"#[a-f0-9]{3}\"},{token:[\"punctuation\",\"entity.other.attribute-name.pseudo-element.css\"],regex:h},{token:[\"punctuation\",\"entity.other.attribute-name.pseudo-class.css\"],regex:p},{include:\"url\"},{token:e,regex:\"\\\\-?[a-zA-Z_][a-zA-Z0-9_\\\\-]*\"},{token:\"paren.lparen\",regex:\"\\\\{\"},{caseInsensitive:!0}],url:[{token:\"support.function\",regex:\"(?:url(:?-prefix)?|domain|regexp)\\\\(\",push:[{token:\"support.function\",regex:\"\\\\)\",next:\"pop\"},{defaultToken:\"string\"}]}],strings:[{token:\"string.start\",regex:\"'\",push:[{token:\"string.end\",regex:\"'|$\",next:\"pop\"},{include:\"escapes\"},{token:\"constant.language.escape\",regex:/\\\\$/,consumeLineEnd:!0},{defaultToken:\"string\"}]},{token:\"string.start\",regex:'\"',push:[{token:\"string.end\",regex:'\"|$',next:\"pop\"},{include:\"escapes\"},{token:\"constant.language.escape\",regex:/\\\\$/,consumeLineEnd:!0},{defaultToken:\"string\"}]}],escapes:[{token:\"constant.language.escape\",regex:/\\\\([a-fA-F\\d]{1,6}|[^a-fA-F\\d])/}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),define(\"ace/mode/css_completions\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";var r={background:{\"#$0\":1},\"background-color\":{\"#$0\":1,transparent:1,fixed:1},\"background-image\":{\"url('/$0')\":1},\"background-repeat\":{repeat:1,\"repeat-x\":1,\"repeat-y\":1,\"no-repeat\":1,inherit:1},\"background-position\":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},\"background-attachment\":{scroll:1,fixed:1},\"background-size\":{cover:1,contain:1},\"background-clip\":{\"border-box\":1,\"padding-box\":1,\"content-box\":1},\"background-origin\":{\"border-box\":1,\"padding-box\":1,\"content-box\":1},border:{\"solid $0\":1,\"dashed $0\":1,\"dotted $0\":1,\"#$0\":1},\"border-color\":{\"#$0\":1},\"border-style\":{solid:2,dashed:2,dotted:2,\"double\":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},\"border-collapse\":{collapse:1,separate:1},bottom:{px:1,em:1,\"%\":1},clear:{left:1,right:1,both:1,none:1},color:{\"#$0\":1,\"rgb(#$00,0,0)\":1},cursor:{\"default\":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,\"n-resize\":1,\"ne-resize\":1,\"e-resize\":1,\"se-resize\":1,\"s-resize\":1,\"sw-resize\":1,\"w-resize\":1,\"nw-resize\":1},display:{none:1,block:1,inline:1,\"inline-block\":1,\"table-cell\":1},\"empty-cells\":{show:1,hide:1},\"float\":{left:1,right:1,none:1},\"font-family\":{Arial:2,\"Comic Sans MS\":2,Consolas:2,\"Courier New\":2,Courier:2,Georgia:2,Monospace:2,\"Sans-Serif\":2,\"Segoe UI\":2,Tahoma:2,\"Times New Roman\":2,\"Trebuchet MS\":2,Verdana:1},\"font-size\":{px:1,em:1,\"%\":1},\"font-weight\":{bold:1,normal:1},\"font-style\":{italic:1,normal:1},\"font-variant\":{normal:1,\"small-caps\":1},height:{px:1,em:1,\"%\":1},left:{px:1,em:1,\"%\":1},\"letter-spacing\":{normal:1},\"line-height\":{normal:1},\"list-style-type\":{none:1,disc:1,circle:1,square:1,decimal:1,\"decimal-leading-zero\":1,\"lower-roman\":1,\"upper-roman\":1,\"lower-greek\":1,\"lower-latin\":1,\"upper-latin\":1,georgian:1,\"lower-alpha\":1,\"upper-alpha\":1},margin:{px:1,em:1,\"%\":1},\"margin-right\":{px:1,em:1,\"%\":1},\"margin-left\":{px:1,em:1,\"%\":1},\"margin-top\":{px:1,em:1,\"%\":1},\"margin-bottom\":{px:1,em:1,\"%\":1},\"max-height\":{px:1,em:1,\"%\":1},\"max-width\":{px:1,em:1,\"%\":1},\"min-height\":{px:1,em:1,\"%\":1},\"min-width\":{px:1,em:1,\"%\":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},\"overflow-x\":{hidden:1,visible:1,auto:1,scroll:1},\"overflow-y\":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,\"%\":1},\"padding-top\":{px:1,em:1,\"%\":1},\"padding-right\":{px:1,em:1,\"%\":1},\"padding-bottom\":{px:1,em:1,\"%\":1},\"padding-left\":{px:1,em:1,\"%\":1},\"page-break-after\":{auto:1,always:1,avoid:1,left:1,right:1},\"page-break-before\":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,\"static\":1},right:{px:1,em:1,\"%\":1},\"table-layout\":{fixed:1,auto:1},\"text-decoration\":{none:1,underline:1,\"line-through\":1,blink:1},\"text-align\":{left:1,right:1,center:1,justify:1},\"text-transform\":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,\"%\":1},\"vertical-align\":{top:1,bottom:1},visibility:{hidden:1,visible:1},\"white-space\":{nowrap:1,normal:1,pre:1,\"pre-line\":1,\"pre-wrap\":1},width:{px:1,em:1,\"%\":1},\"word-spacing\":{normal:1},filter:{\"alpha(opacity=$0100)\":1},\"text-shadow\":{\"$02px 2px 2px #777\":1},\"text-overflow\":{\"ellipsis-word\":1,clip:1,ellipsis:1},\"-moz-border-radius\":1,\"-moz-border-radius-topright\":1,\"-moz-border-radius-bottomright\":1,\"-moz-border-radius-topleft\":1,\"-moz-border-radius-bottomleft\":1,\"-webkit-border-radius\":1,\"-webkit-border-top-right-radius\":1,\"-webkit-border-top-left-radius\":1,\"-webkit-border-bottom-right-radius\":1,\"-webkit-border-bottom-left-radius\":1,\"-moz-box-shadow\":1,\"-webkit-box-shadow\":1,transform:{\"rotate($00deg)\":1,\"skew($00deg)\":1},\"-moz-transform\":{\"rotate($00deg)\":1,\"skew($00deg)\":1},\"-webkit-transform\":{\"rotate($00deg)\":1,\"skew($00deg)\":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement(\"c\").style;for(var t in e){if(typeof e[t]!=\"string\")continue;var n=t.replace(/[A-Z]/g,function(e){return\"-\"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();if(e===\"ruleset\"||t.$mode.$id==\"ace/mode/scss\"){var i=t.getLine(n.row).substr(0,n.column),s=/\\([^)]*$/.test(i);return s&&(i=i.substr(i.lastIndexOf(\"(\")+1)),/:[^;]+$/.test(i)?(/([\\w\\-]+):[^:]*$/.test(i),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r,s)}return[]},this.getPropertyCompletions=function(e,t,n,i,s){s=s||!1;var o=Object.keys(r);return o.map(function(e){return{caption:e,snippet:e+\": $0\"+(s?\"\":\";\"),meta:\"property\",score:1e6}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\\w\\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]==\"object\"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:\"property value\",score:1e6}})}}).call(i.prototype),t.CssCompletions=i}),define(\"ace/mode/behaviour/css\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/behaviour\",\"ace/mode/behaviour/cstyle\",\"ace/token_iterator\"],function(e,t,n){\"use strict\";var r=e(\"../../lib/oop\"),i=e(\"../behaviour\").Behaviour,s=e(\"./cstyle\").CstyleBehaviour,o=e(\"../../token_iterator\").TokenIterator,u=function(){this.inherit(s),this.add(\"colon\",\"insertion\",function(e,t,n,r,i){if(i===\":\"&&n.selection.isEmpty()){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\\s+/)&&(a=u.stepBackward());if(a&&a.type===\"support.type\"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===\":\")return{text:\"\",selection:[1,1]};if(/^(\\s+[^;]|\\s*$)/.test(f.substring(s.column)))return{text:\":;\",selection:[1,1]}}}}),this.add(\"colon\",\"deletion\",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===\":\"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\\s+/)&&(f=a.stepBackward());if(f&&f.type===\"support.type\"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===\";\")return i.end.column++,i}}}),this.add(\"semicolon\",\"insertion\",function(e,t,n,r,i){if(i===\";\"&&n.selection.isEmpty()){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===\";\")return{text:\"\",selection:[1,1]}}}),this.add(\"!important\",\"insertion\",function(e,t,n,r,i){if(i===\"!\"&&n.selection.isEmpty()){var s=n.getCursorPosition(),o=r.doc.getLine(s.row);if(/^\\s*(;|}|$)/.test(o.substring(s.column)))return{text:\"!important\",selection:[10,10]}}})};r.inherits(u,s),t.CssBehaviour=u}),define(\"ace/mode/css\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/mode/css_highlight_rules\",\"ace/mode/matching_brace_outdent\",\"ace/worker/worker_client\",\"ace/mode/css_completions\",\"ace/mode/behaviour/css\",\"ace/mode/folding/cstyle\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"./text\").Mode,s=e(\"./css_highlight_rules\").CssHighlightRules,o=e(\"./matching_brace_outdent\").MatchingBraceOutdent,u=e(\"../worker/worker_client\").WorkerClient,a=e(\"./css_completions\").CssCompletions,f=e(\"./behaviour/css\").CssBehaviour,l=e(\"./folding/cstyle\").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules=\"cStyle\",this.blockComment={start:\"/*\",end:\"*/\"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type==\"comment\")return r;var s=t.match(/^.*\\{\\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u([\"ace\"],\"ace/mode/css_worker\",\"Worker\");return t.attachToDocument(e.getDocument()),t.on(\"annotate\",function(t){e.setAnnotations(t.data)}),t.on(\"terminate\",function(){e.clearAnnotations()}),t},this.$id=\"ace/mode/css\",this.snippetFileId=\"ace/snippets/css\"}.call(c.prototype),t.Mode=c}),define(\"ace/mode/xml_highlight_rules\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text_highlight_rules\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"./text_highlight_rules\").TextHighlightRules,s=function(e){var t=\"[_:a-zA-Z\\u00c0-\\uffff][-_:.a-zA-Z0-9\\u00c0-\\uffff]*\";this.$rules={start:[{token:\"string.cdata.xml\",regex:\"<\\\\!\\\\[CDATA\\\\[\",next:\"cdata\"},{token:[\"punctuation.instruction.xml\",\"keyword.instruction.xml\"],regex:\"(<\\\\?)(\"+t+\")\",next:\"processing_instruction\"},{token:\"comment.start.xml\",regex:\"<\\\\!--\",next:\"comment\"},{token:[\"xml-pe.doctype.xml\",\"xml-pe.doctype.xml\"],regex:\"(<\\\\!)(DOCTYPE)(?=[\\\\s])\",next:\"doctype\",caseInsensitive:!0},{include:\"tag\"},{token:\"text.end-tag-open.xml\",regex:\"</\"},{token:\"text.tag-open.xml\",regex:\"<\"},{include:\"reference\"},{defaultToken:\"text.xml\"}],processing_instruction:[{token:\"entity.other.attribute-name.decl-attribute-name.xml\",regex:t},{token:\"keyword.operator.decl-attribute-equals.xml\",regex:\"=\"},{include:\"whitespace\"},{include:\"string\"},{token:\"punctuation.xml-decl.xml\",regex:\"\\\\?>\",next:\"start\"}],doctype:[{include:\"whitespace\"},{include:\"string\"},{token:\"xml-pe.doctype.xml\",regex:\">\",next:\"start\"},{token:\"xml-pe.xml\",regex:\"[-_a-zA-Z0-9:]+\"},{token:\"punctuation.int-subset\",regex:\"\\\\[\",push:\"int_subset\"}],int_subset:[{token:\"text.xml\",regex:\"\\\\s+\"},{token:\"punctuation.int-subset.xml\",regex:\"]\",next:\"pop\"},{token:[\"punctuation.markup-decl.xml\",\"keyword.markup-decl.xml\"],regex:\"(<\\\\!)(\"+t+\")\",push:[{token:\"text\",regex:\"\\\\s+\"},{token:\"punctuation.markup-decl.xml\",regex:\">\",next:\"pop\"},{include:\"string\"}]}],cdata:[{token:\"string.cdata.xml\",regex:\"\\\\]\\\\]>\",next:\"start\"},{token:\"text.xml\",regex:\"\\\\s+\"},{token:\"text.xml\",regex:\"(?:[^\\\\]]|\\\\](?!\\\\]>))+\"}],comment:[{token:\"comment.end.xml\",regex:\"-->\",next:\"start\"},{defaultToken:\"comment.xml\"}],reference:[{token:\"constant.language.escape.reference.xml\",regex:\"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\\\.-]+;)\"}],attr_reference:[{token:\"constant.language.escape.reference.attribute-value.xml\",regex:\"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\\\.-]+;)\"}],tag:[{token:[\"meta.tag.punctuation.tag-open.xml\",\"meta.tag.punctuation.end-tag-open.xml\",\"meta.tag.tag-name.xml\"],regex:\"(?:(<)|(</))((?:\"+t+\":)?\"+t+\")\",next:[{include:\"attributes\"},{token:\"meta.tag.punctuation.tag-close.xml\",regex:\"/?>\",next:\"start\"}]}],tag_whitespace:[{token:\"text.tag-whitespace.xml\",regex:\"\\\\s+\"}],whitespace:[{token:\"text.whitespace.xml\",regex:\"\\\\s+\"}],string:[{token:\"string.xml\",regex:\"'\",push:[{token:\"string.xml\",regex:\"'\",next:\"pop\"},{defaultToken:\"string.xml\"}]},{token:\"string.xml\",regex:'\"',push:[{token:\"string.xml\",regex:'\"',next:\"pop\"},{defaultToken:\"string.xml\"}]}],attributes:[{token:\"entity.other.attribute-name.xml\",regex:t},{token:\"keyword.operator.attribute-equals.xml\",regex:\"=\"},{include:\"tag_whitespace\"},{include:\"attribute_value\"}],attribute_value:[{token:\"string.attribute-value.xml\",regex:\"'\",push:[{token:\"string.attribute-value.xml\",regex:\"'\",next:\"pop\"},{include:\"attr_reference\"},{defaultToken:\"string.attribute-value.xml\"}]},{token:\"string.attribute-value.xml\",regex:'\"',push:[{token:\"string.attribute-value.xml\",regex:'\"',next:\"pop\"},{include:\"attr_reference\"},{defaultToken:\"string.attribute-value.xml\"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:[\"meta.tag.punctuation.tag-open.xml\",\"meta.tag.\"+n+\".tag-name.xml\"],regex:\"(<)(\"+n+\"(?=\\\\s|>|$))\",next:[{include:\"attributes\"},{token:\"meta.tag.punctuation.tag-close.xml\",regex:\"/?>\",next:t+\"start\"}]}),this.$rules[n+\"-end\"]=[{include:\"attributes\"},{token:\"meta.tag.punctuation.tag-close.xml\",regex:\"/?>\",next:\"start\",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:[\"meta.tag.punctuation.end-tag-open.xml\",\"meta.tag.\"+n+\".tag-name.xml\"],regex:\"(</)(\"+n+\"(?=\\\\s|>|$))\",next:n+\"-end\"},{token:\"string.cdata.xml\",regex:\"<\\\\!\\\\[CDATA\\\\[\"},{token:\"string.cdata.xml\",regex:\"\\\\]\\\\]>\"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),define(\"ace/mode/html_highlight_rules\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/lang\",\"ace/mode/css_highlight_rules\",\"ace/mode/javascript_highlight_rules\",\"ace/mode/xml_highlight_rules\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"../lib/lang\"),s=e(\"./css_highlight_rules\").CssHighlightRules,o=e(\"./javascript_highlight_rules\").JavaScriptHighlightRules,u=e(\"./xml_highlight_rules\").XmlHighlightRules,a=i.createMap({a:\"anchor\",button:\"form\",form:\"form\",img:\"image\",input:\"form\",label:\"form\",option:\"form\",script:\"script\",select:\"form\",textarea:\"form\",style:\"style\",table:\"table\",tbody:\"table\",td:\"table\",tfoot:\"table\",th:\"table\",tr:\"table\"}),f=function(){u.call(this),this.addRules({attributes:[{include:\"tag_whitespace\"},{token:\"entity.other.attribute-name.xml\",regex:\"[-_a-zA-Z0-9:.]+\"},{token:\"keyword.operator.attribute-equals.xml\",regex:\"=\",push:[{include:\"tag_whitespace\"},{token:\"string.unquoted.attribute-value.html\",regex:\"[^<>='\\\"`\\\\s]+\",next:\"pop\"},{token:\"empty\",regex:\"\",next:\"pop\"}]},{include:\"attribute_value\"}],tag:[{token:function(e,t){var n=a[t];return[\"meta.tag.punctuation.\"+(e==\"<\"?\"\":\"end-\")+\"tag-open.xml\",\"meta.tag\"+(n?\".\"+n:\"\")+\".tag-name.xml\"]},regex:\"(</?)([-_a-zA-Z0-9:.]+)\",next:\"tag_stuff\"}],tag_stuff:[{include:\"attributes\"},{token:\"meta.tag.punctuation.tag-close.xml\",regex:\"/?>\",next:\"start\"}]}),this.embedTagRules(s,\"css-\",\"style\"),this.embedTagRules((new o({jsx:!1})).getRules(),\"js-\",\"script\"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),define(\"ace/mode/folding/mixed\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/folding/fold_mode\"],function(e,t,n){\"use strict\";var r=e(\"../../lib/oop\"),i=e(\"./fold_mode\").FoldMode,s=t.FoldMode=function(e,t){this.defaultMode=e,this.subModes=t};r.inherits(s,i),function(){this.$getMode=function(e){typeof e!=\"string\"&&(e=e[0]);for(var t in this.subModes)if(e.indexOf(t)===0)return this.subModes[t];return null},this.$tryMode=function(e,t,n,r){var i=this.$getMode(e);return i?i.getFoldWidget(t,n,r):\"\"},this.getFoldWidget=function(e,t,n){return this.$tryMode(e.getState(n-1),e,t,n)||this.$tryMode(e.getState(n),e,t,n)||this.defaultMode.getFoldWidget(e,t,n)},this.getFoldWidgetRange=function(e,t,n){var r=this.$getMode(e.getState(n-1));if(!r||!r.getFoldWidget(e,t,n))r=this.$getMode(e.getState(n));if(!r||!r.getFoldWidget(e,t,n))r=this.defaultMode;return r.getFoldWidgetRange(e,t,n)}}.call(s.prototype)}),define(\"ace/mode/folding/html\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/folding/mixed\",\"ace/mode/folding/xml\",\"ace/mode/folding/cstyle\"],function(e,t,n){\"use strict\";var r=e(\"../../lib/oop\"),i=e(\"./mixed\").FoldMode,s=e(\"./xml\").FoldMode,o=e(\"./cstyle\").FoldMode,u=t.FoldMode=function(e,t){i.call(this,new s(e,t),{\"js-\":new o,\"css-\":new o})};r.inherits(u,i)}),define(\"ace/mode/html_completions\",[\"require\",\"exports\",\"module\",\"ace/token_iterator\"],function(e,t,n){\"use strict\";function f(e,t){return e.type.lastIndexOf(t+\".xml\")>-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,\"tag-name\"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,\"attribute-name\"))i=n.stepBackward();if(i)return i.value}var r=e(\"../token_iterator\").TokenIterator,i=[\"accesskey\",\"class\",\"contenteditable\",\"contextmenu\",\"dir\",\"draggable\",\"dropzone\",\"hidden\",\"id\",\"inert\",\"itemid\",\"itemprop\",\"itemref\",\"itemscope\",\"itemtype\",\"lang\",\"spellcheck\",\"style\",\"tabindex\",\"title\",\"translate\"],s=[\"onabort\",\"onblur\",\"oncancel\",\"oncanplay\",\"oncanplaythrough\",\"onchange\",\"onclick\",\"onclose\",\"oncontextmenu\",\"oncuechange\",\"ondblclick\",\"ondrag\",\"ondragend\",\"ondragenter\",\"ondragleave\",\"ondragover\",\"ondragstart\",\"ondrop\",\"ondurationchange\",\"onemptied\",\"onended\",\"onerror\",\"onfocus\",\"oninput\",\"oninvalid\",\"onkeydown\",\"onkeypress\",\"onkeyup\",\"onload\",\"onloadeddata\",\"onloadedmetadata\",\"onloadstart\",\"onmousedown\",\"onmousemove\",\"onmouseout\",\"onmouseover\",\"onmouseup\",\"onmousewheel\",\"onpause\",\"onplay\",\"onplaying\",\"onprogress\",\"onratechange\",\"onreset\",\"onscroll\",\"onseeked\",\"onseeking\",\"onselect\",\"onshow\",\"onstalled\",\"onsubmit\",\"onsuspend\",\"ontimeupdate\",\"onvolumechange\",\"onwaiting\"],o=i.concat(s),u={a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},abbr:{},address:{},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},article:{pubdate:1},aside:{},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},b:{},base:{href:1,target:1},bdi:{},bdo:{},blockquote:{cite:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},br:{},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},canvas:{width:1,height:1},caption:{},cite:{},code:{},col:{span:1},colgroup:{span:1},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},data:{},datalist:{},dd:{},del:{cite:1,datetime:1},details:{open:1},dfn:{},dialog:{open:1},div:{},dl:{},dt:{},em:{},embed:{src:1,height:1,width:1,type:1},fieldset:{disabled:1,form:1,name:1},figcaption:{},figure:{},footer:{},form:{\"accept-charset\":1,action:1,autocomplete:1,enctype:{\"multipart/form-data\":1,\"application/x-www-form-urlencoded\":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},head:{},header:{},hr:{},html:{manifest:1},i:{},iframe:{name:1,src:1,height:1,width:1,sandbox:{\"allow-same-origin\":1,\"allow-top-navigation\":1,\"allow-forms\":1,\"allow-scripts\":1},seamless:{seamless:1}},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,\"datetime-local\":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{\"application/x-www-form-urlencoded\":1,\"multipart/form-data\":1,\"text/plain\":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},ins:{cite:1,datetime:1},kbd:{},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},label:{form:1,\"for\":1},legend:{},li:{value:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{\"text/css\":1,\"image/png\":1,\"image/jpeg\":1,\"image/gif\":1},sizes:1},main:{},map:{name:1},mark:{},math:{},menu:{type:1,label:1},meta:{\"http-equiv\":{\"content-type\":1},name:{description:1,keywords:1},content:{\"text/html; charset=UTF-8\":1},charset:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},nav:{},noscript:{href:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},ol:{start:1,reversed:1},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},output:{\"for\":1,form:1,name:1},p:{},param:{name:1,value:1},pre:{},progress:{value:1,max:1},q:{cite:1},rp:{},rt:{},ruby:{},s:{},samp:{},script:{charset:1,type:{\"text/javascript\":1},src:1,defer:1,async:1},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},small:{},source:{src:1,type:1,media:1},span:{},strong:{},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},sub:{},sup:{},svg:{},table:{summary:1},tbody:{},td:{headers:1,rowspan:1,colspan:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},tfoot:{},th:{headers:1,rowspan:1,colspan:1,scope:1},thead:{},time:{datetime:1},title:{},tr:{},track:{kind:1,src:1,srclang:1,label:1,\"default\":1},section:{},summary:{},u:{},ul:{},\"var\":{},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},wbr:{}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,\"tag-name\")||f(i,\"tag-open\")||f(i,\"end-tag-open\"))return this.getTagCompletions(e,t,n,r);if(f(i,\"tag-whitespace\")||f(i,\"attribute-name\"))return this.getAttributeCompletions(e,t,n,r);if(f(i,\"attribute-value\"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[a-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:\"tag\",score:1e6}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'=\"$0\"',meta:\"attribute\",score:1e6}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]==\"object\"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:\"attribute value\",score:1e6}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=[\"Aacute;\",\"aacute;\",\"Acirc;\",\"acirc;\",\"acute;\",\"AElig;\",\"aelig;\",\"Agrave;\",\"agrave;\",\"alefsym;\",\"Alpha;\",\"alpha;\",\"amp;\",\"and;\",\"ang;\",\"Aring;\",\"aring;\",\"asymp;\",\"Atilde;\",\"atilde;\",\"Auml;\",\"auml;\",\"bdquo;\",\"Beta;\",\"beta;\",\"brvbar;\",\"bull;\",\"cap;\",\"Ccedil;\",\"ccedil;\",\"cedil;\",\"cent;\",\"Chi;\",\"chi;\",\"circ;\",\"clubs;\",\"cong;\",\"copy;\",\"crarr;\",\"cup;\",\"curren;\",\"Dagger;\",\"dagger;\",\"dArr;\",\"darr;\",\"deg;\",\"Delta;\",\"delta;\",\"diams;\",\"divide;\",\"Eacute;\",\"eacute;\",\"Ecirc;\",\"ecirc;\",\"Egrave;\",\"egrave;\",\"empty;\",\"emsp;\",\"ensp;\",\"Epsilon;\",\"epsilon;\",\"equiv;\",\"Eta;\",\"eta;\",\"ETH;\",\"eth;\",\"Euml;\",\"euml;\",\"euro;\",\"exist;\",\"fnof;\",\"forall;\",\"frac12;\",\"frac14;\",\"frac34;\",\"frasl;\",\"Gamma;\",\"gamma;\",\"ge;\",\"gt;\",\"hArr;\",\"harr;\",\"hearts;\",\"hellip;\",\"Iacute;\",\"iacute;\",\"Icirc;\",\"icirc;\",\"iexcl;\",\"Igrave;\",\"igrave;\",\"image;\",\"infin;\",\"int;\",\"Iota;\",\"iota;\",\"iquest;\",\"isin;\",\"Iuml;\",\"iuml;\",\"Kappa;\",\"kappa;\",\"Lambda;\",\"lambda;\",\"lang;\",\"laquo;\",\"lArr;\",\"larr;\",\"lceil;\",\"ldquo;\",\"le;\",\"lfloor;\",\"lowast;\",\"loz;\",\"lrm;\",\"lsaquo;\",\"lsquo;\",\"lt;\",\"macr;\",\"mdash;\",\"micro;\",\"middot;\",\"minus;\",\"Mu;\",\"mu;\",\"nabla;\",\"nbsp;\",\"ndash;\",\"ne;\",\"ni;\",\"not;\",\"notin;\",\"nsub;\",\"Ntilde;\",\"ntilde;\",\"Nu;\",\"nu;\",\"Oacute;\",\"oacute;\",\"Ocirc;\",\"ocirc;\",\"OElig;\",\"oelig;\",\"Ograve;\",\"ograve;\",\"oline;\",\"Omega;\",\"omega;\",\"Omicron;\",\"omicron;\",\"oplus;\",\"or;\",\"ordf;\",\"ordm;\",\"Oslash;\",\"oslash;\",\"Otilde;\",\"otilde;\",\"otimes;\",\"Ouml;\",\"ouml;\",\"para;\",\"part;\",\"permil;\",\"perp;\",\"Phi;\",\"phi;\",\"Pi;\",\"pi;\",\"piv;\",\"plusmn;\",\"pound;\",\"Prime;\",\"prime;\",\"prod;\",\"prop;\",\"Psi;\",\"psi;\",\"quot;\",\"radic;\",\"rang;\",\"raquo;\",\"rArr;\",\"rarr;\",\"rceil;\",\"rdquo;\",\"real;\",\"reg;\",\"rfloor;\",\"Rho;\",\"rho;\",\"rlm;\",\"rsaquo;\",\"rsquo;\",\"sbquo;\",\"Scaron;\",\"scaron;\",\"sdot;\",\"sect;\",\"shy;\",\"Sigma;\",\"sigma;\",\"sigmaf;\",\"sim;\",\"spades;\",\"sub;\",\"sube;\",\"sum;\",\"sup;\",\"sup1;\",\"sup2;\",\"sup3;\",\"supe;\",\"szlig;\",\"Tau;\",\"tau;\",\"there4;\",\"Theta;\",\"theta;\",\"thetasym;\",\"thinsp;\",\"THORN;\",\"thorn;\",\"tilde;\",\"times;\",\"trade;\",\"Uacute;\",\"uacute;\",\"uArr;\",\"uarr;\",\"Ucirc;\",\"ucirc;\",\"Ugrave;\",\"ugrave;\",\"uml;\",\"upsih;\",\"Upsilon;\",\"upsilon;\",\"Uuml;\",\"uuml;\",\"weierp;\",\"Xi;\",\"xi;\",\"Yacute;\",\"yacute;\",\"yen;\",\"Yuml;\",\"yuml;\",\"Zeta;\",\"zeta;\",\"zwj;\",\"zwnj;\"];return i.map(function(e){return{caption:e,snippet:e,meta:\"html entity\",score:1e6}})}}).call(h.prototype),t.HtmlCompletions=h}),define(\"ace/mode/html\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/lang\",\"ace/mode/text\",\"ace/mode/javascript\",\"ace/mode/css\",\"ace/mode/html_highlight_rules\",\"ace/mode/behaviour/xml\",\"ace/mode/folding/html\",\"ace/mode/html_completions\",\"ace/worker/worker_client\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"../lib/lang\"),s=e(\"./text\").Mode,o=e(\"./javascript\").Mode,u=e(\"./css\").Mode,a=e(\"./html_highlight_rules\").HtmlHighlightRules,f=e(\"./behaviour/xml\").XmlBehaviour,l=e(\"./folding/html\").FoldMode,c=e(\"./html_completions\").HtmlCompletions,h=e(\"../worker/worker_client\").WorkerClient,p=[\"area\",\"base\",\"br\",\"col\",\"embed\",\"hr\",\"img\",\"input\",\"keygen\",\"link\",\"meta\",\"menuitem\",\"param\",\"source\",\"track\",\"wbr\"],d=[\"li\",\"dt\",\"dd\",\"p\",\"rt\",\"rp\",\"optgroup\",\"option\",\"colgroup\",\"td\",\"th\"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({\"js-\":o,\"css-\":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:\"<!--\",end:\"-->\"},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h([\"ace\"],\"ace/mode/html_worker\",\"Worker\");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call(\"setOptions\",[{context:this.fragmentContext}]),t.on(\"error\",function(t){e.setAnnotations(t.data)}),t.on(\"terminate\",function(){e.clearAnnotations()}),t},this.$id=\"ace/mode/html\",this.snippetFileId=\"ace/snippets/html\"}.call(v.prototype),t.Mode=v});                (function() {\n+                    window.require([\"ace/mode/html\"], function(m) {\n+                        if (typeof module == \"object\" && typeof exports == \"object\" && module) {\n+                            module.exports = m;\n+                        }\n+                    });\n+                })();\n+            \n\\ No newline at end of file"}, {"sha": "e23a333bb77598c6abca3f1a727b5c359b57c29a", "filename": "data/interfaces/default/js/ace/theme-tautulli.js", "status": "added", "additions": 7, "deletions": 0, "changes": 7, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Ftheme-tautulli.js", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Ftheme-tautulli.js", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Ftheme-tautulli.js?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,7 @@\n+define(\"ace/theme/tautulli-css\",[\"require\",\"exports\",\"module\"],function(e,t,n){n.exports=\".ace-tautulli .ace_gutter {\\n  background: #282828;\\n  color: #999\\n}\\n\\n.ace-tautulli .ace_print-margin {\\n  width: 1px;\\n  background: #555\\n}\\n\\n.ace-tautulli {\\n  background-color: #333;\\n  color: #eee\\n}\\n\\n.ace-tautulli .ace_cursor {\\n  color: #eee\\n}\\n\\n.ace-tautulli .ace_marker-layer .ace_selection {\\n  background: #555\\n}\\n\\n.ace-tautulli.ace_multiselect .ace_selection.ace_start {\\n  box-shadow: 0 0 3px 0px #333;\\n}\\n\\n.ace-tautulli .ace_marker-layer .ace_step {\\n  background: rgb(102, 82, 0)\\n}\\n\\n.ace-tautulli .ace_marker-layer .ace_bracket {\\n  margin: -1px 0 0 -1px;\\n  border: 1px solid #444\\n}\\n\\n.ace-tautulli .ace_marker-layer .ace_active-line {\\n  background: #3B3B3B\\n}\\n\\n.ace-tautulli .ace_gutter-active-line {\\n  background-color: #3B3B3B\\n}\\n\\n.ace-tautulli .ace_marker-layer .ace_selected-word {\\n  border: 1px solid #555\\n}\\n\\n.ace-tautulli .ace_invisible {\\n  color: #444\\n}\\n\\n.ace-tautulli .ace_entity.ace_name.ace_tag,\\n.ace-tautulli .ace_keyword,\\n.ace-tautulli .ace_meta,\\n.ace-tautulli .ace_meta.ace_tag,\\n.ace-tautulli .ace_storage {\\n  color: #e9a049\\n}\\n\\n.ace-tautulli .ace_constant,\\n.ace-tautulli .ace_constant.ace_character,\\n.ace-tautulli .ace_constant.ace_character.ace_escape,\\n.ace-tautulli .ace_constant.ace_other,\\n.ace-tautulli .ace_support.ace_type {\\n  color: #68C1D8\\n}\\n\\n.ace-tautulli .ace_constant.ace_character.ace_escape {\\n  color: #B3E5B4\\n}\\n\\n.ace-tautulli .ace_constant.ace_language {\\n  color: #e9a049\\n}\\n\\n.ace-tautulli .ace_constant.ace_library,\\n.ace-tautulli .ace_string,\\n.ace-tautulli .ace_support.ace_constant {\\n  color: #8EC65F\\n}\\n\\n.ace-tautulli .ace_constant.ace_numeric {\\n  color: #7FC578\\n}\\n\\n.ace-tautulli .ace_invalid,\\n.ace-tautulli .ace_invalid.ace_deprecated {\\n  color: #eee;\\n  background-color: #c9302c\\n}\\n\\n.ace-tautulli .ace_fold {\\n  background-color: #e9a049;\\n  border-color: #eee\\n}\\n\\n.ace-tautulli .ace_comment,\\n.ace-tautulli .ace_meta {\\n  font-style: italic;\\n  color: #999\\n}\\n\\n.ace-tautulli .ace_entity.ace_other.ace_attribute-name {\\n  color: #EAF1A3\\n}\\n\\n.ace-tautulli .ace_indent-guide {\\n  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWOQkpLyZfD09PwPAAfYAnaStpHRAAAAAElFTkSuQmCC) right repeat-y\\n}\\n\\n.ace-tautulli .ace_indent-guide-active {\\n  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQIW2PQ1dX9zzBz5sz/ABCcBFFentLlAAAAAElFTkSuQmCC) right repeat-y;\\n}\\n\"}),define(\"ace/theme/tautulli\",[\"require\",\"exports\",\"module\",\"ace/theme/tautulli-css\",\"ace/lib/dom\"],function(e,t,n){t.isDark=!0,t.cssClass=\"ace-tautulli\",t.cssText=e(\"./tautulli-css\");var r=e(\"../lib/dom\");r.importCssString(t.cssText,t.cssClass,!1)});(function() {\n+    window.require([\"ace/theme/tautulli\"], function(m) {\n+        if (typeof module == \"object\" && typeof exports == \"object\" && module) {\n+            module.exports = m;\n+        }\n+    });\n+})();"}, {"sha": "4acc991ce9dd4c2854a55e9fc5d6e371b63416db", "filename": "data/interfaces/default/js/ace/worker-html.js", "status": "added", "additions": 1, "deletions": 0, "changes": 1, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Fworker-html.js", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Fworker-html.js", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fjs%2Face%2Fworker-html.js?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "9c0d68324bf7c9675667f627d2c49a5ddb6488f9", "filename": "data/interfaces/default/js/beautify-html.min.js", "status": "added", "additions": 2, "deletions": 0, "changes": 2, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Fbeautify-html.min.js", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Fbeautify-html.min.js", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fjs%2Fbeautify-html.min.js?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,2 @@\n+/* js-beautify v1.15.4 - https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.15.4/beautify-html.min.js */\n+!function(){!function(){\"use strict\";var _=[,,function(t){function i(t){this.__parent=t,this.__character_count=0,this.__indent_count=-1,this.__alignment_count=0,this.__wrap_point_index=0,this.__wrap_point_character_count=0,this.__wrap_point_indent_count=-1,this.__wrap_point_alignment_count=0,this.__items=[]}function n(t,e){this.__cache=[\"\"],this.__indent_size=t.indent_size,this.__indent_string=t.indent_char,t.indent_with_tabs||(this.__indent_string=new Array(t.indent_size+1).join(t.indent_char)),e=e||\"\",0<t.indent_level&&(e=new Array(t.indent_level+1).join(this.__indent_string)),this.__base_string=e,this.__base_string_length=e.length}function e(t,e){this.__indent_cache=new n(t,e),this.raw=!1,this._end_with_newline=t.end_with_newline,this.indent_size=t.indent_size,this.wrap_line_length=t.wrap_line_length,this.indent_empty_lines=t.indent_empty_lines,this.__lines=[],this.previous_line=null,this.current_line=null,this.next_line=new i(this),this.space_before_token=!1,this.non_breaking_space=!1,this.previous_token_wrapped=!1,this.__add_outputline()}i.prototype.clone_empty=function(){var t=new i(this.__parent);return t.set_indent(this.__indent_count,this.__alignment_count),t},i.prototype.item=function(t){return t<0?this.__items[this.__items.length+t]:this.__items[t]},i.prototype.has_match=function(t){for(var e=this.__items.length-1;0<=e;e--)if(this.__items[e].match(t))return!0;return!1},i.prototype.set_indent=function(t,e){this.is_empty()&&(this.__indent_count=t||0,this.__alignment_count=e||0,this.__character_count=this.__parent.get_indent_size(this.__indent_count,this.__alignment_count))},i.prototype._set_wrap_point=function(){this.__parent.wrap_line_length&&(this.__wrap_point_index=this.__items.length,this.__wrap_point_character_count=this.__character_count,this.__wrap_point_indent_count=this.__parent.next_line.__indent_count,this.__wrap_point_alignment_count=this.__parent.next_line.__alignment_count)},i.prototype._should_wrap=function(){return this.__wrap_point_index&&this.__character_count>this.__parent.wrap_line_length&&this.__wrap_point_character_count>this.__parent.next_line.__character_count},i.prototype._allow_wrap=function(){var t;return!!this._should_wrap()&&(this.__parent.add_new_line(),(t=this.__parent.current_line).set_indent(this.__wrap_point_indent_count,this.__wrap_point_alignment_count),t.__items=this.__items.slice(this.__wrap_point_index),this.__items=this.__items.slice(0,this.__wrap_point_index),t.__character_count+=this.__character_count-this.__wrap_point_character_count,this.__character_count=this.__wrap_point_character_count,\" \"===t.__items[0]&&(t.__items.splice(0,1),--t.__character_count),!0)},i.prototype.is_empty=function(){return 0===this.__items.length},i.prototype.last=function(){return this.is_empty()?null:this.__items[this.__items.length-1]},i.prototype.push=function(t){this.__items.push(t);var e=t.lastIndexOf(\"\\n\");-1!==e?this.__character_count=t.length-e:this.__character_count+=t.length},i.prototype.pop=function(){var t=null;return this.is_empty()||(t=this.__items.pop(),this.__character_count-=t.length),t},i.prototype._remove_indent=function(){0<this.__indent_count&&(--this.__indent_count,this.__character_count-=this.__parent.indent_size)},i.prototype._remove_wrap_indent=function(){0<this.__wrap_point_indent_count&&--this.__wrap_point_indent_count},i.prototype.trim=function(){for(;\" \"===this.last();)this.__items.pop(),--this.__character_count},i.prototype.toString=function(){var t=\"\";return this.is_empty()?this.__parent.indent_empty_lines&&(t=this.__parent.get_indent_string(this.__indent_count)):(t=this.__parent.get_indent_string(this.__indent_count,this.__alignment_count),t+=this.__items.join(\"\")),t},n.prototype.get_indent_size=function(t,e){var n=this.__base_string_length;return n=(n=t<0?0:n)+t*this.__indent_size+(e=e||0)},n.prototype.get_indent_string=function(t,e){var n=this.__base_string;return e=e||0,t<0&&(t=0,n=\"\"),e+=t*this.__indent_size,this.__ensure_cache(e),n+=this.__cache[e]},n.prototype.__ensure_cache=function(t){for(;t>=this.__cache.length;)this.__add_column()},n.prototype.__add_column=function(){var t,e=this.__cache.length,n=\"\";this.__indent_size&&e>=this.__indent_size&&(e-=(t=Math.floor(e/this.__indent_size))*this.__indent_size,n=new Array(t+1).join(this.__indent_string)),e&&(n+=new Array(e+1).join(\" \")),this.__cache.push(n)},e.prototype.__add_outputline=function(){this.previous_line=this.current_line,this.current_line=this.next_line.clone_empty(),this.__lines.push(this.current_line)},e.prototype.get_line_number=function(){return this.__lines.length},e.prototype.get_indent_string=function(t,e){return this.__indent_cache.get_indent_string(t,e)},e.prototype.get_indent_size=function(t,e){return this.__indent_cache.get_indent_size(t,e)},e.prototype.is_empty=function(){return!this.previous_line&&this.current_line.is_empty()},e.prototype.add_new_line=function(t){return!(this.is_empty()||!t&&this.just_added_newline())&&(this.raw||this.__add_outputline(),!0)},e.prototype.get_code=function(t){this.trim(!0);var e=this.current_line.pop(),e=(e&&(\"\\n\"===e[e.length-1]&&(e=e.replace(/\\n+$/g,\"\")),this.current_line.push(e)),this._end_with_newline&&this.__add_outputline(),this.__lines.join(\"\\n\"));return e=\"\\n\"!==t?e.replace(/[\\n]/g,t):e},e.prototype.set_wrap_point=function(){this.current_line._set_wrap_point()},e.prototype.set_indent=function(t,e){return this.next_line.set_indent(t=t||0,e=e||0),1<this.__lines.length?(this.current_line.set_indent(t,e),!0):(this.current_line.set_indent(),!1)},e.prototype.add_raw_token=function(t){for(var e=0;e<t.newlines;e++)this.__add_outputline();this.current_line.set_indent(-1),this.current_line.push(t.whitespace_before),this.current_line.push(t.text),this.space_before_token=!1,this.non_breaking_space=!1,this.previous_token_wrapped=!1},e.prototype.add_token=function(t){this.__add_space_before_token(),this.current_line.push(t),this.space_before_token=!1,this.non_breaking_space=!1,this.previous_token_wrapped=this.current_line._allow_wrap()},e.prototype.__add_space_before_token=function(){this.space_before_token&&!this.just_added_newline()&&(this.non_breaking_space||this.set_wrap_point(),this.current_line.push(\" \"))},e.prototype.remove_indent=function(t){for(var e=this.__lines.length;t<e;)this.__lines[t]._remove_indent(),t++;this.current_line._remove_wrap_indent()},e.prototype.trim=function(t){for(t=void 0!==t&&t,this.current_line.trim();t&&1<this.__lines.length&&this.current_line.is_empty();)this.__lines.pop(),this.current_line=this.__lines[this.__lines.length-1],this.current_line.trim();this.previous_line=1<this.__lines.length?this.__lines[this.__lines.length-2]:null},e.prototype.just_added_newline=function(){return this.current_line.is_empty()},e.prototype.just_added_blankline=function(){return this.is_empty()||this.current_line.is_empty()&&this.previous_line.is_empty()},e.prototype.ensure_empty_line_above=function(t,e){for(var n=this.__lines.length-2;0<=n;){var _=this.__lines[n];if(_.is_empty())break;if(0!==_.item(0).indexOf(t)&&_.item(-1)!==e){this.__lines.splice(n+1,0,new i(this)),this.previous_line=this.__lines[this.__lines.length-2];break}n--}},t.exports.Output=e},function(t){t.exports.Token=function(t,e,n,_){this.type=t,this.text=e,this.comments_before=null,this.newlines=n||0,this.whitespace_before=_||\"\",this.parent=null,this.next=null,this.previous=null,this.opened=null,this.closed=null,this.directives=null}},,,function(t){function e(t,e){this.raw_options=n(t,e),this.disabled=this._get_boolean(\"disabled\"),this.eol=this._get_characters(\"eol\",\"auto\"),this.end_with_newline=this._get_boolean(\"end_with_newline\"),this.indent_size=this._get_number(\"indent_size\",4),this.indent_char=this._get_characters(\"indent_char\",\" \"),this.indent_level=this._get_number(\"indent_level\"),this.preserve_newlines=this._get_boolean(\"preserve_newlines\",!0),this.max_preserve_newlines=this._get_number(\"max_preserve_newlines\",32786),this.preserve_newlines||(this.max_preserve_newlines=0),this.indent_with_tabs=this._get_boolean(\"indent_with_tabs\",\"\\t\"===this.indent_char),this.indent_with_tabs&&(this.indent_char=\"\\t\",1===this.indent_size&&(this.indent_size=4)),this.wrap_line_length=this._get_number(\"wrap_line_length\",this._get_number(\"max_char\")),this.indent_empty_lines=this._get_boolean(\"indent_empty_lines\"),this.templating=this._get_selection_list(\"templating\",[\"auto\",\"none\",\"angular\",\"django\",\"erb\",\"handlebars\",\"php\",\"smarty\"],[\"auto\"])}function n(t,e){var n,_={};for(n in t=i(t))n!==e&&(_[n]=t[n]);if(e&&t[e])for(n in t[e])_[n]=t[e][n];return _}function i(t){var e,n={};for(e in t)n[e.replace(/-/g,\"_\")]=t[e];return n}e.prototype._get_array=function(t,e){t=this.raw_options[t],e=e||[];return\"object\"==typeof t?null!==t&&\"function\"==typeof t.concat&&(e=t.concat()):\"string\"==typeof t&&(e=t.split(/[^a-zA-Z0-9_\\/\\-]+/)),e},e.prototype._get_boolean=function(t,e){t=this.raw_options[t];return void 0===t?!!e:!!t},e.prototype._get_characters=function(t,e){t=this.raw_options[t],e=e||\"\";return e=\"string\"==typeof t?t.replace(/\\\\r/,\"\\r\").replace(/\\\\n/,\"\\n\").replace(/\\\\t/,\"\\t\"):e},e.prototype._get_number=function(t,e){t=this.raw_options[t],e=parseInt(e,10),isNaN(e)&&(e=0),t=parseInt(t,10);return t=isNaN(t)?e:t},e.prototype._get_selection=function(t,e,n){n=this._get_selection_list(t,e,n);if(1!==n.length)throw new Error(\"Invalid Option Value: The option '\"+t+\"' can only be one of the following values:\\n\"+e+\"\\nYou passed in: '\"+this.raw_options[t]+\"'\");return n[0]},e.prototype._get_selection_list=function(t,e,n){if(!e||0===e.length)throw new Error(\"Selection list cannot be empty.\");if(n=n||[e[0]],!this._is_valid_selection(n,e))throw new Error(\"Invalid Default Value!\");n=this._get_array(t,n);if(this._is_valid_selection(n,e))return n;throw new Error(\"Invalid Option Value: The option '\"+t+\"' can contain only the following values:\\n\"+e+\"\\nYou passed in: '\"+this.raw_options[t]+\"'\")},e.prototype._is_valid_selection=function(t,e){return t.length&&e.length&&!t.some(function(t){return-1===e.indexOf(t)})},t.exports.Options=e,t.exports.normalizeOpts=i,t.exports.mergeOpts=n},,function(t){var _=RegExp.prototype.hasOwnProperty(\"sticky\");function e(t){this.__input=t||\"\",this.__input_length=this.__input.length,this.__position=0}e.prototype.restart=function(){this.__position=0},e.prototype.back=function(){0<this.__position&&--this.__position},e.prototype.hasNext=function(){return this.__position<this.__input_length},e.prototype.next=function(){var t=null;return this.hasNext()&&(t=this.__input.charAt(this.__position),this.__position+=1),t},e.prototype.peek=function(t){var e=null;return t=t||0,e=0<=(t+=this.__position)&&t<this.__input_length?this.__input.charAt(t):e},e.prototype.__match=function(t,e){t.lastIndex=e;var n=t.exec(this.__input);return!n||_&&t.sticky||n.index!==e&&(n=null),n},e.prototype.test=function(t,e){return e=e||0,0<=(e+=this.__position)&&e<this.__input_length&&!!this.__match(t,e)},e.prototype.testChar=function(t,e){e=this.peek(e);return t.lastIndex=0,null!==e&&t.test(e)},e.prototype.match=function(t){t=this.__match(t,this.__position);return t?this.__position+=t[0].length:t=null,t},e.prototype.read=function(t,e,n){var _,i=\"\";return t&&(_=this.match(t))&&(i+=_[0]),!e||!_&&t||(i+=this.readUntil(e,n)),i},e.prototype.readUntil=function(t,e){var n=this.__position,t=(t.lastIndex=this.__position,t.exec(this.__input));return t?(n=t.index,e&&(n+=t[0].length)):n=this.__input_length,e=this.__input.substring(this.__position,n),this.__position=n,e},e.prototype.readUntilAfter=function(t){return this.readUntil(t,!0)},e.prototype.get_regexp=function(t,e){var n=null,e=e&&_?\"y\":\"g\";return\"string\"==typeof t&&\"\"!==t?n=new RegExp(t,e):t&&(n=new RegExp(t.source,e)),n},e.prototype.get_literal_regexp=function(t){return RegExp(t.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\"))},e.prototype.peekUntilAfter=function(t){var e=this.__position,t=this.readUntilAfter(t);return this.__position=e,t},e.prototype.lookBack=function(t){var e=this.__position-1;return e>=t.length&&this.__input.substring(e-t.length,e).toLowerCase()===t},t.exports.InputScanner=e},function(t,e,n){function _(t,e){this._input=new i(t),this._options=e||{},this.__tokens=null,this._patterns={},this._patterns.whitespace=new a(this._input)}var i=n(8).InputScanner,s=n(3).Token,r=n(10).TokenStream,a=n(11).WhitespacePattern,o={START:\"TK_START\",RAW:\"TK_RAW\",EOF:\"TK_EOF\"};_.prototype.tokenize=function(){this._input.restart(),this.__tokens=new r,this._reset();for(var t,e=new s(o.START,\"\"),n=null,_=[],i=new r;e.type!==o.EOF;){for(t=this._get_next_token(e,n);this._is_comment(t);)i.add(t),t=this._get_next_token(e,n);i.isEmpty()||(t.comments_before=i,i=new r),t.parent=n,this._is_opening(t)?(_.push(n),n=t):n&&this._is_closing(t,n)&&((t.opened=n).closed=t,n=_.pop(),t.parent=n),(t.previous=e).next=t,this.__tokens.add(t),e=t}return this.__tokens},_.prototype._is_first_token=function(){return this.__tokens.isEmpty()},_.prototype._reset=function(){},_.prototype._get_next_token=function(t,e){this._readWhitespace();var n=this._input.read(/.+/g);return n?this._create_token(o.RAW,n):this._create_token(o.EOF,\"\")},_.prototype._is_comment=function(t){return!1},_.prototype._is_opening=function(t){return!1},_.prototype._is_closing=function(t,e){return!1},_.prototype._create_token=function(t,e){return new s(t,e,this._patterns.whitespace.newline_count,this._patterns.whitespace.whitespace_before_token)},_.prototype._readWhitespace=function(){return this._patterns.whitespace.read()},t.exports.Tokenizer=_,t.exports.TOKEN=o},function(t){function e(t){this.__tokens=[],this.__tokens_length=this.__tokens.length,this.__position=0,this.__parent_token=t}e.prototype.restart=function(){this.__position=0},e.prototype.isEmpty=function(){return 0===this.__tokens_length},e.prototype.hasNext=function(){return this.__position<this.__tokens_length},e.prototype.next=function(){var t=null;return this.hasNext()&&(t=this.__tokens[this.__position],this.__position+=1),t},e.prototype.peek=function(t){var e=null;return t=t||0,e=0<=(t+=this.__position)&&t<this.__tokens_length?this.__tokens[t]:e},e.prototype.add=function(t){this.__parent_token&&(t.parent=this.__parent_token),this.__tokens.push(t),this.__tokens_length+=1},t.exports.TokenStream=e},function(t,e,n){var _=n(12).Pattern;function i(t,e){_.call(this,t,e),e?this._line_regexp=this._input.get_regexp(e._line_regexp):this.__set_whitespace_patterns(\"\",\"\"),this.newline_count=0,this.whitespace_before_token=\"\"}(i.prototype=new _).__set_whitespace_patterns=function(t,e){this._match_pattern=this._input.get_regexp(\"[\"+(t+=\"\\\\t \")+(e+=\"\\\\n\\\\r\")+\"]+\",!0),this._newline_regexp=this._input.get_regexp(\"\\\\r\\\\n|[\"+e+\"]\")},i.prototype.read=function(){this.newline_count=0,this.whitespace_before_token=\"\";var t,e=this._input.read(this._match_pattern);return\" \"===e?this.whitespace_before_token=\" \":e&&(t=this.__split(this._newline_regexp,e),this.newline_count=t.length-1,this.whitespace_before_token=t[this.newline_count]),e},i.prototype.matching=function(t,e){var n=this._create();return n.__set_whitespace_patterns(t,e),n._update(),n},i.prototype._create=function(){return new i(this._input,this)},i.prototype.__split=function(t,e){for(var n=t.lastIndex=0,_=[],i=t.exec(e);i;)_.push(e.substring(n,i.index)),n=i.index+i[0].length,i=t.exec(e);return n<e.length?_.push(e.substring(n,e.length)):_.push(\"\"),_},t.exports.WhitespacePattern=i},function(t){function e(t,e){this._input=t,this._starting_pattern=null,this._match_pattern=null,this._until_pattern=null,this._until_after=!1,e&&(this._starting_pattern=this._input.get_regexp(e._starting_pattern,!0),this._match_pattern=this._input.get_regexp(e._match_pattern,!0),this._until_pattern=this._input.get_regexp(e._until_pattern),this._until_after=e._until_after)}e.prototype.read=function(){var t=this._input.read(this._starting_pattern);return this._starting_pattern&&!t||(t+=this._input.read(this._match_pattern,this._until_pattern,this._until_after)),t},e.prototype.read_match=function(){return this._input.match(this._match_pattern)},e.prototype.until_after=function(t){var e=this._create();return e._until_after=!0,e._until_pattern=this._input.get_regexp(t),e._update(),e},e.prototype.until=function(t){var e=this._create();return e._until_after=!1,e._until_pattern=this._input.get_regexp(t),e._update(),e},e.prototype.starting_with=function(t){var e=this._create();return e._starting_pattern=this._input.get_regexp(t,!0),e._update(),e},e.prototype.matching=function(t){var e=this._create();return e._match_pattern=this._input.get_regexp(t,!0),e._update(),e},e.prototype._create=function(){return new e(this._input,this)},e.prototype._update=function(){},t.exports.Pattern=e},function(t){function e(t,e){t=\"string\"==typeof t?t:t.source,e=\"string\"==typeof e?e:e.source,this.__directives_block_pattern=new RegExp(t+/ beautify( \\w+[:]\\w+)+ /.source+e,\"g\"),this.__directive_pattern=/ (\\w+)[:](\\w+)/g,this.__directives_end_ignore_pattern=new RegExp(t+/\\sbeautify\\signore:end\\s/.source+e,\"g\")}e.prototype.get_directives=function(t){if(!t.match(this.__directives_block_pattern))return null;for(var e={},n=(this.__directive_pattern.lastIndex=0,this.__directive_pattern.exec(t));n;)e[n[1]]=n[2],n=this.__directive_pattern.exec(t);return e},e.prototype.readIgnored=function(t){return t.readUntilAfter(this.__directives_end_ignore_pattern)},t.exports.Directives=e},function(t,e,n){var _=n(12).Pattern,i={django:!1,erb:!1,handlebars:!1,php:!1,smarty:!1,angular:!1};function s(t,e){_.call(this,t,e),this.__template_pattern=null,this._disabled=Object.assign({},i),this._excluded=Object.assign({},i),e&&(this.__template_pattern=this._input.get_regexp(e.__template_pattern),this._excluded=Object.assign(this._excluded,e._excluded),this._disabled=Object.assign(this._disabled,e._disabled));e=new _(t);this.__patterns={handlebars_comment:e.starting_with(/{{!--/).until_after(/--}}/),handlebars_unescaped:e.starting_with(/{{{/).until_after(/}}}/),handlebars:e.starting_with(/{{/).until_after(/}}/),php:e.starting_with(/<\\?(?:[= ]|php)/).until_after(/\\?>/),erb:e.starting_with(/<%[^%]/).until_after(/[^%]%>/),django:e.starting_with(/{%/).until_after(/%}/),django_value:e.starting_with(/{{/).until_after(/}}/),django_comment:e.starting_with(/{#/).until_after(/#}/),smarty:e.starting_with(/{(?=[^}{\\s\\n])/).until_after(/[^\\s\\n]}/),smarty_comment:e.starting_with(/{\\*/).until_after(/\\*}/),smarty_literal:e.starting_with(/{literal}/).until_after(/{\\/literal}/)}}(s.prototype=new _)._create=function(){return new s(this._input,this)},s.prototype._update=function(){this.__set_templated_pattern()},s.prototype.disable=function(t){var e=this._create();return e._disabled[t]=!0,e._update(),e},s.prototype.read_options=function(t){var e,n=this._create();for(e in i)n._disabled[e]=-1===t.templating.indexOf(e);return n._update(),n},s.prototype.exclude=function(t){var e=this._create();return e._excluded[t]=!0,e._update(),e},s.prototype.read=function(){for(var t=\"\",t=this._match_pattern?this._input.read(this._starting_pattern):this._input.read(this._starting_pattern,this.__template_pattern),e=this._read_template();e;)this._match_pattern?e+=this._input.read(this._match_pattern):e+=this._input.readUntil(this.__template_pattern),t+=e,e=this._read_template();return this._until_after&&(t+=this._input.readUntilAfter(this._until_pattern)),t},s.prototype.__set_templated_pattern=function(){var t=[];this._disabled.php||t.push(this.__patterns.php._starting_pattern.source),this._disabled.handlebars||t.push(this.__patterns.handlebars._starting_pattern.source),this._disabled.angular||t.push(this.__patterns.handlebars._starting_pattern.source),this._disabled.erb||t.push(this.__patterns.erb._starting_pattern.source),this._disabled.django||(t.push(this.__patterns.django._starting_pattern.source),t.push(this.__patterns.django_value._starting_pattern.source),t.push(this.__patterns.django_comment._starting_pattern.source)),this._disabled.smarty||t.push(this.__patterns.smarty._starting_pattern.source),this._until_pattern&&t.push(this._until_pattern.source),this.__template_pattern=this._input.get_regexp(\"(?:\"+t.join(\"|\")+\")\")},s.prototype._read_template=function(){var t,e=\"\",n=this._input.peek();return\"<\"===n?(t=this._input.peek(1),this._disabled.php||this._excluded.php||\"?\"!==t||(e=e||this.__patterns.php.read()),this._disabled.erb||this._excluded.erb||\"%\"!==t||(e=e||this.__patterns.erb.read())):\"{\"===n&&(this._disabled.handlebars||this._excluded.handlebars||(e=(e=(e=e||this.__patterns.handlebars_comment.read())||this.__patterns.handlebars_unescaped.read())||this.__patterns.handlebars.read()),this._disabled.django||(this._excluded.django||this._excluded.handlebars||(e=e||this.__patterns.django_value.read()),this._excluded.django||(e=(e=e||this.__patterns.django_comment.read())||this.__patterns.django.read())),this._disabled.smarty||this._disabled.django&&this._disabled.handlebars&&(e=(e=(e=e||this.__patterns.smarty_comment.read())||this.__patterns.smarty_literal.read())||this.__patterns.smarty.read())),e},t.exports.TemplatablePattern=s},,,,function(t,e,n){var i=n(19).Beautifier,_=n(20).Options;t.exports=function(t,e,n,_){return new i(t,e,n,_).beautify()},t.exports.defaultOptions=function(){return new _}},function(t,e,n){function p(t,e){this.indent_level=0,this.alignment_size=0,this.max_preserve_newlines=t.max_preserve_newlines,this.preserve_newlines=t.preserve_newlines,this._output=new _(t,e)}function s(t,e){var n=null,_=null;return e.closed?(\"script\"===t?n=\"text/javascript\":\"style\"===t&&(n=\"text/css\"),-1<(n=function(t){for(var e=null,n=t.next;n.type!==u.EOF&&t.closed!==n;){if(n.type===u.ATTRIBUTE&&\"type\"===n.text){n.next&&n.next.type===u.EQUALS&&n.next.next&&n.next.next.type===u.VALUE&&(e=n.next.next.text);break}n=n.next}return e}(e)||n).search(\"text/css\")?_=\"css\":-1<n.search(/module|((text|application|dojo)\\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\\+)?json|method|aspect))/)?_=\"javascript\":-1<n.search(/(text|application|dojo)\\/(x-)?(html)/)?_=\"html\":-1<n.search(/test\\/null/)&&(_=\"null\"),_):null}var i=n(20).Options,_=n(2).Output,h=n(21).Tokenizer,u=n(21).TOKEN,l=/\\r\\n|[\\r\\n]/,c=/\\r\\n|[\\r\\n]/g;p.prototype.current_line_has_match=function(t){return this._output.current_line.has_match(t)},p.prototype.set_space_before_token=function(t,e){this._output.space_before_token=t,this._output.non_breaking_space=e},p.prototype.set_wrap_point=function(){this._output.set_indent(this.indent_level,this.alignment_size),this._output.set_wrap_point()},p.prototype.add_raw_token=function(t){this._output.add_raw_token(t)},p.prototype.print_preserved_newlines=function(t){var e=0;t.type!==u.TEXT&&t.previous.type!==u.TEXT&&(e=t.newlines?1:0),this.preserve_newlines&&(e=t.newlines<this.max_preserve_newlines+1?t.newlines:this.max_preserve_newlines+1);for(var n=0;n<e;n++)this.print_newline(0<n);return 0!==e},p.prototype.traverse_whitespace=function(t){return!(!t.whitespace_before&&!t.newlines)&&(this.print_preserved_newlines(t)||(this._output.space_before_token=!0),!0)},p.prototype.previous_token_wrapped=function(){return this._output.previous_token_wrapped},p.prototype.print_newline=function(t){this._output.add_new_line(t)},p.prototype.print_token=function(t){t.text&&(this._output.set_indent(this.indent_level,this.alignment_size),this._output.add_token(t.text))},p.prototype.indent=function(){this.indent_level++},p.prototype.deindent=function(){0<this.indent_level&&(this.indent_level--,this._output.set_indent(this.indent_level,this.alignment_size))},p.prototype.get_full_indent=function(t){return(t=this.indent_level+(t||0))<1?\"\":this._output.get_indent_string(t)};function r(t,e){return-1!==e.indexOf(t)}function a(t,e,n){this.parent=t||null,this.tag=e?e.tag_name:\"\",this.indent_level=n||0,this.parser_token=e||null}function d(t){this._printer=t,this._current_frame=null}function g(t,e,n,_){this._source_text=t||\"\",e=e||{},this._js_beautify=n,this._css_beautify=_,this._tag_stack=null;t=new i(e,\"html\");this._options=t,this._is_wrap_attributes_force=\"force\"===this._options.wrap_attributes.substr(0,\"force\".length),this._is_wrap_attributes_force_expand_multiline=\"force-expand-multiline\"===this._options.wrap_attributes,this._is_wrap_attributes_force_aligned=\"force-aligned\"===this._options.wrap_attributes,this._is_wrap_attributes_aligned_multiple=\"aligned-multiple\"===this._options.wrap_attributes,this._is_wrap_attributes_preserve=\"preserve\"===this._options.wrap_attributes.substr(0,\"preserve\".length),this._is_wrap_attributes_preserve_aligned=\"preserve-aligned\"===this._options.wrap_attributes}d.prototype.get_parser_token=function(){return this._current_frame?this._current_frame.parser_token:null},d.prototype.record_tag=function(t){t=new a(this._current_frame,t,this._printer.indent_level);this._current_frame=t},d.prototype._try_pop_frame=function(t){var e=null;return t&&(e=t.parser_token,this._printer.indent_level=t.indent_level,this._current_frame=t.parent),e},d.prototype._get_frame=function(t,e){for(var n=this._current_frame;n&&-1===t.indexOf(n.tag);){if(e&&-1!==e.indexOf(n.tag)){n=null;break}n=n.parent}return n},d.prototype.try_pop=function(t,e){t=this._get_frame([t],e);return this._try_pop_frame(t)},d.prototype.indent_to_tag=function(t){t=this._get_frame(t);t&&(this._printer.indent_level=t.indent_level)},g.prototype.beautify=function(){if(this._options.disabled)return this._source_text;for(var t=this._source_text,e=this._options.eol,n=(\"auto\"===this._options.eol&&(e=\"\\n\",t&&l.test(t)&&(e=t.match(l)[0])),(t=t.replace(c,\"\\n\")).match(/^[\\t ]*/)[0]),_={text:\"\",type:\"\"},i=new f(this._options),s=new p(this._options,n),r=new h(t,this._options).tokenize(),a=(this._tag_stack=new d(s),null),o=r.next();o.type!==u.EOF;)o.type===u.TAG_OPEN||o.type===u.COMMENT?i=a=this._handle_tag_open(s,o,i,_,r):o.type===u.ATTRIBUTE||o.type===u.EQUALS||o.type===u.VALUE||o.type===u.TEXT&&!i.tag_complete?a=this._handle_inside_tag(s,o,i,_):o.type===u.TAG_CLOSE?a=this._handle_tag_close(s,o,i):o.type===u.TEXT?a=this._handle_text(s,o,i):o.type===u.CONTROL_FLOW_OPEN?a=this._handle_control_flow_open(s,o):o.type===u.CONTROL_FLOW_CLOSE?a=this._handle_control_flow_close(s,o):s.add_raw_token(o),_=a,o=r.next();return s._output.get_code(e)},g.prototype._handle_control_flow_open=function(t,e){var n={text:e.text,type:e.type};return t.set_space_before_token(e.newlines||\"\"!==e.whitespace_before,!0),e.newlines?t.print_preserved_newlines(e):t.set_space_before_token(e.newlines||\"\"!==e.whitespace_before,!0),t.print_token(e),t.indent(),n},g.prototype._handle_control_flow_close=function(t,e){var n={text:e.text,type:e.type};return t.deindent(),e.newlines?t.print_preserved_newlines(e):t.set_space_before_token(e.newlines||\"\"!==e.whitespace_before,!0),t.print_token(e),n},g.prototype._handle_tag_close=function(t,e,n){var _={text:e.text,type:e.type};return t.alignment_size=0,n.tag_complete=!0,t.set_space_before_token(e.newlines||\"\"!==e.whitespace_before,!0),n.is_unformatted?t.add_raw_token(e):(\"<\"===n.tag_start_char&&(t.set_space_before_token(\"/\"===e.text[0],!0),this._is_wrap_attributes_force_expand_multiline&&n.has_wrapped_attrs&&t.print_newline(!1)),t.print_token(e)),!n.indent_content||n.is_unformatted||n.is_content_unformatted||(t.indent(),n.indent_content=!1),n.is_inline_element||n.is_unformatted||n.is_content_unformatted||t.set_wrap_point(),_},g.prototype._handle_inside_tag=function(t,e,n,_){var i=n.has_wrapped_attrs,s={text:e.text,type:e.type};return t.set_space_before_token(e.newlines||\"\"!==e.whitespace_before,!0),n.is_unformatted?t.add_raw_token(e):\"{\"===n.tag_start_char&&e.type===u.TEXT?t.print_preserved_newlines(e)?(e.newlines=0,t.add_raw_token(e)):t.print_token(e):(e.type===u.ATTRIBUTE?t.set_space_before_token(!0):(e.type===u.EQUALS||e.type===u.VALUE&&e.previous.type===u.EQUALS)&&t.set_space_before_token(!1),e.type===u.ATTRIBUTE&&\"<\"===n.tag_start_char&&((this._is_wrap_attributes_preserve||this._is_wrap_attributes_preserve_aligned)&&(t.traverse_whitespace(e),i=i||0!==e.newlines),this._is_wrap_attributes_force&&n.attr_count>=this._options.wrap_attributes_min_attrs&&(_.type!==u.TAG_OPEN||this._is_wrap_attributes_force_expand_multiline)&&(t.print_newline(!1),i=!0)),t.print_token(e),i=i||t.previous_token_wrapped(),n.has_wrapped_attrs=i),s},g.prototype._handle_text=function(t,e,n){var _={text:e.text,type:\"TK_CONTENT\"};return n.custom_beautifier_name?this._print_custom_beatifier_text(t,e,n):n.is_unformatted||n.is_content_unformatted?t.add_raw_token(e):(t.traverse_whitespace(e),t.print_token(e)),_},g.prototype._print_custom_beatifier_text=function(t,e,n){var _=this;if(\"\"!==e.text){var i,s=e.text,r=1,a=\"\",o=\"\",r=(\"javascript\"===n.custom_beautifier_name&&\"function\"==typeof this._js_beautify?i=this._js_beautify:\"css\"===n.custom_beautifier_name&&\"function\"==typeof this._css_beautify?i=this._css_beautify:\"html\"===n.custom_beautifier_name&&(i=function(t,e){return new g(t,e,_._js_beautify,_._css_beautify).beautify()}),\"keep\"===this._options.indent_scripts?r=0:\"separate\"===this._options.indent_scripts&&(r=-t.indent_level),t.get_full_indent(r));if(s=s.replace(/\\n[ \\t]*$/,\"\"),\"html\"!==n.custom_beautifier_name&&\"<\"===s[0]&&s.match(/^(<!--|<!\\[CDATA\\[)/)){var n=/^(<!--[^\\n]*|<!\\[CDATA\\[)(\\n?)([ \\t\\n]*)([\\s\\S]*)(-->|]]>)$/.exec(s);if(!n)return void t.add_raw_token(e);a=r+n[1]+\"\\n\",s=n[4],n[5]&&(o=r+n[5]),s=s.replace(/\\n[ \\t]*$/,\"\"),(n[2]||-1!==n[3].indexOf(\"\\n\"))&&(n=n[3].match(/[ \\t]+$/))&&(e.whitespace_before=n[0])}s=s&&(i?((n=function(){this.eol=\"\\n\"}).prototype=this._options.raw_options,i(r+s,new n)):r+(s=(i=e.whitespace_before)?s.replace(new RegExp(\"\\n(\"+i+\")?\",\"g\"),\"\\n\"):s).replace(/\\n/g,\"\\n\"+r)),a&&(s=s?a+s+\"\\n\"+o:a+o),t.print_newline(!1),s&&(e.text=s,e.whitespace_before=\"\",e.newlines=0,t.add_raw_token(e),t.print_newline(!0))}},g.prototype._handle_tag_open=function(t,e,n,_,i){var s=this._get_tag_open_token(e);if(!n.is_unformatted&&!n.is_content_unformatted||n.is_empty_element||e.type!==u.TAG_OPEN||s.is_start_tag?(t.traverse_whitespace(e),this._set_tag_position(t,e,s,n,_),s.is_inline_element||t.set_wrap_point(),t.print_token(e)):(t.add_raw_token(e),s.start_tag_token=this._tag_stack.try_pop(s.tag_name)),s.is_start_tag&&this._is_wrap_attributes_force)for(var r,a=0;(r=i.peek(a)).type===u.ATTRIBUTE&&(s.attr_count+=1),a+=1,r.type!==u.EOF&&r.type!==u.TAG_CLOSE;);return(this._is_wrap_attributes_force_aligned||this._is_wrap_attributes_aligned_multiple||this._is_wrap_attributes_preserve_aligned)&&(s.alignment_size=e.text.length+1),s.tag_complete||s.is_unformatted||(t.alignment_size=s.alignment_size),s};var f=function(t,e,n){var _;this.parent=e||null,this.text=\"\",this.type=\"TK_TAG_OPEN\",this.tag_name=\"\",this.is_inline_element=!1,this.is_unformatted=!1,this.is_content_unformatted=!1,this.is_empty_element=!1,this.is_start_tag=!1,this.is_end_tag=!1,this.indent_content=!1,this.multiline_content=!1,this.custom_beautifier_name=null,this.start_tag_token=null,this.attr_count=0,this.has_wrapped_attrs=!1,this.alignment_size=0,this.tag_complete=!1,this.tag_start_char=\"\",this.tag_check=\"\",n?(this.tag_start_char=n.text[0],this.text=n.text,\"<\"===this.tag_start_char?(_=n.text.match(/^<([^\\s>]*)/),this.tag_check=_?_[1]:\"\"):(_=n.text.match(/^{{~?(?:[\\^]|#\\*?)?([^\\s}]+)/),this.tag_check=_?_[1]:\"\",(n.text.startsWith(\"{{#>\")||n.text.startsWith(\"{{~#>\"))&&\">\"===this.tag_check[0]&&(\">\"===this.tag_check&&null!==n.next?this.tag_check=n.next.text.split(\" \")[0]:this.tag_check=n.text.split(\">\")[1])),this.tag_check=this.tag_check.toLowerCase(),n.type===u.COMMENT&&(this.tag_complete=!0),this.is_start_tag=\"/\"!==this.tag_check.charAt(0),this.tag_name=this.is_start_tag?this.tag_check:this.tag_check.substr(1),this.is_end_tag=!this.is_start_tag||n.closed&&\"/>\"===n.closed.text,e=2,\"{\"===this.tag_start_char&&3<=this.text.length&&\"~\"===this.text.charAt(2)&&(e=3),this.is_end_tag=this.is_end_tag||\"{\"===this.tag_start_char&&(!t.indent_handlebars||this.text.length<3||/[^#\\^]/.test(this.text.charAt(e)))):this.tag_complete=!0},o=(g.prototype._get_tag_open_token=function(t){t=new f(this._options,this._tag_stack.get_parser_token(),t);return t.alignment_size=this._options.wrap_attributes_indent_size,t.is_end_tag=t.is_end_tag||r(t.tag_check,this._options.void_elements),t.is_empty_element=t.tag_complete||t.is_start_tag&&t.is_end_tag,t.is_unformatted=!t.tag_complete&&r(t.tag_check,this._options.unformatted),t.is_content_unformatted=!t.is_empty_element&&r(t.tag_check,this._options.content_unformatted),t.is_inline_element=r(t.tag_name,this._options.inline)||this._options.inline_custom_elements&&t.tag_name.includes(\"-\")||\"{\"===t.tag_start_char,t},g.prototype._set_tag_position=function(t,e,n,_,i){n.is_empty_element||(n.is_end_tag?n.start_tag_token=this._tag_stack.try_pop(n.tag_name):(this._do_optional_end_element(n)&&!n.is_inline_element&&t.print_newline(!1),this._tag_stack.record_tag(n),\"script\"!==n.tag_name&&\"style\"!==n.tag_name||n.is_unformatted||n.is_content_unformatted||(n.custom_beautifier_name=s(n.tag_check,e)))),r(n.tag_check,this._options.extra_liners)&&(t.print_newline(!1),t._output.just_added_blankline()||t.print_newline(!0)),n.is_empty_element?(\"{\"===n.tag_start_char&&\"else\"===n.tag_check&&(this._tag_stack.indent_to_tag([\"if\",\"unless\",\"each\"]),n.indent_content=!0,t.current_line_has_match(/{{#if/)||t.print_newline(!1)),\"!--\"===n.tag_name&&i.type===u.TAG_CLOSE&&_.is_end_tag&&-1===n.text.indexOf(\"\\n\")||(n.is_inline_element||n.is_unformatted||t.print_newline(!1),this._calcluate_parent_multiline(t,n))):n.is_end_tag?(e=!1,e=(e=n.start_tag_token&&n.start_tag_token.multiline_content)||!n.is_inline_element&&!(_.is_inline_element||_.is_unformatted)&&!(i.type===u.TAG_CLOSE&&n.start_tag_token===_)&&\"TK_CONTENT\"!==i.type,(e=n.is_content_unformatted||n.is_unformatted?!1:e)&&t.print_newline(!1)):(n.indent_content=!n.custom_beautifier_name,\"<\"===n.tag_start_char&&(\"html\"===n.tag_name?n.indent_content=this._options.indent_inner_html:\"head\"===n.tag_name?n.indent_content=this._options.indent_head_inner_html:\"body\"===n.tag_name&&(n.indent_content=this._options.indent_body_inner_html)),n.is_inline_element||n.is_unformatted||\"TK_CONTENT\"===i.type&&!n.is_content_unformatted||t.print_newline(!1),this._calcluate_parent_multiline(t,n))},g.prototype._calcluate_parent_multiline=function(t,e){!e.parent||!t._output.just_added_newline()||(e.is_inline_element||e.is_unformatted)&&e.parent.is_inline_element||(e.parent.multiline_content=!0)},[\"address\",\"article\",\"aside\",\"blockquote\",\"details\",\"div\",\"dl\",\"fieldset\",\"figcaption\",\"figure\",\"footer\",\"form\",\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"header\",\"hr\",\"main\",\"menu\",\"nav\",\"ol\",\"p\",\"pre\",\"section\",\"table\",\"ul\"]),m=[\"a\",\"audio\",\"del\",\"ins\",\"map\",\"noscript\",\"video\"];g.prototype._do_optional_end_element=function(t){var e,n=null;if(!t.is_empty_element&&t.is_start_tag&&t.parent)return\"body\"===t.tag_name?n=n||this._tag_stack.try_pop(\"head\"):\"li\"===t.tag_name?n=n||this._tag_stack.try_pop(\"li\",[\"ol\",\"ul\",\"menu\"]):\"dd\"===t.tag_name||\"dt\"===t.tag_name?n=(n=n||this._tag_stack.try_pop(\"dt\",[\"dl\"]))||this._tag_stack.try_pop(\"dd\",[\"dl\"]):\"p\"===t.parent.tag_name&&-1!==o.indexOf(t.tag_name)?(e=t.parent.parent)&&-1!==m.indexOf(e.tag_name)||(n=n||this._tag_stack.try_pop(\"p\")):\"rp\"===t.tag_name||\"rt\"===t.tag_name?n=(n=n||this._tag_stack.try_pop(\"rt\",[\"ruby\",\"rtc\"]))||this._tag_stack.try_pop(\"rp\",[\"ruby\",\"rtc\"]):\"optgroup\"===t.tag_name?n=n||this._tag_stack.try_pop(\"optgroup\",[\"select\"]):\"option\"===t.tag_name?n=n||this._tag_stack.try_pop(\"option\",[\"select\",\"datalist\",\"optgroup\"]):\"colgroup\"===t.tag_name?n=n||this._tag_stack.try_pop(\"caption\",[\"table\"]):\"thead\"===t.tag_name?n=(n=n||this._tag_stack.try_pop(\"caption\",[\"table\"]))||this._tag_stack.try_pop(\"colgroup\",[\"table\"]):\"tbody\"===t.tag_name||\"tfoot\"===t.tag_name?n=(n=(n=(n=n||this._tag_stack.try_pop(\"caption\",[\"table\"]))||this._tag_stack.try_pop(\"colgroup\",[\"table\"]))||this._tag_stack.try_pop(\"thead\",[\"table\"]))||this._tag_stack.try_pop(\"tbody\",[\"table\"]):\"tr\"===t.tag_name?n=(n=(n=n||this._tag_stack.try_pop(\"caption\",[\"table\"]))||this._tag_stack.try_pop(\"colgroup\",[\"table\"]))||this._tag_stack.try_pop(\"tr\",[\"table\",\"thead\",\"tbody\",\"tfoot\"]):\"th\"!==t.tag_name&&\"td\"!==t.tag_name||(n=(n=n||this._tag_stack.try_pop(\"td\",[\"table\",\"thead\",\"tbody\",\"tfoot\",\"tr\"]))||this._tag_stack.try_pop(\"th\",[\"table\",\"thead\",\"tbody\",\"tfoot\",\"tr\"])),t.parent=this._tag_stack.get_parser_token(),n},t.exports.Beautifier=g},function(t,e,n){var _=n(6).Options;function i(t){_.call(this,t,\"html\"),1===this.templating.length&&\"auto\"===this.templating[0]&&(this.templating=[\"django\",\"erb\",\"handlebars\",\"php\"]),this.indent_inner_html=this._get_boolean(\"indent_inner_html\"),this.indent_body_inner_html=this._get_boolean(\"indent_body_inner_html\",!0),this.indent_head_inner_html=this._get_boolean(\"indent_head_inner_html\",!0),this.indent_handlebars=this._get_boolean(\"indent_handlebars\",!0),this.wrap_attributes=this._get_selection(\"wrap_attributes\",[\"auto\",\"force\",\"force-aligned\",\"force-expand-multiline\",\"aligned-multiple\",\"preserve\",\"preserve-aligned\"]),this.wrap_attributes_min_attrs=this._get_number(\"wrap_attributes_min_attrs\",2),this.wrap_attributes_indent_size=this._get_number(\"wrap_attributes_indent_size\",this.indent_size),this.extra_liners=this._get_array(\"extra_liners\",[\"head\",\"body\",\"/html\"]),this.inline=this._get_array(\"inline\",[\"a\",\"abbr\",\"area\",\"audio\",\"b\",\"bdi\",\"bdo\",\"br\",\"button\",\"canvas\",\"cite\",\"code\",\"data\",\"datalist\",\"del\",\"dfn\",\"em\",\"embed\",\"i\",\"iframe\",\"img\",\"input\",\"ins\",\"kbd\",\"keygen\",\"label\",\"map\",\"mark\",\"math\",\"meter\",\"noscript\",\"object\",\"output\",\"progress\",\"q\",\"ruby\",\"s\",\"samp\",\"select\",\"small\",\"span\",\"strong\",\"sub\",\"sup\",\"svg\",\"template\",\"textarea\",\"time\",\"u\",\"var\",\"video\",\"wbr\",\"text\",\"acronym\",\"big\",\"strike\",\"tt\"]),this.inline_custom_elements=this._get_boolean(\"inline_custom_elements\",!0),this.void_elements=this._get_array(\"void_elements\",[\"area\",\"base\",\"br\",\"col\",\"embed\",\"hr\",\"img\",\"input\",\"keygen\",\"link\",\"menuitem\",\"meta\",\"param\",\"source\",\"track\",\"wbr\",\"!doctype\",\"?xml\",\"basefont\",\"isindex\"]),this.unformatted=this._get_array(\"unformatted\",[]),this.content_unformatted=this._get_array(\"content_unformatted\",[\"pre\",\"textarea\"]),this.unformatted_content_delimiter=this._get_characters(\"unformatted_content_delimiter\"),this.indent_scripts=this._get_selection(\"indent_scripts\",[\"normal\",\"keep\",\"separate\"])}i.prototype=new _,t.exports.Options=i},function(t,e,n){function _(t,e){i.call(this,t,e),this._current_tag_name=\"\";var t=new a(this._input).read_options(this._options),e=new o(this._input);this.__patterns={word:t.until(/[\\n\\r\\t <]/),word_control_flow_close_excluded:t.until(/[\\n\\r\\t <}]/),single_quote:t.until_after(/'/),double_quote:t.until_after(/\"/),attribute:t.until(/[\\n\\r\\t =>]|\\/>/),element_name:t.until(/[\\n\\r\\t >\\/]/),angular_control_flow_start:e.matching(/\\@[a-zA-Z]+[^({]*[({]/),handlebars_comment:e.starting_with(/{{!--/).until_after(/--}}/),handlebars:e.starting_with(/{{/).until_after(/}}/),handlebars_open:e.until(/[\\n\\r\\t }]/),handlebars_raw_close:e.until(/}}/),comment:e.starting_with(/<!--/).until_after(/-->/),cdata:e.starting_with(/<!\\[CDATA\\[/).until_after(/]]>/),conditional_comment:e.starting_with(/<!\\[/).until_after(/]>/),processing:e.starting_with(/<\\?/).until_after(/\\?>/)},this._options.indent_handlebars&&(this.__patterns.word=this.__patterns.word.exclude(\"handlebars\"),this.__patterns.word_control_flow_close_excluded=this.__patterns.word_control_flow_close_excluded.exclude(\"handlebars\")),this._unformatted_content_delimiter=null,this._options.unformatted_content_delimiter&&(t=this._input.get_literal_regexp(this._options.unformatted_content_delimiter),this.__patterns.unformatted_content_delimiter=e.matching(t).until_after(t))}var i=n(9).Tokenizer,s=n(9).TOKEN,r=n(13).Directives,a=n(14).TemplatablePattern,o=n(12).Pattern,p={TAG_OPEN:\"TK_TAG_OPEN\",TAG_CLOSE:\"TK_TAG_CLOSE\",CONTROL_FLOW_OPEN:\"TK_CONTROL_FLOW_OPEN\",CONTROL_FLOW_CLOSE:\"TK_CONTROL_FLOW_CLOSE\",ATTRIBUTE:\"TK_ATTRIBUTE\",EQUALS:\"TK_EQUALS\",VALUE:\"TK_VALUE\",COMMENT:\"TK_COMMENT\",TEXT:\"TK_TEXT\",UNKNOWN:\"TK_UNKNOWN\",START:s.START,RAW:s.RAW,EOF:s.EOF},h=new r(/<\\!--/,/-->/);(_.prototype=new i)._is_comment=function(t){return!1},_.prototype._is_opening=function(t){return t.type===p.TAG_OPEN||t.type===p.CONTROL_FLOW_OPEN},_.prototype._is_closing=function(t,e){return t.type===p.TAG_CLOSE&&e&&((\">\"===t.text||\"/>\"===t.text)&&\"<\"===e.text[0]||\"}}\"===t.text&&\"{\"===e.text[0]&&\"{\"===e.text[1])||t.type===p.CONTROL_FLOW_CLOSE&&\"}\"===t.text&&e.text.endsWith(\"{\")},_.prototype._reset=function(){this._current_tag_name=\"\"},_.prototype._get_next_token=function(t,e){this._readWhitespace();var n=this._input.peek();return null===n?this._create_token(p.EOF,\"\"):this._read_open_handlebars(n,e)||this._read_attribute(n,t,e)||this._read_close(n,e)||this._read_script_and_style(n,t)||this._read_control_flows(n,e)||this._read_raw_content(n,t,e)||this._read_content_word(n,e)||this._read_comment_or_cdata(n)||this._read_processing(n)||this._read_open(n,e)||this._create_token(p.UNKNOWN,this._input.next())},_.prototype._read_comment_or_cdata=function(t){var e=null,n=null,_=null;return\"<\"===t&&(\"!\"===this._input.peek(1)&&((n=this.__patterns.comment.read())?(_=h.get_directives(n))&&\"start\"===_.ignore&&(n+=h.readIgnored(this._input)):n=this.__patterns.cdata.read()),n&&((e=this._create_token(p.COMMENT,n)).directives=_)),e},_.prototype._read_processing=function(t){var e=null,n=null;return\"<\"===t&&(n=\"!\"!==(t=this._input.peek(1))&&\"?\"!==t?n:(n=this.__patterns.conditional_comment.read())||this.__patterns.processing.read())&&((e=this._create_token(p.COMMENT,n)).directives=null),e},_.prototype._read_open=function(t,e){var n=null,_=null;return e&&e.type!==p.CONTROL_FLOW_OPEN||\"<\"===t&&(n=this._input.next(),\"/\"===this._input.peek()&&(n+=this._input.next()),n+=this.__patterns.element_name.read(),_=this._create_token(p.TAG_OPEN,n)),_},_.prototype._read_open_handlebars=function(t,e){var n=null,_=null;return e&&e.type!==p.CONTROL_FLOW_OPEN||(this._options.templating.includes(\"angular\")||this._options.indent_handlebars)&&\"{\"===t&&\"{\"===this._input.peek(1)&&(_=this._options.indent_handlebars&&\"!\"===this._input.peek(2)?(n=(n=this.__patterns.handlebars_comment.read())||this.__patterns.handlebars.read(),this._create_token(p.COMMENT,n)):(n=this.__patterns.handlebars_open.read(),this._create_token(p.TAG_OPEN,n))),_},_.prototype._read_control_flows=function(t,e){var n=\"\",_=null;if(!this._options.templating.includes(\"angular\"))return _;if(\"@\"===t){if(\"\"===(n=this.__patterns.angular_control_flow_start.read()))return _;for(var i=n.endsWith(\"(\")?1:0,s=0;!n.endsWith(\"{\")||i!==s;){var r=this._input.next();if(null===r)break;\"(\"===r?i++:\")\"===r&&s++,n+=r}_=this._create_token(p.CONTROL_FLOW_OPEN,n)}else\"}\"===t&&e&&e.type===p.CONTROL_FLOW_OPEN&&(n=this._input.next(),_=this._create_token(p.CONTROL_FLOW_CLOSE,n));return _},_.prototype._read_close=function(t,e){var n=null,_=null;return e&&e.type===p.TAG_OPEN&&(\"<\"===e.text[0]&&(\">\"===t||\"/\"===t&&\">\"===this._input.peek(1))?(n=this._input.next(),\"/\"===t&&(n+=this._input.next()),_=this._create_token(p.TAG_CLOSE,n)):\"{\"===e.text[0]&&\"}\"===t&&\"}\"===this._input.peek(1)&&(this._input.next(),this._input.next(),_=this._create_token(p.TAG_CLOSE,\"}}\"))),_},_.prototype._read_attribute=function(t,e,n){var _=null;return n&&\"<\"===n.text[0]&&(\"=\"===t?_=this._create_token(p.EQUALS,this._input.next()):'\"'===t||\"'\"===t?(n=this._input.next(),n+=('\"'===t?this.__patterns.double_quote:this.__patterns.single_quote).read(),_=this._create_token(p.VALUE,n)):(t=this.__patterns.attribute.read())&&(_=e.type===p.EQUALS?this._create_token(p.VALUE,t):this._create_token(p.ATTRIBUTE,t))),_},_.prototype._is_content_unformatted=function(t){return-1===this._options.void_elements.indexOf(t)&&(-1!==this._options.content_unformatted.indexOf(t)||-1!==this._options.unformatted.indexOf(t))},_.prototype._read_raw_content=function(t,e,n){var _=\"\";return n&&\"{\"===n.text[0]?_=this.__patterns.handlebars_raw_close.read():e.type===p.TAG_CLOSE&&\"<\"===e.opened.text[0]&&\"/\"!==e.text[0]&&(n=e.opened.text.substr(1).toLowerCase(),this._is_content_unformatted(n)&&(_=this._input.readUntil(new RegExp(\"</\"+n+\"[\\\\n\\\\r\\\\t ]*?>\",\"ig\")))),_?this._create_token(p.TEXT,_):null},_.prototype._read_script_and_style=function(t,e){if(e.type===p.TAG_CLOSE&&\"<\"===e.opened.text[0]&&\"/\"!==e.text[0]){e=e.opened.text.substr(1).toLowerCase();if(\"script\"===e||\"style\"===e){t=this._read_comment_or_cdata(t);if(t)return t.type=p.TEXT,t;t=this._input.readUntil(new RegExp(\"</\"+e+\"[\\\\n\\\\r\\\\t ]*?>\",\"ig\"));if(t)return this._create_token(p.TEXT,t)}}return null},_.prototype._read_content_word=function(t,e){var n=\"\";return(n=(n=this._options.unformatted_content_delimiter&&t===this._options.unformatted_content_delimiter[0]?this.__patterns.unformatted_content_delimiter.read():n)||(e&&e.type===p.CONTROL_FLOW_OPEN?this.__patterns.word_control_flow_close_excluded:this.__patterns.word).read())?this._create_token(p.TEXT,n):null},t.exports.Tokenizer=_,t.exports.TOKEN=p}],i={};var t=function t(e){var n=i[e];if(void 0!==n)return n.exports;n=i[e]={exports:{}};return _[e](n,n.exports,t),n.exports}(18);e=t}();var e,n,_,i=e;\"function\"==typeof define&&define.amd?define([\"require\",\"./beautify\",\"./beautify-css\"],function(t){var n=t(\"./beautify\"),_=t(\"./beautify-css\");return{html_beautify:function(t,e){return i(t,e,n.js_beautify,_.css_beautify)}}}):\"undefined\"!=typeof exports?(n=require(\"./beautify.js\"),_=require(\"./beautify-css.js\"),exports.html_beautify=function(t,e){return i(t,e,n.js_beautify,_.css_beautify)}):\"undefined\"!=typeof window?window.html_beautify=function(t,e){return i(t,e,window.js_beautify,window.css_beautify)}:\"undefined\"!=typeof global&&(global.html_beautify=function(t,e){return i(t,e,global.js_beautify,global.css_beautify)})}();\n\\ No newline at end of file"}, {"sha": "2b5723003850a2cf3404251d8f2503956ecc7039", "filename": "data/interfaces/default/js/bootstrap-select.min.js", "status": "modified", "additions": 2, "deletions": 2, "changes": 4, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Fbootstrap-select.min.js", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Fbootstrap-select.min.js", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fjs%2Fbootstrap-select.min.js?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "cf9d8935a2558aef11ee79018a430787b5a64629", "filename": "data/interfaces/default/js/jquery.scrollbar.min.js", "status": "modified", "additions": 1, "deletions": 1, "changes": 2, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Fjquery.scrollbar.min.js", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Fjquery.scrollbar.min.js", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fjs%2Fjquery.scrollbar.min.js?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -13,4 +13,4 @@\n  * @url https://github.com/gromo/jquery.scrollbar/\n  *\n  */\n-!function(a,b){\"function\"==typeof define&&define.amd?define([\"jquery\"],b):b(\"undefined\"!=typeof exports?require(\"jquery\"):a.jQuery)}(this,function(a){\"use strict\";function h(b){if(c.webkit&&!b)return{height:0,width:0};if(!c.data.outer){var d={border:\"none\",\"box-sizing\":\"content-box\",height:\"200px\",margin:\"0\",padding:\"0\",width:\"200px\"};c.data.inner=a(\"<div>\").css(a.extend({},d)),c.data.outer=a(\"<div>\").css(a.extend({left:\"-1000px\",overflow:\"scroll\",position:\"absolute\",top:\"-1000px\"},d)).append(c.data.inner).appendTo(\"body\")}return c.data.outer.scrollLeft(1e3).scrollTop(1e3),{height:Math.ceil(c.data.outer.offset().top-c.data.inner.offset().top||0),width:Math.ceil(c.data.outer.offset().left-c.data.inner.offset().left||0)}}function i(){var a=h(!0);return!(a.height||a.width)}function j(a){var b=a.originalEvent;return(!b.axis||b.axis!==b.HORIZONTAL_AXIS)&&!b.wheelDeltaX}var b=!1,c={data:{index:0,name:\"scrollbar\"},firefox:/firefox/i.test(navigator.userAgent),macosx:/mac/i.test(navigator.platform),msedge:/edge\\/\\d+/i.test(navigator.userAgent),msie:/(msie|trident)/i.test(navigator.userAgent),mobile:/android|webos|iphone|ipad|ipod|blackberry/i.test(navigator.userAgent),overlay:null,scroll:null,scrolls:[],webkit:/webkit/i.test(navigator.userAgent)&&!/edge\\/\\d+/i.test(navigator.userAgent)};c.scrolls.add=function(a){this.remove(a).push(a)},c.scrolls.remove=function(b){for(;a.inArray(b,this)>=0;)this.splice(a.inArray(b,this),1);return this};var d={autoScrollSize:!0,autoUpdate:!0,debug:!1,disableBodyScroll:!1,duration:200,ignoreMobile:!1,ignoreOverlay:!1,isRtl:!1,scrollStep:30,showArrows:!1,stepScrolling:!0,scrollx:null,scrolly:null,onDestroy:null,onFallback:null,onInit:null,onScroll:null,onUpdate:null},e=function(b){c.scroll||(c.overlay=i(),c.scroll=h(),g(),a(window).resize(function(){var a=!1;if(c.scroll&&(c.scroll.height||c.scroll.width)){var b=h();b.height===c.scroll.height&&b.width===c.scroll.width||(c.scroll=b,a=!0)}g(a)})),this.container=b,this.namespace=\".scrollbar_\"+c.data.index++,this.options=a.extend({},d,window.jQueryScrollbarOptions||{}),this.scrollTo=null,this.scrollx={},this.scrolly={},b.data(c.data.name,this),c.scrolls.add(this)};e.prototype={destroy:function(){if(this.wrapper){this.container.removeData(c.data.name),c.scrolls.remove(this);var b=this.container.scrollLeft(),d=this.container.scrollTop();this.container.insertBefore(this.wrapper).css({height:\"\",margin:\"\",\"max-height\":\"\"}).removeClass(\"scroll-content scroll-scrollx_visible scroll-scrolly_visible\").off(this.namespace).scrollLeft(b).scrollTop(d),this.scrollx.scroll.removeClass(\"scroll-scrollx_visible\").find(\"div\").addBack().off(this.namespace),this.scrolly.scroll.removeClass(\"scroll-scrolly_visible\").find(\"div\").addBack().off(this.namespace),this.wrapper.remove(),a(document).add(\"body\").off(this.namespace),a.isFunction(this.options.onDestroy)&&this.options.onDestroy.apply(this,[this.container])}},init:function(b){var d=this,e=this.container,f=this.containerWrapper||e,g=this.namespace,h=a.extend(this.options,b||{}),i={x:this.scrollx,y:this.scrolly},k=this.wrapper,l={},m={scrollLeft:e.scrollLeft(),scrollTop:e.scrollTop()};if(c.mobile&&h.ignoreMobile||c.overlay&&h.ignoreOverlay||c.macosx&&!c.webkit)return a.isFunction(h.onFallback)&&h.onFallback.apply(this,[e]),!1;if(k)l={height:\"auto\",\"margin-bottom\":c.scroll.height*-1+\"px\",\"max-height\":\"\"},l[h.isRtl?\"margin-left\":\"margin-right\"]=c.scroll.width*-1+\"px\",f.css(l);else{if(this.wrapper=k=a(\"<div>\").addClass(\"scroll-wrapper\").addClass(e.attr(\"class\")).css(\"position\",\"absolute\"===e.css(\"position\")?\"absolute\":\"relative\").insertBefore(e).append(e),h.isRtl&&k.addClass(\"scroll--rtl\"),e.is(\"textarea\")&&(this.containerWrapper=f=a(\"<div>\").insertBefore(e).append(e),k.addClass(\"scroll-textarea\")),l={height:\"auto\",\"margin-bottom\":c.scroll.height*-1+\"px\",\"max-height\":\"\"},l[h.isRtl?\"margin-left\":\"margin-right\"]=c.scroll.width*-1+\"px\",f.addClass(\"scroll-content\").css(l),e.on(\"scroll\"+g,function(b){var f=e.scrollLeft(),g=e.scrollTop();if(h.isRtl)switch(!0){case c.firefox:f=Math.abs(f);case c.msedge||c.msie:f=e[0].scrollWidth-e[0].clientWidth-f}a.isFunction(h.onScroll)&&h.onScroll.call(d,{maxScroll:i.y.maxScrollOffset,scroll:g,size:i.y.size,visible:i.y.visible},{maxScroll:i.x.maxScrollOffset,scroll:f,size:i.x.size,visible:i.x.visible}),i.x.isVisible&&i.x.scroll.bar.css(\"left\",f*i.x.kx+\"px\"),i.y.isVisible&&i.y.scroll.bar.css(\"top\",g*i.y.kx+\"px\")}),k.on(\"scroll\"+g,function(){k.scrollTop(0).scrollLeft(0)}),h.disableBodyScroll){var n=function(a){j(a)?i.y.isVisible&&i.y.mousewheel(a):i.x.isVisible&&i.x.mousewheel(a)};k.on(\"MozMousePixelScroll\"+g,n),k.on(\"mousewheel\"+g,n),c.mobile&&k.on(\"touchstart\"+g,function(b){var c=b.originalEvent.touches&&b.originalEvent.touches[0]||b,d={pageX:c.pageX,pageY:c.pageY},f={left:e.scrollLeft(),top:e.scrollTop()};a(document).on(\"touchmove\"+g,function(a){var b=a.originalEvent.targetTouches&&a.originalEvent.targetTouches[0]||a;e.scrollLeft(f.left+d.pageX-b.pageX),e.scrollTop(f.top+d.pageY-b.pageY),a.preventDefault()}),a(document).on(\"touchend\"+g,function(){a(document).off(g)})})}a.isFunction(h.onInit)&&h.onInit.apply(this,[e])}a.each(i,function(b,f){var k=null,l=1,m=\"x\"===b?\"scrollLeft\":\"scrollTop\",n=h.scrollStep,o=function(){var a=e[m]();e[m](a+n),1==l&&a+n>=p&&(a=e[m]()),l==-1&&a+n<=p&&(a=e[m]()),e[m]()==a&&k&&k()},p=0;f.scroll||(f.scroll=d._getScroll(h[\"scroll\"+b]).addClass(\"scroll-\"+b),h.showArrows&&f.scroll.addClass(\"scroll-element_arrows_visible\"),f.mousewheel=function(a){if(!f.isVisible||\"x\"===b&&j(a))return!0;if(\"y\"===b&&!j(a))return i.x.mousewheel(a),!0;var c=a.originalEvent.wheelDelta*-1||a.originalEvent.detail,g=f.size-f.visible-f.offset;return c||(\"x\"===b&&a.originalEvent.deltaX?c=40*a.originalEvent.deltaX:\"y\"===b&&a.originalEvent.deltaY&&(c=40*a.originalEvent.deltaY)),(c>0&&p<g||c<0&&p>0)&&(p+=c,p<0&&(p=0),p>g&&(p=g),d.scrollTo=d.scrollTo||{},d.scrollTo[m]=p,setTimeout(function(){d.scrollTo&&(e.stop().animate(d.scrollTo,240,\"linear\",function(){p=e[m]()}),d.scrollTo=null)},1)),a.preventDefault(),!1},f.scroll.on(\"MozMousePixelScroll\"+g,f.mousewheel).on(\"mousewheel\"+g,f.mousewheel).on(\"mouseenter\"+g,function(){p=e[m]()}),f.scroll.find(\".scroll-arrow, .scroll-element_track\").on(\"mousedown\"+g,function(g){if(1!=g.which)return!0;l=1;var i={eventOffset:g[\"x\"===b?\"pageX\":\"pageY\"],maxScrollValue:f.size-f.visible-f.offset,scrollbarOffset:f.scroll.bar.offset()[\"x\"===b?\"left\":\"top\"],scrollbarSize:f.scroll.bar[\"x\"===b?\"outerWidth\":\"outerHeight\"]()},j=0,q=0;if(a(this).hasClass(\"scroll-arrow\")){if(l=a(this).hasClass(\"scroll-arrow_more\")?1:-1,n=h.scrollStep*l,p=l>0?i.maxScrollValue:0,h.isRtl)switch(!0){case c.firefox:p=l>0?0:i.maxScrollValue*-1;break;case c.msie||c.msedge:}}else l=i.eventOffset>i.scrollbarOffset+i.scrollbarSize?1:i.eventOffset<i.scrollbarOffset?-1:0,\"x\"===b&&h.isRtl&&(c.msie||c.msedge)&&(l*=-1),n=Math.round(.75*f.visible)*l,p=i.eventOffset-i.scrollbarOffset-(h.stepScrolling?1==l?i.scrollbarSize:0:Math.round(i.scrollbarSize/2)),p=e[m]()+p/f.kx;return d.scrollTo=d.scrollTo||{},d.scrollTo[m]=h.stepScrolling?e[m]()+n:p,h.stepScrolling&&(k=function(){p=e[m](),clearInterval(q),clearTimeout(j),j=0,q=0},j=setTimeout(function(){q=setInterval(o,40)},h.duration+100)),setTimeout(function(){d.scrollTo&&(e.animate(d.scrollTo,h.duration),d.scrollTo=null)},1),d._handleMouseDown(k,g)}),f.scroll.bar.on(\"mousedown\"+g,function(i){if(1!=i.which)return!0;var j=i[\"x\"===b?\"pageX\":\"pageY\"],k=e[m]();return f.scroll.addClass(\"scroll-draggable\"),a(document).on(\"mousemove\"+g,function(a){var d=parseInt((a[\"x\"===b?\"pageX\":\"pageY\"]-j)/f.kx,10);\"x\"===b&&h.isRtl&&(c.msie||c.msedge)&&(d*=-1),e[m](k+d)}),d._handleMouseDown(function(){f.scroll.removeClass(\"scroll-draggable\"),p=e[m]()},i)}))}),a.each(i,function(a,b){var c=\"scroll-scroll\"+a+\"_visible\",d=\"x\"==a?i.y:i.x;b.scroll.removeClass(c),d.scroll.removeClass(c),f.removeClass(c)}),a.each(i,function(b,c){a.extend(c,\"x\"==b?{offset:parseInt(e.css(\"left\"),10)||0,size:e.prop(\"scrollWidth\"),visible:k.width()}:{offset:parseInt(e.css(\"top\"),10)||0,size:e.prop(\"scrollHeight\"),visible:k.height()})}),this._updateScroll(\"x\",this.scrollx),this._updateScroll(\"y\",this.scrolly),a.isFunction(h.onUpdate)&&h.onUpdate.apply(this,[e]),a.each(i,function(a,b){var c=\"x\"===a?\"left\":\"top\",d=\"x\"===a?\"outerWidth\":\"outerHeight\",f=\"x\"===a?\"width\":\"height\",g=parseInt(e.css(c),10)||0,i=b.size,j=b.visible+g,k=b.scroll.size[d]()+(parseInt(b.scroll.size.css(c),10)||0);h.autoScrollSize&&(b.scrollbarSize=parseInt(k*j/i,10),b.scroll.bar.css(f,b.scrollbarSize+\"px\")),b.scrollbarSize=b.scroll.bar[d](),b.kx=(k-b.scrollbarSize)/(i-j)||1,b.maxScrollOffset=i-j}),e.scrollLeft(m.scrollLeft).scrollTop(m.scrollTop).trigger(\"scroll\")},_getScroll:function(b){var c={advanced:['<div class=\"scroll-element\">','<div class=\"scroll-element_corner\"></div>','<div class=\"scroll-arrow scroll-arrow_less\"></div>','<div class=\"scroll-arrow scroll-arrow_more\"></div>','<div class=\"scroll-element_outer\">','<div class=\"scroll-element_size\"></div>','<div class=\"scroll-element_inner-wrapper\">','<div class=\"scroll-element_inner scroll-element_track\">','<div class=\"scroll-element_inner-bottom\"></div>',\"</div>\",\"</div>\",'<div class=\"scroll-bar\">','<div class=\"scroll-bar_body\">','<div class=\"scroll-bar_body-inner\"></div>',\"</div>\",'<div class=\"scroll-bar_bottom\"></div>','<div class=\"scroll-bar_center\"></div>',\"</div>\",\"</div>\",\"</div>\"].join(\"\"),simple:['<div class=\"scroll-element\">','<div class=\"scroll-element_outer\">','<div class=\"scroll-element_size\"></div>','<div class=\"scroll-element_track\"></div>','<div class=\"scroll-bar\"></div>',\"</div>\",\"</div>\"].join(\"\")};return c[b]&&(b=c[b]),b||(b=c.simple),b=\"string\"==typeof b?a(b).appendTo(this.wrapper):a(b),a.extend(b,{bar:b.find(\".scroll-bar\"),size:b.find(\".scroll-element_size\"),track:b.find(\".scroll-element_track\")}),b},_handleMouseDown:function(b,c){var d=this.namespace;return a(document).on(\"blur\"+d,function(){a(document).add(\"body\").off(d),b&&b()}),a(document).on(\"dragstart\"+d,function(a){return a.preventDefault(),!1}),a(document).on(\"mouseup\"+d,function(){a(document).add(\"body\").off(d),b&&b()}),a(\"body\").on(\"selectstart\"+d,function(a){return a.preventDefault(),!1}),c&&c.preventDefault(),!1},_updateScroll:function(b,d){var e=this.container,f=this.containerWrapper||e,g=\"scroll-scroll\"+b+\"_visible\",h=\"x\"===b?this.scrolly:this.scrollx,i=parseInt(this.container.css(\"x\"===b?\"left\":\"top\"),10)||0,j=this.wrapper,k=d.size,l=d.visible+i;d.isVisible=k-l>1,d.isVisible?(d.scroll.addClass(g),h.scroll.addClass(g),f.addClass(g)):(d.scroll.removeClass(g),h.scroll.removeClass(g),f.removeClass(g)),\"y\"===b&&(e.is(\"textarea\")||k<l?f.css({height:l+c.scroll.height+\"px\",\"max-height\":\"none\"}):f.css({\"max-height\":l+c.scroll.height+\"px\"})),d.size==e.prop(\"scrollWidth\")&&h.size==e.prop(\"scrollHeight\")&&d.visible==j.width()&&h.visible==j.height()&&d.offset==(parseInt(e.css(\"left\"),10)||0)&&h.offset==(parseInt(e.css(\"top\"),10)||0)||(a.extend(this.scrollx,{offset:parseInt(e.css(\"left\"),10)||0,size:e.prop(\"scrollWidth\"),visible:j.width()}),a.extend(this.scrolly,{offset:parseInt(e.css(\"top\"),10)||0,size:this.container.prop(\"scrollHeight\"),visible:j.height()}),this._updateScroll(\"x\"===b?\"y\":\"x\",h))}};var f=e;a.fn.scrollbar=function(b,d){return\"string\"!=typeof b&&(d=b,b=\"init\"),\"undefined\"==typeof d&&(d=[]),a.isArray(d)||(d=[d]),this.not(\"body, .scroll-wrapper\").each(function(){var e=a(this),g=e.data(c.data.name);(g||\"init\"===b)&&(g||(g=new f(e)),g[b]&&g[b].apply(g,d))}),this},a.fn.scrollbar.options=d;var g=function(){var a=0,d=0;return function(e){var f,h,i,j,k,l,m;for(f=0;f<c.scrolls.length;f++)j=c.scrolls[f],h=j.container,i=j.options,k=j.wrapper,l=j.scrollx,m=j.scrolly,(e||i.autoUpdate&&k&&k.is(\":visible\")&&(h.prop(\"scrollWidth\")!=l.size||h.prop(\"scrollHeight\")!=m.size||k.width()!=l.visible||k.height()!=m.visible))&&(j.init(),i.debug&&(window.console&&console.log({scrollHeight:h.prop(\"scrollHeight\")+\":\"+j.scrolly.size,scrollWidth:h.prop(\"scrollWidth\")+\":\"+j.scrollx.size,visibleHeight:k.height()+\":\"+j.scrolly.visible,visibleWidth:k.width()+\":\"+j.scrollx.visible},!0),d++));b&&d>10?(window.console&&console.log(\"Scroll updates exceed 10\"),g=function(){}):(clearTimeout(a),a=setTimeout(g,300))}}();window.angular&&!function(a){a.module(\"jQueryScrollbar\",[]).provider(\"jQueryScrollbar\",function(){var b=d;return{setOptions:function(c){a.extend(b,c)},$get:function(){return{options:a.copy(b)}}}}).directive(\"jqueryScrollbar\",[\"jQueryScrollbar\",\"$parse\",function(a,b){return{restrict:\"AC\",link:function(c,d,e){var f=b(e.jqueryScrollbar),g=f(c);d.scrollbar(g||a.options).on(\"$destroy\",function(){d.scrollbar(\"destroy\")})}}}])}(window.angular)});\n\\ No newline at end of file\n+!function(a,b){\"function\"==typeof define&&define.amd?define([\"jquery\"],b):b(\"undefined\"!=typeof exports?require(\"jquery\"):a.jQuery)}(this,function(a){\"use strict\";function h(b){if(c.webkit&&!b)return{height:0,width:0};if(!c.data.outer){var d={border:\"none\",\"box-sizing\":\"content-box\",height:\"200px\",margin:\"0\",padding:\"0\",width:\"200px\"};c.data.inner=a(\"<div>\").css(a.extend({},d)),c.data.outer=a(\"<div>\").css(a.extend({left:\"-1000px\",overflow:\"scroll\",position:\"absolute\",top:\"-1000px\"},d)).append(c.data.inner).appendTo(\"body\")}return c.data.outer.scrollLeft(1e3).scrollTop(1e3),{height:Math.ceil(c.data.outer.offset().top-c.data.inner.offset().top||0),width:Math.ceil(c.data.outer.offset().left-c.data.inner.offset().left||0)}}function i(){var a=h(!0);return!(a.height||a.width)}function j(a){var b=a.originalEvent;return(!b.axis||b.axis!==b.HORIZONTAL_AXIS)&&!b.wheelDeltaX}var b=!1,c={data:{index:0,name:\"scrollbar\"},firefox:/firefox/i.test(navigator.userAgent),macosx:/mac/i.test(navigator.platform),msedge:/edge\\/\\d+/i.test(navigator.userAgent),msie:/(msie|trident)/i.test(navigator.userAgent),mobile:/android|webos|iphone|ipad|ipod|blackberry/i.test(navigator.userAgent),overlay:null,scroll:null,scrolls:[],webkit:/webkit/i.test(navigator.userAgent)&&!/edge\\/\\d+/i.test(navigator.userAgent)};c.scrolls.add=function(a){this.remove(a).push(a)},c.scrolls.remove=function(b){for(;a.inArray(b,this)>=0;)this.splice(a.inArray(b,this),1);return this};var d={autoScrollSize:!0,autoUpdate:!0,debug:!1,disableBodyScroll:!1,duration:200,ignoreMobile:!1,ignoreOverlay:!1,isRtl:!1,scrollStep:30,showArrows:!1,stepScrolling:!0,scrollx:null,scrolly:null,onDestroy:null,onFallback:null,onInit:null,onScroll:null,onUpdate:null},e=function(b){c.scroll||(c.overlay=i(),c.scroll=h(),g(),a(window).resize(function(){var a=!1;if(c.scroll&&(c.scroll.height||c.scroll.width)){var b=h();b.height===c.scroll.height&&b.width===c.scroll.width||(c.scroll=b,a=!0)}g(a)})),this.container=b,this.namespace=\".scrollbar_\"+c.data.index++,this.options=a.extend({},d,window.jQueryScrollbarOptions||{}),this.scrollTo=null,this.scrollx={},this.scrolly={},b.data(c.data.name,this),c.scrolls.add(this)};e.prototype={destroy:function(){if(this.wrapper){this.container.removeData(c.data.name),c.scrolls.remove(this);var b=this.container.scrollLeft(),d=this.container.scrollTop();this.container.insertBefore(this.wrapper).css({height:\"\",margin:\"\",\"max-height\":\"\"}).removeClass(\"scroll-content scroll-scrollx_visible scroll-scrolly_visible\").off(this.namespace).scrollLeft(b).scrollTop(d),this.scrollx.scroll.removeClass(\"scroll-scrollx_visible\").find(\"div\").addBack().off(this.namespace),this.scrolly.scroll.removeClass(\"scroll-scrolly_visible\").find(\"div\").addBack().off(this.namespace),this.wrapper.remove(),a(document).add(\"body\").off(this.namespace),a.isFunction(this.options.onDestroy)&&this.options.onDestroy.apply(this,[this.container])}},init:function(b){var d=this,e=this.container,f=this.containerWrapper||e,g=this.namespace,h=a.extend(this.options,b||{}),i={x:this.scrollx,y:this.scrolly},k=this.wrapper,l={},m={scrollLeft:e.scrollLeft(),scrollTop:e.scrollTop()};if(c.mobile&&h.ignoreMobile||c.overlay&&h.ignoreOverlay)return a.isFunction(h.onFallback)&&h.onFallback.apply(this,[e]),!1;if(k)l={height:\"auto\",\"margin-bottom\":c.scroll.height*-1+\"px\",\"max-height\":\"\"},l[h.isRtl?\"margin-left\":\"margin-right\"]=c.scroll.width*-1+\"px\",f.css(l);else{if(this.wrapper=k=a(\"<div>\").addClass(\"scroll-wrapper\").addClass(e.attr(\"class\")).css(\"position\",\"absolute\"===e.css(\"position\")?\"absolute\":\"relative\").insertBefore(e).append(e),h.isRtl&&k.addClass(\"scroll--rtl\"),e.is(\"textarea\")&&(this.containerWrapper=f=a(\"<div>\").insertBefore(e).append(e),k.addClass(\"scroll-textarea\")),l={height:\"auto\",\"margin-bottom\":c.scroll.height*-1+\"px\",\"max-height\":\"\"},l[h.isRtl?\"margin-left\":\"margin-right\"]=c.scroll.width*-1+\"px\",f.addClass(\"scroll-content\").css(l),e.on(\"scroll\"+g,function(b){var f=e.scrollLeft(),g=e.scrollTop();if(h.isRtl)switch(!0){case c.firefox:f=Math.abs(f);case c.msedge||c.msie:f=e[0].scrollWidth-e[0].clientWidth-f}a.isFunction(h.onScroll)&&h.onScroll.call(d,{maxScroll:i.y.maxScrollOffset,scroll:g,size:i.y.size,visible:i.y.visible},{maxScroll:i.x.maxScrollOffset,scroll:f,size:i.x.size,visible:i.x.visible}),i.x.isVisible&&i.x.scroll.bar.css(\"left\",f*i.x.kx+\"px\"),i.y.isVisible&&i.y.scroll.bar.css(\"top\",g*i.y.kx+\"px\")}),k.on(\"scroll\"+g,function(){k.scrollTop(0).scrollLeft(0)}),h.disableBodyScroll){var n=function(a){j(a)?i.y.isVisible&&i.y.mousewheel(a):i.x.isVisible&&i.x.mousewheel(a)};k.on(\"MozMousePixelScroll\"+g,n),k.on(\"mousewheel\"+g,n),c.mobile&&k.on(\"touchstart\"+g,function(b){var c=b.originalEvent.touches&&b.originalEvent.touches[0]||b,d={pageX:c.pageX,pageY:c.pageY},f={left:e.scrollLeft(),top:e.scrollTop()};a(document).on(\"touchmove\"+g,function(a){var b=a.originalEvent.targetTouches&&a.originalEvent.targetTouches[0]||a;e.scrollLeft(f.left+d.pageX-b.pageX),e.scrollTop(f.top+d.pageY-b.pageY),a.preventDefault()}),a(document).on(\"touchend\"+g,function(){a(document).off(g)})})}a.isFunction(h.onInit)&&h.onInit.apply(this,[e])}a.each(i,function(b,f){var k=null,l=1,m=\"x\"===b?\"scrollLeft\":\"scrollTop\",n=h.scrollStep,o=function(){var a=e[m]();e[m](a+n),1==l&&a+n>=p&&(a=e[m]()),l==-1&&a+n<=p&&(a=e[m]()),e[m]()==a&&k&&k()},p=0;f.scroll||(f.scroll=d._getScroll(h[\"scroll\"+b]).addClass(\"scroll-\"+b),h.showArrows&&f.scroll.addClass(\"scroll-element_arrows_visible\"),f.mousewheel=function(a){if(!f.isVisible||\"x\"===b&&j(a))return!0;if(\"y\"===b&&!j(a))return i.x.mousewheel(a),!0;var c=a.originalEvent.wheelDelta*-1||a.originalEvent.detail,g=f.size-f.visible-f.offset;return c||(\"x\"===b&&a.originalEvent.deltaX?c=40*a.originalEvent.deltaX:\"y\"===b&&a.originalEvent.deltaY&&(c=40*a.originalEvent.deltaY)),(c>0&&p<g||c<0&&p>0)&&(p+=c,p<0&&(p=0),p>g&&(p=g),d.scrollTo=d.scrollTo||{},d.scrollTo[m]=p,setTimeout(function(){d.scrollTo&&(e.stop().animate(d.scrollTo,240,\"linear\",function(){p=e[m]()}),d.scrollTo=null)},1)),a.preventDefault(),!1},f.scroll.on(\"MozMousePixelScroll\"+g,f.mousewheel).on(\"mousewheel\"+g,f.mousewheel).on(\"mouseenter\"+g,function(){p=e[m]()}),f.scroll.find(\".scroll-arrow, .scroll-element_track\").on(\"mousedown\"+g,function(g){if(1!=g.which)return!0;l=1;var i={eventOffset:g[\"x\"===b?\"pageX\":\"pageY\"],maxScrollValue:f.size-f.visible-f.offset,scrollbarOffset:f.scroll.bar.offset()[\"x\"===b?\"left\":\"top\"],scrollbarSize:f.scroll.bar[\"x\"===b?\"outerWidth\":\"outerHeight\"]()},j=0,q=0;if(a(this).hasClass(\"scroll-arrow\")){if(l=a(this).hasClass(\"scroll-arrow_more\")?1:-1,n=h.scrollStep*l,p=l>0?i.maxScrollValue:0,h.isRtl)switch(!0){case c.firefox:p=l>0?0:i.maxScrollValue*-1;break;case c.msie||c.msedge:}}else l=i.eventOffset>i.scrollbarOffset+i.scrollbarSize?1:i.eventOffset<i.scrollbarOffset?-1:0,\"x\"===b&&h.isRtl&&(c.msie||c.msedge)&&(l*=-1),n=Math.round(.75*f.visible)*l,p=i.eventOffset-i.scrollbarOffset-(h.stepScrolling?1==l?i.scrollbarSize:0:Math.round(i.scrollbarSize/2)),p=e[m]()+p/f.kx;return d.scrollTo=d.scrollTo||{},d.scrollTo[m]=h.stepScrolling?e[m]()+n:p,h.stepScrolling&&(k=function(){p=e[m](),clearInterval(q),clearTimeout(j),j=0,q=0},j=setTimeout(function(){q=setInterval(o,40)},h.duration+100)),setTimeout(function(){d.scrollTo&&(e.animate(d.scrollTo,h.duration),d.scrollTo=null)},1),d._handleMouseDown(k,g)}),f.scroll.bar.on(\"mousedown\"+g,function(i){if(1!=i.which)return!0;var j=i[\"x\"===b?\"pageX\":\"pageY\"],k=e[m]();return f.scroll.addClass(\"scroll-draggable\"),a(document).on(\"mousemove\"+g,function(a){var d=parseInt((a[\"x\"===b?\"pageX\":\"pageY\"]-j)/f.kx,10);\"x\"===b&&h.isRtl&&(c.msie||c.msedge)&&(d*=-1),e[m](k+d)}),d._handleMouseDown(function(){f.scroll.removeClass(\"scroll-draggable\"),p=e[m]()},i)}))}),a.each(i,function(a,b){var c=\"scroll-scroll\"+a+\"_visible\",d=\"x\"==a?i.y:i.x;b.scroll.removeClass(c),d.scroll.removeClass(c),f.removeClass(c)}),a.each(i,function(b,c){a.extend(c,\"x\"==b?{offset:parseInt(e.css(\"left\"),10)||0,size:e.prop(\"scrollWidth\"),visible:k.width()}:{offset:parseInt(e.css(\"top\"),10)||0,size:e.prop(\"scrollHeight\"),visible:k.height()})}),this._updateScroll(\"x\",this.scrollx),this._updateScroll(\"y\",this.scrolly),a.isFunction(h.onUpdate)&&h.onUpdate.apply(this,[e]),a.each(i,function(a,b){var c=\"x\"===a?\"left\":\"top\",d=\"x\"===a?\"outerWidth\":\"outerHeight\",f=\"x\"===a?\"width\":\"height\",g=parseInt(e.css(c),10)||0,i=b.size,j=b.visible+g,k=b.scroll.size[d]()+(parseInt(b.scroll.size.css(c),10)||0);h.autoScrollSize&&(b.scrollbarSize=parseInt(k*j/i,10),b.scroll.bar.css(f,b.scrollbarSize+\"px\")),b.scrollbarSize=b.scroll.bar[d](),b.kx=(k-b.scrollbarSize)/(i-j)||1,b.maxScrollOffset=i-j}),e.scrollLeft(m.scrollLeft).scrollTop(m.scrollTop).trigger(\"scroll\")},_getScroll:function(b){var c={advanced:['<div class=\"scroll-element\">','<div class=\"scroll-element_corner\"></div>','<div class=\"scroll-arrow scroll-arrow_less\"></div>','<div class=\"scroll-arrow scroll-arrow_more\"></div>','<div class=\"scroll-element_outer\">','<div class=\"scroll-element_size\"></div>','<div class=\"scroll-element_inner-wrapper\">','<div class=\"scroll-element_inner scroll-element_track\">','<div class=\"scroll-element_inner-bottom\"></div>',\"</div>\",\"</div>\",'<div class=\"scroll-bar\">','<div class=\"scroll-bar_body\">','<div class=\"scroll-bar_body-inner\"></div>',\"</div>\",'<div class=\"scroll-bar_bottom\"></div>','<div class=\"scroll-bar_center\"></div>',\"</div>\",\"</div>\",\"</div>\"].join(\"\"),simple:['<div class=\"scroll-element\">','<div class=\"scroll-element_outer\">','<div class=\"scroll-element_size\"></div>','<div class=\"scroll-element_track\"></div>','<div class=\"scroll-bar\"></div>',\"</div>\",\"</div>\"].join(\"\")};return c[b]&&(b=c[b]),b||(b=c.simple),b=\"string\"==typeof b?a(b).appendTo(this.wrapper):a(b),a.extend(b,{bar:b.find(\".scroll-bar\"),size:b.find(\".scroll-element_size\"),track:b.find(\".scroll-element_track\")}),b},_handleMouseDown:function(b,c){var d=this.namespace;return a(document).on(\"blur\"+d,function(){a(document).add(\"body\").off(d),b&&b()}),a(document).on(\"dragstart\"+d,function(a){return a.preventDefault(),!1}),a(document).on(\"mouseup\"+d,function(){a(document).add(\"body\").off(d),b&&b()}),a(\"body\").on(\"selectstart\"+d,function(a){return a.preventDefault(),!1}),c&&c.preventDefault(),!1},_updateScroll:function(b,d){var e=this.container,f=this.containerWrapper||e,g=\"scroll-scroll\"+b+\"_visible\",h=\"x\"===b?this.scrolly:this.scrollx,i=parseInt(this.container.css(\"x\"===b?\"left\":\"top\"),10)||0,j=this.wrapper,k=d.size,l=d.visible+i;d.isVisible=k-l>1,d.isVisible?(d.scroll.addClass(g),h.scroll.addClass(g),f.addClass(g)):(d.scroll.removeClass(g),h.scroll.removeClass(g),f.removeClass(g)),\"y\"===b&&(e.is(\"textarea\")||k<l?f.css({height:l+c.scroll.height+\"px\",\"max-height\":\"none\"}):f.css({\"max-height\":l+c.scroll.height+\"px\"})),d.size==e.prop(\"scrollWidth\")&&h.size==e.prop(\"scrollHeight\")&&d.visible==j.width()&&h.visible==j.height()&&d.offset==(parseInt(e.css(\"left\"),10)||0)&&h.offset==(parseInt(e.css(\"top\"),10)||0)||(a.extend(this.scrollx,{offset:parseInt(e.css(\"left\"),10)||0,size:e.prop(\"scrollWidth\"),visible:j.width()}),a.extend(this.scrolly,{offset:parseInt(e.css(\"top\"),10)||0,size:this.container.prop(\"scrollHeight\"),visible:j.height()}),this._updateScroll(\"x\"===b?\"y\":\"x\",h))}};var f=e;a.fn.scrollbar=function(b,d){return\"string\"!=typeof b&&(d=b,b=\"init\"),\"undefined\"==typeof d&&(d=[]),a.isArray(d)||(d=[d]),this.not(\"body, .scroll-wrapper\").each(function(){var e=a(this),g=e.data(c.data.name);(g||\"init\"===b)&&(g||(g=new f(e)),g[b]&&g[b].apply(g,d))}),this},a.fn.scrollbar.options=d;var g=function(){var a=0,d=0;return function(e){var f,h,i,j,k,l,m;for(f=0;f<c.scrolls.length;f++)j=c.scrolls[f],h=j.container,i=j.options,k=j.wrapper,l=j.scrollx,m=j.scrolly,(e||i.autoUpdate&&k&&k.is(\":visible\")&&(h.prop(\"scrollWidth\")!=l.size||h.prop(\"scrollHeight\")!=m.size||k.width()!=l.visible||k.height()!=m.visible))&&(j.init(),i.debug&&(window.console&&console.log({scrollHeight:h.prop(\"scrollHeight\")+\":\"+j.scrolly.size,scrollWidth:h.prop(\"scrollWidth\")+\":\"+j.scrollx.size,visibleHeight:k.height()+\":\"+j.scrolly.visible,visibleWidth:k.width()+\":\"+j.scrollx.visible},!0),d++));b&&d>10?(window.console&&console.log(\"Scroll updates exceed 10\"),g=function(){}):(clearTimeout(a),a=setTimeout(g,300))}}();window.angular&&!function(a){a.module(\"jQueryScrollbar\",[]).provider(\"jQueryScrollbar\",function(){var b=d;return{setOptions:function(c){a.extend(b,c)},$get:function(){return{options:a.copy(b)}}}}).directive(\"jqueryScrollbar\",[\"jQueryScrollbar\",\"$parse\",function(a,b){return{restrict:\"AC\",link:function(c,d,e){var f=b(e.jqueryScrollbar),g=f(c);d.scrollbar(g||a.options).on(\"$destroy\",function(){d.scrollbar(\"destroy\")})}}}])}(window.angular)});\n\\ No newline at end of file"}, {"sha": "477e613b4468fed727e0428bab68837dccbf848f", "filename": "data/interfaces/default/js/script.js", "status": "modified", "additions": 34, "deletions": 46, "changes": 80, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Fscript.js", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Fscript.js", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fjs%2Fscript.js?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -288,23 +288,10 @@ function isPrivateIP(ip_address) {\n }\n \n function humanTime(seconds) {\n-    var d = Math.floor(moment.duration(seconds, 'seconds').asDays());\n-    var h = Math.floor(moment.duration((seconds % 86400), 'seconds').asHours());\n-    var m = Math.round(moment.duration(((seconds % 86400) % 3600), 'seconds').asMinutes());\n-\n-    var text = '';\n-    if (d > 0) {\n-        text = '<h3>' + d + '</h3><p> day' + ((d > 1) ? 's' : '') + '</p>'\n-             + '<h3>' + h + '</h3><p> hr' + ((h > 1) ? 's' : '') + '</p>'\n-             + '<h3>' + m + '</h3><p> min' + ((m > 1) ? 's' : '') + '</p>';\n-    } else if (h > 0) {\n-        text = '<h3>' + h + '</h3><p> hr' + ((h > 1) ? 's' : '') + '</p>'\n-             + '<h3>' + m + '</h3><p> min' + ((m > 1) ? 's' : '') + '</p>';\n-    } else {\n-        text = '<h3>' + m + '</h3><p> min' + ((m > 1) ? 's' : '') + '</p>';\n+    if (seconds > 0) {\n+        return humanDuration(seconds * 1000).replaceAll(/(\\d+) (\\w+)/g, '<h3>$1</h3><p>$2</p>')\n     }\n-\n-    return text\n+    return \"<h3>0</h3><p>mins</p>\";\n }\n \n String.prototype.toProperCase = function () {\n@@ -325,21 +312,17 @@ function getPercent(value1, value2) {\n     return Math.round(percent)\n }\n \n-function millisecondsToMinutes(ms, roundToMinute) {\n+function millisecondsToHoursMinutes(ms) {\n     if (ms > 0) {\n-      var minutes = Math.floor(ms / 60000);\n-      var seconds = ((ms % 60000) / 1000).toFixed(0);\n-      if (roundToMinute) {\n-          return (seconds >= 30 ? (minutes + 1) : minutes);\n-      } else {\n-          return (seconds == 60 ? (minutes + 1) + \":00\" : minutes + \":\" + (seconds < 10 ? \"0\" : \"\") + seconds);\n-      }\n-    } else {\n-        if (roundToMinute) {\n-            return '0';\n-        } else {\n-            return '0:00';\n+        var hours = Math.floor(ms / 3600000);\n+        var minutes = Math.floor((ms % 3600000) / 60000);\n+        var seconds = Math.floor(((ms % 3600000) % 60000) / 1000);\n+        if (hours > 0) {\n+            return hours + \":\" + (minutes < 10 ? \"0\" : \"\") + minutes + \":\" + (seconds < 10 ? \"0\" : \"\") + seconds;\n         }\n+        return minutes + \":\" + (seconds < 10 ? \"0\" : \"\") + seconds;\n+    } else {\n+        return '0:00';\n     }\n }\n \n@@ -695,9 +678,9 @@ getPlexOAuthPin = function (clientID) {\n \n var polling = null;\n \n-function PlexOAuth(success, error, pre, clientID) {\n-    if (typeof pre === \"function\") {\n-        pre()\n+function PlexOAuth(successCallback, errorCallback, maxRetryCallback, pollingCallback, preFunction, clientID) {\n+    if (typeof preFunction === \"function\") {\n+        preFunction()\n     }\n     closePlexOAuthWindow();\n     plex_oauth_window = PopupCenter('', 'Plex-OAuth', 600, 700);\n@@ -723,41 +706,46 @@ function PlexOAuth(success, error, pre, clientID) {\n         }\n \n         plex_oauth_window.location = 'https://app.plex.tv/auth/#!?' + encodeData(oauth_params);\n-        polling = pin;\n+        polling = pin;  // Set pin to prevent polling from multiple popups\n+\n+        let maxPollCount = 120;  // 2 minutes at 1-second intervals\n \n         (function poll() {\n+            maxPollCount--;\n+\n             $.ajax({\n                 url: 'https://plex.tv/api/v2/pins/' + pin,\n                 type: 'GET',\n                 headers: x_plex_headers,\n                 success: function (data) {\n                     if (data.authToken){\n+                        polling = null;\n                         closePlexOAuthWindow();\n-                        if (typeof success === \"function\") {\n-                            success(data.authToken)\n+                        if (typeof successCallback === \"function\") {\n+                            successCallback(data.authToken)\n                         }\n                     }\n                 },\n-                error: function (jqXHR, textStatus, errorThrown) {\n-                    if (textStatus !== \"timeout\") {\n+                complete: function () {\n+                    if (maxPollCount <= 0) {\n                         closePlexOAuthWindow();\n-                        if (typeof error === \"function\") {\n-                            error()\n+                        if (typeof maxRetryCallback === \"function\") {\n+                            maxRetryCallback()\n                         }\n-                    }\n-                },\n-                complete: function () {\n-                    if (!plex_oauth_window.closed && polling === pin){\n+                    } else if (polling === pin) {\n                         setTimeout(function() {poll()}, 1000);\n+                        if (typeof pollingCallback === \"function\") {\n+                            pollingCallback(maxPollCount);\n+                        }\n                     }\n                 },\n-                timeout: 10000\n+                timeout: 1000\n             });\n         })();\n     }, function () {\n         closePlexOAuthWindow();\n-        if (typeof error === \"function\") {\n-            error()\n+        if (typeof errorCallback === \"function\") {\n+            errorCallback()\n         }\n     });\n }"}, {"sha": "bd316d4fbd08f078240035bf62c5cdad2df1c82d", "filename": "data/interfaces/default/js/tables/export_table.js", "status": "modified", "additions": 7, "deletions": 7, "changes": 14, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Ftables%2Fexport_table.js", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fjs%2Ftables%2Fexport_table.js", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fjs%2Ftables%2Fexport_table.js?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -99,11 +99,11 @@ export_table_options = {\n             \"data\": \"file_format\",\n             \"createdCell\": function (td, cellData, rowData, row, col) {\n                 if (cellData !== '') {\n-                    var images = '';\n-                    if (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level']) {\n-                        images = ' + images';\n+                    var resources = '';\n+                    if (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level'] || rowData['squareArt_level'] || rowData['theme_level']) {\n+                        resources = ' + resources';\n                     }\n-                    $(td).html(cellData + images);\n+                    $(td).html(cellData + resources);\n                 }\n             },\n             \"width\": \"7%\",\n@@ -161,14 +161,14 @@ export_table_options = {\n                 if (cellData === 1 && rowData['exists']) {\n                     var tooltip_title = '';\n                     var icon = '';\n-                    if (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level'] || rowData['individual_files']) {\n-                        tooltip_title = 'Zip Archive';\n+                    if (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level'] || rowData['squareArt_level'] || rowData['theme_level'] || rowData['individual_files']) {\n+                        tooltip_title = 'ZIP Archive';\n                         icon = 'fa-file-archive';\n                     } else {\n                         tooltip_title = rowData['file_format'].toUpperCase() + ' File';\n                         icon = 'fa-file-download';\n                     }\n-                    var icon = (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level'] || rowData['individual_files']) ? 'fa-file-archive' : 'fa-file-download';\n+                    var icon = (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level'] || rowData['squareArt_level'] || rowData['theme_level'] || rowData['individual_files']) ? 'fa-file-archive' : 'fa-file-download';\n                     $(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>');\n                 } else if (cellData === 0) {\n                     var percent = Math.min(getPercent(rowData['exported_items'], rowData['total_items']), 99)"}, {"sha": "d3f247170347577c9fc596e8de279b1fd821d287", "filename": "data/interfaces/default/login.html", "status": "modified", "additions": 7, "deletions": 1, "changes": 8, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Flogin.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Flogin.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Flogin.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -116,15 +116,21 @@\n <script>\n     var login_accordion = new Accordion($('#login-methods'), false, false);\n \n+    function OAuthPreFunction() {\n+        $('#sign-in-alert').hide();\n+    }\n     function OAuthSuccessCallback(authToken) {\n         signIn(true, authToken);\n     }\n     function OAuthErrorCallback() {\n         $('#sign-in-alert').text('Error communicating with Plex.tv.').show();\n     }\n+    function OAuthMaxRetryCallback() {\n+        $('#sign-in-alert').text('Authentication timed out. Please try again.').show();\n+    }\n \n     $('#sign-in-plex').click(function() {\n-        PlexOAuth(OAuthSuccessCallback, OAuthErrorCallback);\n+        PlexOAuth(OAuthSuccessCallback, OAuthErrorCallback, OAuthMaxRetryCallback, null, OAuthPreFunction);\n     });\n \n     $('#login-form').submit(function(event) {"}, {"sha": "2b868aa352155e378d85385584889ed192178925", "filename": "data/interfaces/default/newsletter_config.html", "status": "modified", "additions": 254, "deletions": 28, "changes": 282, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fnewsletter_config.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fnewsletter_config.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fnewsletter_config.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -9,6 +9,7 @@\n     email_notifiers = [{'id': 0, 'agent_label': 'New Email Configuration', 'friendly_name': ''}] + email_notifiers\n     other_notifiers = [{'id': 0, 'agent_label': 'Select a Notification Agent', 'friendly_name': ''}] + all_notifiers\n %>\n+\n <div class=\"modal-dialog\" role=\"document\">\n     <div class=\"modal-content\">\n         <div class=\"modal-header\">\n@@ -21,7 +22,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                     <ul class=\"nav nav-tabs list-unstyled\" role=\"tablist\">\n                         <li role=\"presentation\" class=\"active\"><a href=\"#tabs-newsletter_config\" aria-controls=\"tabs-newsletter_config\" role=\"tab\" data-toggle=\"tab\">Configuration</a></li>\n                         <li role=\"presentation\"><a href=\"#tabs-newsletter_saving_sending\" aria-controls=\"tabs-newsletter_saving_sending\" role=\"tab\" data-toggle=\"tab\">Saving & Sending</a></li>\n-                        <li role=\"presentation\"><a href=\"#tabs-newsletter_text\" aria-controls=\"tabs-newsletter_text\" role=\"tab\" data-toggle=\"tab\">Newsletter Text</a></li>\n+                        <li role=\"presentation\"><a id=\"newsletter-text-tab\" href=\"#tabs-newsletter_text\" aria-controls=\"tabs-newsletter_text\" role=\"tab\" data-toggle=\"tab\">Newsletter Text</a></li>\n                         <li role=\"presentation\"><a href=\"#tabs-test_newsletter\" aria-controls=\"tabs-test_newsletter\" role=\"tab\" data-toggle=\"tab\">Test Newsletter</a></li>\n                     </ul>\n                 </div>\n@@ -32,19 +33,19 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                 <div class=\"col-md-12\">\n                                     <div class=\"checkbox\" style=\"margin-bottom: 20px;\">\n                                         <label>\n-                                            <input type=\"checkbox\" data-id=\"active_value\" class=\"checkboxes\" value=\"1\" ${checked(newsletter['active'])}> Enable the Newsletter\n+                                            <input type=\"checkbox\" data-id=\"active_value\" class=\"checkboxes\" value=\"1\" autocomplete=\"off\" ${checked(newsletter['active'])}> Enable the Newsletter\n                                         </label>\n                                         <input type=\"hidden\" id=\"active_value\" name=\"active\" value=\"${newsletter['active']}\">\n                                     </div>\n                                     <div class=\"form-group\">\n                                         <label for=\"custom_cron\">Schedule</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <select class=\"form-control\" id=\"custom_cron\" name=\"newsletter_config_custom_cron\">\n+                                                <select class=\"form-control\" id=\"custom_cron\" name=\"newsletter_config_custom_cron\" autocomplete=\"off\">\n                                                     <option value=\"0\" ${'selected' if newsletter['config']['custom_cron'] == 0 else ''}>Simple</option>\n                                                     <option value=\"1\" ${'selected' if newsletter['config']['custom_cron'] == 1 else ''}>Custom</option>\n                                                 </select>\n-                                                <input type=\"text\" id=\"cron_value\" name=\"cron\" value=\"${newsletter['cron']}\" />\n+                                                <input type=\"text\" id=\"cron_value\" name=\"cron\" value=\"${newsletter['cron']}\" autocomplete=\"off\" />\n                                                 <div id=\"cron-widget\"></div>\n                                             </div>\n                                         </div>\n@@ -62,8 +63,8 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                             <div class=\"col-md-5\">\n                                                 <div class=\"input-group newsletter-time_frame\">\n                                                     <span class=\"input-group-addon form-control btn-dark inactive\">Last</span>\n-                                                    <input type=\"number\" class=\"form-control\" id=\"newsletter_config_time_frame\" name=\"newsletter_config_time_frame\" value=\"${newsletter['config']['time_frame']}\">\n-                                                    <select class=\"form-control\" id=\"newsletter_config_time_frame_units\" name=\"newsletter_config_time_frame_units\">\n+                                                    <input type=\"number\" class=\"form-control\" id=\"newsletter_config_time_frame\" name=\"newsletter_config_time_frame\" value=\"${newsletter['config']['time_frame']}\" autocomplete=\"off\">\n+                                                    <select class=\"form-control\" id=\"newsletter_config_time_frame_units\" name=\"newsletter_config_time_frame_units\" autocomplete=\"off\">\n                                                         <option value=\"months\" ${'selected' if newsletter['config']['time_frame_units'] == 'months' else ''}>months</option>\n                                                         <option value=\"days\" ${'selected' if newsletter['config']['time_frame_units'] == 'days' else ''}>days</option>\n                                                         <option value=\"hours\" ${'selected' if newsletter['config']['time_frame_units'] == 'hours' else ''}>hours</option>\n@@ -88,7 +89,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"${item['name']}\">${item['label']}</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <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 ''}>\n+                                                <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 ''}>\n                                             </div>\n                                         </div>\n                                         <p class=\"help-block\">${item['description'] | n}</p>\n@@ -98,7 +99,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"${item['name']}\">${item['label']}</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-3\">\n-                                                <input type=\"${item['input_type']}\" class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\" size=\"30\">\n+                                                <input type=\"${item['input_type']}\" class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\" size=\"30\" autocomplete=\"off\">\n                                             </div>\n                                         </div>\n                                         <p class=\"help-block\">${item['description'] | n}</p>\n@@ -116,7 +117,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                     % elif item['input_type'] == 'checkbox':\n                                     <div class=\"checkbox\">\n                                         <label>\n-                                            <input type=\"checkbox\" data-id=\"${item['name']}\" class=\"checkboxes\" value=\"1\" ${checked(item['value'])}> ${item['label']}\n+                                            <input type=\"checkbox\" data-id=\"${item['name']}\" class=\"checkboxes\" value=\"1\" autocomplete=\"off\" ${checked(item['value'])}> ${item['label']}\n                                         </label>\n                                         <p class=\"help-block\">${item['description'] | n}</p>\n                                         <input type=\"hidden\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\">\n@@ -126,7 +127,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"${item['name']}\">${item['label']}</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\">\n+                                                <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" autocomplete=\"off\">\n                                                     % for key, value in sorted(item['select_options'].items()):\n                                                     % if key == item['value']:\n                                                     <option value=\"${key}\" selected>${value}</option>\n@@ -144,7 +145,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"${item['name']}\">${item['label']}</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\">\n+                                                <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" autocomplete=\"off\">\n                                                     % if item['select_all']:\n                                                     <option value=\"select-all\">Select All</option>\n                                                     <option value=\"remove-all\">Remove All</option>\n@@ -178,7 +179,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"id_name\">Unique ID Name</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <input type=\"text\" class=\"form-control\" id=\"id_name\" name=\"id_name\" value=\"${newsletter['id_name']}\" size=\"30\">\n+                                                <input type=\"text\" class=\"form-control\" id=\"id_name\" name=\"id_name\" value=\"${newsletter['id_name']}\" size=\"30\" autocomplete=\"off\">\n                                             </div>\n                                         </div>\n                                         <p class=\"help-block\">\n@@ -191,7 +192,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"friendly_name\">Description</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <input type=\"text\" class=\"form-control\" id=\"friendly_name\" name=\"friendly_name\" value=\"${newsletter['friendly_name']}\" size=\"30\">\n+                                                <input type=\"text\" class=\"form-control\" id=\"friendly_name\" name=\"friendly_name\" value=\"${newsletter['friendly_name']}\" size=\"30\" autocomplete=\"off\">\n                                             </div>\n                                         </div>\n                                         <p class=\"help-block\">Optional: Enter a description to help identify this newsletter in the newsletters list.</p>\n@@ -205,7 +206,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                     <label>Saving</label>\n                                     <div class=\"checkbox\">\n                                         <label>\n-                                            <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\n+                                            <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\n                                         </label>\n                                         <p class=\"help-block\">Enable to save the newsletter HTML file without sending it to any notification agent.</p>\n                                         <input type=\"hidden\" id=\"newsletter_config_save_only\" name=\"newsletter_config_save_only\" value=\"${newsletter['config']['save_only']}\">\n@@ -214,7 +215,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"newsletter_config_filename\">HTML File Name</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <input type=\"text\" class=\"form-control\" id=\"newsletter_config_filename\" name=\"newsletter_config_filename\" value=\"${newsletter['config']['filename']}\" size=\"30\">\n+                                                <input type=\"text\" class=\"form-control\" id=\"newsletter_config_filename\" name=\"newsletter_config_filename\" value=\"${newsletter['config']['filename']}\" size=\"30\" autocomplete=\"off\">\n                                             </div>\n                                         </div>\n                                         <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>\n@@ -224,23 +225,23 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                     <label>Sending</label>\n                                     <div class=\"checkbox\">\n                                         <label>\n-                                            <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\n+                                            <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\n                                         </label>\n                                         <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>\n                                         <input type=\"hidden\" id=\"newsletter_config_formatted\" name=\"newsletter_config_formatted\" value=\"${newsletter['config']['formatted']}\">\n                                     </div>\n                                     <div class=\"form-group\" id=\"email_notifier_select\">\n                                         <div class=\"checkbox\">\n                                             <label>\n-                                                <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\n+                                                <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\n                                             </label>\n                                             <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>\n                                             <input type=\"hidden\" id=\"newsletter_config_threaded\" name=\"newsletter_config_threaded\" value=\"${newsletter['config']['threaded']}\">\n                                         </div>\n                                         <label for=\"newsletter_email_notifier_id\">Email Notification Agent</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <select class=\"form-control\" id=\"newsletter_email_notifier_id\" name=\"newsletter_email_notifier_id\">\n+                                                <select class=\"form-control\" id=\"newsletter_email_notifier_id\" name=\"newsletter_email_notifier_id\" autocomplete=\"off\">\n                                                     % for notifier in email_notifiers:\n                                                     <% selected = 'selected' if notifier['id'] == newsletter['email_config']['notifier_id'] else '' %>\n                                                     % if notifier['friendly_name']:\n@@ -263,7 +264,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"newsletter_config_notifier_id\">Notification Agent</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <select class=\"form-control\" id=\"newsletter_config_notifier_id\" name=\"newsletter_config_notifier_id\">\n+                                                <select class=\"form-control\" id=\"newsletter_config_notifier_id\" name=\"newsletter_config_notifier_id\" autocomplete=\"off\">\n                                                     % for notifier in other_notifiers:\n                                                     <% selected = 'selected' if notifier['id'] == newsletter['config']['notifier_id'] else '' %>\n                                                     % if notifier['friendly_name']:\n@@ -294,7 +295,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                             <label for=\"${item['name']}\">${item['label']}</label>\n                                             <div class=\"row\">\n                                                 <div class=\"col-md-12\">\n-                                                    <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 ''}>\n+                                                    <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 ''}>\n                                                 </div>\n                                             </div>\n                                             <p class=\"help-block\">${item['description'] | n}</p>\n@@ -304,7 +305,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                             <label for=\"${item['name']}\">${item['label']}</label>\n                                             <div class=\"row\">\n                                                 <div class=\"col-md-3\">\n-                                                    <input type=\"${item['input_type']}\" class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\" size=\"30\">\n+                                                    <input type=\"${item['input_type']}\" class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\" size=\"30\" autocomplete=\"off\">\n                                                 </div>\n                                             </div>\n                                             <p class=\"help-block\">${item['description'] | n}</p>\n@@ -322,7 +323,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         % elif item['input_type'] == 'checkbox' and item['name'] != 'newsletter_email_html_support':\n                                         <div class=\"checkbox\">\n                                             <label>\n-                                                <input type=\"checkbox\" data-id=\"${item['name']}\" class=\"checkboxes\" value=\"1\" ${checked(item['value'])}> ${item['label']}\n+                                                <input type=\"checkbox\" data-id=\"${item['name']}\" class=\"checkboxes\" value=\"1\" autocomplete=\"off\" ${checked(item['value'])}> ${item['label']}\n                                             </label>\n                                             <p class=\"help-block\">${item['description'] | n}</p>\n                                             <input type=\"hidden\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\">\n@@ -332,7 +333,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                             <label for=\"${item['name']}\">${item['label']}</label>\n                                             <div class=\"row\">\n                                                 <div class=\"col-md-12\">\n-                                                    <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\">\n+                                                    <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" autocomplete=\"off\">\n                                                         % for key, value in sorted(item['select_options'].items()):\n                                                         % if isinstance(value, list):\n                                                         <optgroup label=\"${key}\">\n@@ -354,7 +355,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                             <label for=\"${item['name']}\">${item['label']}</label>\n                                             <div class=\"row\">\n                                                 <div class=\"col-md-12\">\n-                                                    <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\">\n+                                                    <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" autocomplete=\"off\">\n                                                         <option value=\"select-all\">Select All</option>\n                                                         <option value=\"remove-all\">Remove All</option>\n                                                         % if isinstance(item['select_options'], dict):\n@@ -399,7 +400,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"subject\">Subject</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <input type=\"text\" class=\"form-control\" id=\"subject\" name=\"subject\" value=\"${newsletter['subject']}\" size=\"30\">\n+                                                <input type=\"text\" class=\"form-control\" id=\"subject\" name=\"subject\" value=\"${newsletter['subject']}\" size=\"30\" autocomplete=\"off\">\n                                             </div>\n                                         </div>\n                                         <p class=\"help-block\">\n@@ -410,7 +411,7 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"body\">Body</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <textarea class=\"form-control\"  id=\"body\" name=\"body\" data-autoresize>${newsletter['body']}</textarea>\n+                                                <textarea class=\"form-control\"  id=\"body\" name=\"body\" autocomplete=\"off\" data-autoresize>${newsletter['body']}</textarea>\n                                             </div>\n                                         </div>\n                                         <p class=\"help-block\">\n@@ -421,11 +422,25 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n                                         <label for=\"message\">Message</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <textarea class=\"form-control\"  id=\"message\" name=\"message\" data-autoresize>${newsletter['message']}</textarea>\n+                                                <div class=\"editor-container\">\n+                                                    <div class=\"editor-controls\">\n+                                                        <button type=\"button\" class=\"btn btn-dark btn-edit editor-control-btn\" id=\"format-code\" title=\"Beautify HTML Code\">\n+                                                            <i class=\"fa fa-magic\"></i>\n+                                                        </button>\n+                                                        <button type=\"button\" class=\"btn btn-dark btn-edit editor-control-btn\" id=\"toggle-wrap\" title=\"Toggle Word Wrap\">\n+                                                            <i class=\"fa fa-arrows-h\"></i>\n+                                                        </button>\n+                                                        <button type=\"button\" class=\"btn btn-dark btn-edit editor-control-btn\" id=\"toggle-gutter\" title=\"Toggle Gutter\">\n+                                                            <i class=\"fa fa-list-ol\"></i>\n+                                                        </button>\n+                                                    </div>\n+                                                    <div id=\"message-editor\" style=\"width: 100%; height: 300px;\"></div>\n+                                                    <textarea class=\"form-control\" id=\"message\" name=\"message\" autocomplete=\"off\" style=\"display: none;\">${newsletter['message']}</textarea>\n+                                                </div>\n                                             </div>\n                                         </div>\n                                         <p class=\"help-block\">\n-                                            Enter a custom message to include on the newsletter.\n+                                            Enter a custom message to include on the newsletter. Supports HTML with syntax highlighting.\n                                         </p>\n                                     </div>\n                                 </div>\n@@ -471,8 +486,214 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n </div>\n \n <script src=\"${http_root}js/jquery-cron-min.js\"></script>\n+<script src=\"${http_root}js/ace/ace.js\"></script>\n+<script src=\"${http_root}js/beautify-html.min.js\"></script>\n <script>\n \n+    // Initialize Ace Editor for the message field\n+    var messageEditor;\n+    \n+    function initMessageEditor() {\n+        if (messageEditor) {\n+            messageEditor.destroy();\n+        }\n+        \n+        try {\n+            // Check if Ace editor is available\n+            if (typeof ace === 'undefined') {\n+                throw new Error('Ace editor not loaded');\n+            }\n+            \n+            messageEditor = ace.edit('message-editor');\n+            messageEditor.setTheme('ace/theme/tautulli');\n+            messageEditor.session.setMode('ace/mode/html');\n+            messageEditor.setOptions({\n+                showPrintMargin: false,\n+                showGutter: true,\n+                fontSize: '14px',\n+                tabSize: 2,\n+                wrap: true,\n+                highlightActiveLine: true\n+            });\n+            \n+            // Set initial value from the hidden textarea\n+            var initialValue = $('#message').val() || '';\n+            messageEditor.setValue(initialValue);\n+            messageEditor.clearSelection();\n+            \n+            var session = messageEditor.getSession();\n+            \n+            // Sync Ace editor content with hidden textarea on change\n+            session.on('change', function() {\n+                $('#message').val(messageEditor.getValue());\n+            });\n+            \n+            // Filter out HTML annotations\n+            session.on('changeAnnotation', function () {\n+                var annotations = session.getAnnotations() || [], i = len = annotations.length;\n+                while (i--) {\n+                    if (/doctype first\\. Expected/.test(annotations[i].text)) {\n+                        annotations.splice(i, 1);\n+                    }\n+                    else if (/Unexpected End of file\\. Expected/.test(annotations[i].text)) {\n+                        annotations.splice(i, 1);\n+                    }\n+                }\n+                if (len > annotations.length) {\n+                    session.setAnnotations(annotations);\n+                }\n+            });\n+            \n+            // Resize editor to fit content\n+            messageEditor.resize();\n+            \n+            // Set up event handlers for editor controls\n+            setupEditorControls();\n+            \n+        } catch (error) {\n+            console.error('Failed to initialize Ace editor:', error);\n+            // Fallback to regular textarea if Ace editor fails\n+            $('#message-editor').hide();\n+            $('.editor-controls').hide();\n+            $('.editor-container').css({\n+                'border': 'none',\n+            });\n+            $('#message').show().removeAttr('style').css({\n+                'height': '300px',\n+                'font-family': 'Monaco, Menlo, Ubuntu Mono, monospace'\n+            });\n+        }\n+    }\n+    \n+    function setupEditorControls() {\n+        // Format code button\n+        $('#format-code').off('click').on('click', function() {\n+            if (messageEditor) {\n+                var $btn = $(this);\n+                var originalIcon = $btn.find('i').attr('class');\n+                var originalTitle = $btn.attr('title');\n+                \n+                // Show loading state\n+                $btn.find('i').attr('class', 'fa fa-spinner fa-spin');\n+                $btn.attr('title', 'Formatting...');\n+                $btn.prop('disabled', true);\n+                \n+                try {\n+                    var content = messageEditor.getValue();\n+                    if (content.trim()) {\n+                        content = formatHTML(content);\n+                        messageEditor.setValue(content);\n+                        messageEditor.clearSelection();\n+                        \n+                        // Show success feedback\n+                        $btn.find('i').attr('class', 'fa fa-check');\n+                        $btn.attr('title', 'Code formatted successfully!');\n+                        setTimeout(function() {\n+                            $btn.find('i').attr('class', originalIcon);\n+                            $btn.attr('title', originalTitle);\n+                        }, 1500);\n+                    } else {\n+                        // Show warning for empty content\n+                        $btn.find('i').attr('class', 'fa fa-exclamation-triangle');\n+                        $btn.attr('title', 'No content to format');\n+                        setTimeout(function() {\n+                            $btn.find('i').attr('class', originalIcon);\n+                            $btn.attr('title', originalTitle);\n+                        }, 1500);\n+                    }\n+                } catch (error) {\n+                    console.error('Error formatting code:', error);\n+                    // Show error state\n+                    $btn.find('i').attr('class', 'fa fa-times');\n+                    $btn.attr('title', 'Formatting failed');\n+                    setTimeout(function() {\n+                        $btn.find('i').attr('class', originalIcon);\n+                        $btn.attr('title', originalTitle);\n+                    }, 2000);\n+                } finally {\n+                    $btn.prop('disabled', false);\n+                }\n+            }\n+        });\n+        \n+        // Toggle word wrap button\n+        $('#toggle-wrap').off('click').on('click', function() {\n+            if (messageEditor) {\n+                var currentWrap = messageEditor.getSession().getUseWrapMode();\n+                messageEditor.getSession().setUseWrapMode(!currentWrap);\n+            }\n+        });\n+        \n+        // Toggle gutter button\n+        $('#toggle-gutter').off('click').on('click', function() {\n+            if (messageEditor) {\n+                var currentGutter = messageEditor.renderer.getShowGutter();\n+                messageEditor.renderer.setShowGutter(!currentGutter);\n+            }\n+        });\n+    }\n+    \n+\n+    \n+    function formatHTML(html) {\n+        // Clean up the input first\n+        var cleanHtml = html.trim();\n+        if (!cleanHtml) {\n+            return cleanHtml;\n+        }\n+        \n+        try {\n+            // Check if content looks like HTML\n+            if (cleanHtml.includes('<') && cleanHtml.includes('>')) {\n+                return html_beautify(cleanHtml, {\n+                    indent_size: 4,\n+                    indent_char: ' ',\n+                    max_preserve_newlines: 5,\n+                    preserve_newlines: true,\n+                    keep_array_indentation: false,\n+                    break_chained_methods: false,\n+                    indent_scripts: 'normal',\n+                    brace_style: 'collapse',\n+                    space_before_conditional: true,\n+                    unescape_strings: false,\n+                    jslint_happy: false,\n+                    end_with_newline: false,\n+                    wrap_line_length: 0,\n+                    indent_inner_html: false,\n+                    comma_first: false,\n+                    e4x: false,\n+                    indent_empty_lines: false\n+                });\n+            } else {\n+                // If it's not HTML, try to format as plain text\n+                return html_beautify('<div>' + cleanHtml + '</div>', {\n+                    indent_size: 2,\n+                    indent_char: ' ',\n+                    max_preserve_newlines: 2,\n+                    preserve_newlines: true\n+                }).replace(/^<div>/, '').replace(/<\\/div>$/, '');\n+            }\n+        } catch (error) {\n+            console.warn('js-beautify failed', error);\n+        }\n+    }\n+\n+    \n+    $(document).ready(function() {\n+        // Initialize Ace editor when the modal is shown\n+        $('#newsletter-text-tab').one('shown.bs.tab', function() {\n+            initMessageEditor();\n+        });\n+        \n+        // Clean up editor when modal is hidden - tab isn't hidden, so have to use modal instead\n+        $('#newsletter-config-modal').on('hidden.bs.modal', function() {\n+            if (messageEditor) {\n+                messageEditor.destroy();\n+                messageEditor = null;\n+            }\n+        });\n+    });\n+\n     $('#newsletter-config-modal').unbind('hidden.bs.modal');\n \n     var cron_widget = $('#cron-widget').cron({\n@@ -711,6 +932,11 @@ <h4 class=\"modal-title\" id=\"newsletter-config-modal-header\">${newsletter['agent_\n     }\n \n     function saveNewsletter() {\n+        // Sync Ace editor content before saving\n+        if (messageEditor) {\n+            $('#message').val(messageEditor.getValue());\n+        }\n+        \n         // Trim all text inputs before saving\n         $('input[type=text]').val(function(_, value) {\n             return $.trim(value);"}, {"sha": "febace9a7b8acf82cd3325f78b1efbde84b0ba26", "filename": "data/interfaces/default/notifier_config.html", "status": "modified", "additions": 21, "deletions": 21, "changes": 42, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fnotifier_config.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fnotifier_config.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fnotifier_config.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -51,13 +51,13 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                             <div class=\"col-md-12\">\n                                                 % if notifier['agent_name'] == 'scripts' and item['name'] == 'scripts_script_folder':\n                                                 <div class=\"input-group\">\n-                                                    <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 ''}>\n+                                                    <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 ''}>\n                                                     <span class=\"input-group-btn\">\n                                                         <button class=\"btn btn-form\" type=\"button\" id=\"${item['name']}_browse\" data-toggle=\"browse\" data-filter=\".folderonly\" data-target=\"#${item['name']}\">Browse</button>\n                                                     </span>\n                                                 </div>\n                                                 % else:\n-                                                <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 ''}>\n+                                                <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 ''}>\n                                                 % endif\n                                             </div>\n                                         </div>\n@@ -72,7 +72,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                                     <span class=\"input-group-btn\">\n                                                         <button class=\"btn btn-form reveal-token\" type=\"button\"><i class=\"fa fa-eye-slash\"></i></button>\n                                                     </span>\n-                                                    <input type=\"password\" class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\" size=\"30\" ${'readonly' if item.get('readonly') else ''}>\n+                                                    <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 ''}>\n                                                 </div>\n                                             </div>\n                                         </div>\n@@ -83,7 +83,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                         <label for=\"${item['name']}\">${item['label']}</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-3\">\n-                                                <input type=\"${item['input_type']}\" class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\" size=\"30\">\n+                                                <input type=\"${item['input_type']}\" class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\" size=\"30\" autocomplete=\"off\">\n                                             </div>\n                                         </div>\n                                         <p class=\"help-block\">${item['description'] | n}</p>\n@@ -101,7 +101,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                     % elif item['input_type'] == 'checkbox':\n                                     <div class=\"checkbox\">\n                                         <label>\n-                                            <input type=\"checkbox\" data-id=\"${item['name']}\" class=\"checkboxes\" value=\"1\" ${checked(item['value'])}> ${item['label']}\n+                                            <input type=\"checkbox\" data-id=\"${item['name']}\" class=\"checkboxes\" value=\"1\" autocomplete=\"off\" ${checked(item['value'])}> ${item['label']}\n                                         </label>\n                                         <p class=\"help-block\">${item['description'] | n}</p>\n                                         <input type=\"hidden\" id=\"${item['name']}\" name=\"${item['name']}\" value=\"${item['value']}\">\n@@ -111,7 +111,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                         <label for=\"${item['name']}\">${item['label']}</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\">\n+                                                <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" autocomplete=\"off\">\n                                                     % for key, value in sorted(item['select_options'].items()):\n                                                     % if isinstance(value, list):\n                                                     <optgroup label=\"${key}\">\n@@ -133,7 +133,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                         <label for=\"${item['name']}\">${item['label']}</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\">\n+                                                <select class=\"form-control\" id=\"${item['name']}\" name=\"${item['name']}\" autocomplete=\"off\">\n                                                     % if item['select_all']:\n                                                     <option value=\"select-all\">Select All</option>\n                                                     <option value=\"remove-all\">Remove All</option>\n@@ -167,7 +167,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                         <label for=\"friendly_name\">Description</label>\n                                         <div class=\"row\">\n                                             <div class=\"col-md-12\">\n-                                                <input type=\"text\" class=\"form-control\" id=\"friendly_name\" name=\"friendly_name\" value=\"${notifier['friendly_name']}\" size=\"30\">\n+                                                <input type=\"text\" class=\"form-control\" id=\"friendly_name\" name=\"friendly_name\" value=\"${notifier['friendly_name']}\" size=\"30\" autocomplete=\"off\">\n                                             </div>\n                                         </div>\n                                         <p class=\"help-block\">Optional: Enter a description to help identify this agent in the notification agents list.</p>\n@@ -185,7 +185,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                     % for action in available_notification_actions:\n                                     <div class=\"checkbox\">\n                                         <label>\n-                                            <input type=\"checkbox\" data-id=\"${action['name']}\" class=\"checkboxes\" value=\"1\" ${checked(notifier['actions'][action['name']])}> ${action['label']}\n+                                            <input type=\"checkbox\" data-id=\"${action['name']}\" class=\"checkboxes\" value=\"1\" autocomplete=\"off\" ${checked(notifier['actions'][action['name']])}> ${action['label']}\n                                         </label>\n                                         <p class=\"help-block\">${action['description'] | n}</p>\n                                         <input type=\"hidden\" id=\"${action['name']}\" name=\"${action['name']}\" value=\"${notifier['actions'][action['name']]}\">\n@@ -208,7 +208,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n \n                             <div class=\"form-group\">\n                                 <label for=\"custom_conditions_logic\">Condition Logic</label>\n-                                <input type=\"text\" class=\"form-control\" name=\"custom_conditions_logic\" id=\"custom_conditions_logic\" value=\"${notifier['custom_conditions_logic']}\" />\n+                                <input type=\"text\" class=\"form-control\" name=\"custom_conditions_logic\" id=\"custom_conditions_logic\" value=\"${notifier['custom_conditions_logic']}\" autocomplete=\"off\" />\n                                 <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>\n                                 <p class=\"help-block\">\n                                     Optional: Enter custom logic to use when evaluating the conditions (e.g. <span class=\"inline-pre\">{1} and ({2} or {3})</span>).\n@@ -254,7 +254,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                         <li>\n                                             <div class=\"form-group\">\n                                                 <label for=\"${action['name']}_subject\">Script Arguments</label>\n-                                                <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>\n+                                                <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>\n                                                 <p class=\"help-block\">Set custom arguments passed to the script.</p>\n                                             </div>\n                                             <div class=\"form-group\">\n@@ -280,12 +280,12 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                         <li>\n                                             <div class=\"form-group\">\n                                                 <label for=\"${action['name']}_subject\">JSON Headers</label>\n-                                                <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>\n+                                                <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>\n                                                 <p class=\"help-block\">Set custom JSON headers.</p>\n                                             </div>\n                                             <div class=\"form-group\">\n                                                 <label for=\"${action['name']}_body\">JSON Data</label>\n-                                                <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>\n+                                                <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>\n                                                 <p class=\"help-block\">Set custom JSON data.</p>\n                                             </div>\n                                             <div class=\"form-group\">\n@@ -311,12 +311,12 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                         <li>\n                                             <div class=\"form-group\">\n                                                 <label for=\"${action['name']}_subject\">Subject Line</label>\n-                                                <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>\n+                                                <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>\n                                                 <p class=\"help-block\">Set a custom subject line.</p>\n                                             </div>\n                                             <div class=\"form-group\">\n                                                 <label for=\"${action['name']}_body\">Message Body</label>\n-                                                <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>\n+                                                <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>\n                                                 <p class=\"help-block\">Set a custom body.</p>\n                                             </div>\n                                             <div class=\"form-group\">\n@@ -347,7 +347,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                 <label for=\"test_script\">Script</label>\n                                 <div class=\"row\">\n                                     <div class=\"col-md-12\">\n-                                        <select class=\"form-control\" id=\"test_script\" name=\"test_script\">\n+                                        <select class=\"form-control\" id=\"test_script\" name=\"test_script\" autocomplete=\"off\">\n                                             % for key, value in sorted(notifier['config_options'][2]['select_options'].items()):\n                                             <option value=\"${key}\">${value}</option>\n                                             % endfor\n@@ -360,7 +360,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                 <label for=\"test_script_args\">Script Arguments</label>\n                                 <div class=\"row\">\n                                     <div class=\"col-md-12\">\n-                                        <input class=\"form-control\" type=\"text\" id=\"test_script_args\" name=\"test_script_args\" value=\"\">\n+                                        <input class=\"form-control\" type=\"text\" id=\"test_script_args\" name=\"test_script_args\" value=\"\" autocomplete=\"off\">\n                                     </div>\n                                 </div>\n                                 <p class=\"help-block\">Set custom arguments passed to the script.</p>\n@@ -370,7 +370,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                 <label for=\"test_subject\">JSON Headers</label>\n                                 <div class=\"row\">\n                                     <div class=\"col-md-12\">\n-                                        <textarea class=\"form-control\" id=\"test_subject\" name=\"test_subject\" data-autoresize></textarea>\n+                                        <textarea class=\"form-control\" id=\"test_subject\" name=\"test_subject\" autocomplete=\"off\" data-autoresize></textarea>\n                                     </div>\n                                 </div>\n                                 <p class=\"help-block\">Set custom JSON headers sent to the webhook.</p>\n@@ -379,7 +379,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                 <label for=\"test_body\">JSON Data</label>\n                                 <div class=\"row\">\n                                     <div class=\"col-md-12\">\n-                                        <textarea class=\"form-control\" id=\"test_body\" name=\"test_body\" data-autoresize></textarea>\n+                                        <textarea class=\"form-control\" id=\"test_body\" name=\"test_body\" autocomplete=\"off\" data-autoresize></textarea>\n                                     </div>\n                                 </div>\n                                 <p class=\"help-block\">Set custom JSON data sent to the webhook.</p>\n@@ -389,7 +389,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                 <label for=\"test_subject\">Subject Line</label>\n                                 <div class=\"row\">\n                                     <div class=\"col-md-12\">\n-                                        <input class=\"form-control\" type=\"text\" id=\"test_subject\" name=\"test_subject\" value=\"Tautulli\">\n+                                        <input class=\"form-control\" type=\"text\" id=\"test_subject\" name=\"test_subject\" value=\"Tautulli\" autocomplete=\"off\">\n                                     </div>\n                                 </div>\n                                 <p class=\"help-block\">Set a custom subject line.</p>\n@@ -398,7 +398,7 @@ <h4 class=\"modal-title\" id=\"notifier-config-modal-header\">${notifier['agent_labe\n                                 <label for=\"test_body\">Message Body</label>\n                                 <div class=\"row\">\n                                     <div class=\"col-md-12\">\n-                                        <textarea class=\"form-control\" id=\"test_body\" name=\"test_body\" data-autoresize>Test Notification</textarea>\n+                                        <textarea class=\"form-control\" id=\"test_body\" name=\"test_body\" autocomplete=\"off\" data-autoresize>Test Notification</textarea>\n                                     </div>\n                                 </div>\n                                 <p class=\"help-block\">Set a custom body.</p>"}, {"sha": "3dab89913e777d177b87ce1bfbbeeedcf888756b", "filename": "data/interfaces/default/settings.html", "status": "modified", "additions": 9, "deletions": 4, "changes": 13, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fsettings.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fsettings.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fsettings.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -975,7 +975,7 @@ <h3>Plex.tv Authentication</h3>\n                         <div class=\"form-group has-feedback\">\n                             <label for=\"pms_token\">Plex.tv Account Token</label>\n                             <div class=\"row\">\n-                                <div class=\"col-md-6\">\n+                                <div class=\"col-md-8\">\n                                     <button id=\"sign-in-plex\" class=\"btn btn-form\" type=\"button\">Fetch New Token</button>\n                                     <span style=\"margin-left: 10px; display: none;\" id=\"pms-token-status\"></span>\n                                 </div>\n@@ -2155,7 +2155,6 @@ <h4 class=\"modal-title\">File Browser</h4>\n <script src=\"${http_root}js/parsley.min.js\"></script>\n <script src=\"${http_root}js/Sortable.min.js\"></script>\n <script src=\"${http_root}js/jquery.inputaffix.min.js\"></script>\n-<script src=\"${http_root}js/kjua.min.js\"></script>\n <script>\n     function getConfigurationTable() {\n         $.ajax({\n@@ -2763,7 +2762,10 @@ <h4 class=\"modal-title\">File Browser</h4>\n     });\n \n     function OAuthPreFunction() {\n-        $(\"#pms-token-status\").html('<i class=\"fa fa-refresh fa-spin\"></i>&nbsp; Waiting for authentication...').fadeIn('fast');\n+        $(\"#pms-token-status\").html('<i class=\"fa fa-refresh fa-spin\"></i>&nbsp; Waiting for authentication... <span id=\"pms-token-timer\"></span>').fadeIn('fast');\n+    }\n+    function OAuthPollCallback(counter) {\n+        $(\"#pms-token-timer\").html(moment.duration(counter, 'seconds').format('*mm:ss'));\n     }\n     function OAuthSuccessCallback(authToken) {\n         $.post('save_pms_token', { token: authToken, client_id: $('#pms_client_id').val() }, function() {\n@@ -2776,9 +2778,12 @@ <h4 class=\"modal-title\">File Browser</h4>\n     function OAuthErrorCallback() {\n         $(\"#pms-token-status\").html('<i class=\"fa fa-exclamation-circle\"></i>&nbsp; Error communicating with Plex.tv.').fadeIn('fast');\n     }\n+    function OAuthMaxRetryCallback() {\n+        $(\"#pms-token-status\").html('<i class=\"fa fa-exclamation-circle\"></i>&nbsp; Authentication timed out. Please try again.').fadeIn('fast');\n+    }\n \n     $('#sign-in-plex').click(function() {\n-        PlexOAuth(OAuthSuccessCallback, OAuthErrorCallback, OAuthPreFunction, $('#pms_client_id').val(uuidv4()).val());\n+        PlexOAuth(OAuthSuccessCallback, OAuthErrorCallback, OAuthMaxRetryCallback, OAuthPollCallback, OAuthPreFunction, $('#pms_client_id').val(uuidv4()).val());\n     });\n \n     // Load database import modal"}, {"sha": "a40bc54a0756c1b090bca52b36cd3420edfca9ab", "filename": "data/interfaces/default/stream_data.html", "status": "modified", "additions": 55, "deletions": 24, "changes": 79, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fstream_data.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fstream_data.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fstream_data.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -68,13 +68,13 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n             <table class=\"stream-info\" style=\"margin-top: 0;\">\n                 <thead>\n                     <tr>\n-                        <th>\n-                        </th>\n+                        <th></th>\n                         <th class=\"heading\">\n-                            Stream Details\n+                            Source Details\n                         </th>\n+                        <th><i class=\"fa fa-long-arrow-right\"></i></th>\n                         <th class=\"heading\">\n-                            Source Details\n+                            Stream Details\n                         </th>\n                     </tr>\n                 </thead>\n@@ -85,38 +85,46 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                         <th>\n                             Media\n                         </th>\n+                        <th></th>\n+                        <th></th>\n+                        <th></th>\n                     </tr>\n                 </thead>\n                 <tbody>\n                     <tr>\n                         <td>Bitrate</td>\n-                        <td>${data['stream_bitrate']} ${'kbps' if data['stream_bitrate'] else ''}</td>\n                         <td>${data['bitrate']} ${'kbps' if data['bitrate'] else ''}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_bitrate']} ${'kbps' if data['stream_bitrate'] else ''}</td>\n                     </tr>\n                     % if data['media_type'] != 'track':\n                     <tr>\n                         <td>Resolution</td>\n-                        <td>${data['stream_video_full_resolution']}</td>\n                         <td>${data['video_full_resolution']}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_video_full_resolution']}</td>\n                     </tr>\n                     % endif\n                     <tr>\n                         <td>Quality</td>\n-                        <td>${data['quality_profile']}</td>\n                         <td>-</td>\n+                        <td></td>\n+                        <td>${data['quality_profile']}</td>\n                     </tr>\n                     % if data['optimized_version'] == 1:\n                     <tr>\n                         <td>Optimized Version</td>\n-                        <td>-</td>\n                         <td>${data['optimized_version_profile']}<br>(${data['optimized_version_title']})</td>\n+                        <td></td>\n+                        <td>-</td>\n                     </tr>\n                     % endif\n                     % if data['synced_version'] == 1:\n                     <tr>\n                         <td>Synced Version</td>\n-                        <td>-</td>\n                         <td>${data['synced_version_profile']}</td>\n+                        <td></td>\n+                        <td>-</td>\n                     </tr>\n                     % endif\n                 </tbody>\n@@ -127,6 +135,8 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                         <th>\n                             Container\n                         </th>\n+                        <th></th>\n+                        <th></th>\n                         <th>\n                             ${data['stream_container_decision']}\n                         </th>\n@@ -135,8 +145,9 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                 <tbody>\n                     <tr>\n                         <td>Container</td>\n-                        <td>${data['stream_container'].upper()}</td>\n                         <td>${data['container'].upper()}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_container'].upper()}</td>\n                     </tr>\n                 </tbody>\n             </table>\n@@ -147,6 +158,8 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                         <th>\n                             Video\n                         </th>\n+                        <th></th>\n+                        <th></th>\n                         <th>\n                             ${data['stream_video_decision']}\n                         </th>\n@@ -155,38 +168,45 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                 <tbody>\n                     <tr>\n                         <td>Codec</td>\n-                        <td>${data['stream_video_codec'].upper()} ${'(HW)' if data['transcode_hw_encoding'] else ''}</td>\n                         <td>${data['video_codec'].upper()} ${'(HW)' if data['transcode_hw_decoding'] else ''}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_video_codec'].upper()} ${'(HW)' if data['transcode_hw_encoding'] else ''}</td>\n                     </tr>\n                     <tr>\n                         <td>Bitrate</td>\n-                        <td>${data['stream_video_bitrate']} ${'kbps' if data['stream_video_bitrate'] else ''}</td>\n                         <td>${data['video_bitrate']} ${'kbps' if data['video_bitrate'] else ''}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_video_bitrate']} ${'kbps' if data['stream_video_bitrate'] else ''}</td>\n                     </tr>\n                     <tr>\n                         <td>Width</td>\n-                        <td>${data['stream_video_width']}</td>\n                         <td>${data['video_width']}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_video_width']}</td>\n                     </tr>\n                     <tr>\n                         <td>Height</td>\n-                        <td>${data['stream_video_height']}</td>\n                         <td>${data['video_height']}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_video_height']}</td>\n                     </tr>\n                     <tr>\n                         <td>Framerate</td>\n-                        <td>${data['stream_video_framerate']}</td>\n                         <td>${data['video_framerate']}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_video_framerate']}</td>\n                     </tr>\n                     <tr>\n                         <td>Dynamic Range</td>\n-                        <td>${data['stream_video_dynamic_range']}</td>\n                         <td>${data['video_dynamic_range']}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_video_dynamic_range']}</td>\n                     </tr>\n                     <tr>\n                         <td>Aspect Ratio</td>\n-                        <td>-</td>\n                         <td>${data['aspect_ratio']}</td>\n+                        <td></td>\n+                        <td>-</td>\n                     </tr>\n                 </tbody>\n             </table>\n@@ -197,6 +217,8 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                         <th>\n                             Audio\n                         </th>\n+                        <th></th>\n+                        <th></th>\n                         <th>\n                             ${data['stream_audio_decision']}\n                         </th>\n@@ -205,23 +227,27 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                 <tbody>\n                     <tr>\n                         <td>Codec</td>\n-                        <td>${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())}</td>\n                         <td>${AUDIO_CODEC_OVERRIDES.get(data['audio_codec'], data['audio_codec'].upper())}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${AUDIO_CODEC_OVERRIDES.get(data['stream_audio_codec'], data['stream_audio_codec'].upper())}</td>\n                     </tr>\n                     <tr>\n                         <td>Bitrate</td>\n-                        <td>${data['stream_audio_bitrate']} ${'kbps' if data['stream_audio_bitrate'] else ''}</td>\n                         <td>${data['audio_bitrate']} ${'kbps' if data['audio_bitrate'] else ''}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_audio_bitrate']} ${'kbps' if data['stream_audio_bitrate'] else ''}</td>\n                     </tr>\n                     <tr>\n                         <td>Channels</td>\n-                        <td>${data['stream_audio_channels']}</td>\n                         <td>${data['audio_channels']}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_audio_channels']}</td>\n                     </tr>\n                     <tr>\n                         <td>Language</td>\n-                        <td>-</td>\n                         <td>${data['audio_language'] or 'Unknown'}</td>\n+                        <td></td>\n+                        <td>-</td>\n                     </tr>\n \n                 </tbody>\n@@ -233,6 +259,8 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                         <th>\n                             Subtitles\n                         </th>\n+                        <th></th>\n+                        <th></th>\n                         <th>\n                             ${'direct play' if data['stream_subtitle_decision'] not in ('transcode', 'copy', 'burn') else data['stream_subtitle_decision']}\n                         </th>\n@@ -241,19 +269,22 @@ <h4 class=\"modal-title\" id=\"info-modal-title\">\n                 <tbody>\n                     <tr>\n                         <td>Codec</td>\n-                        <td>${data['stream_subtitle_codec'].upper() or '-'}</td>\n                         <td>${data['subtitle_codec'].upper()}</td>\n+                        <td><i class=\"fa fa-long-arrow-right\"></i></td>\n+                        <td>${data['stream_subtitle_codec'].upper() or '-'}</td>\n                     </tr>\n                     <tr>\n                         <td>Language</td>\n-                        <td>-</td>\n                         <td>${data['subtitle_language'] or 'Unknown'}</td>\n+                        <td></td>\n+                        <td>-</td>\n                     </tr>\n                     % if data['subtitle_forced']:\n                     <tr>\n                         <td>Forced</td>\n-                        <td>-</td>\n                         <td>${bool(data['subtitle_forced'])}</td>\n+                        <td></td>\n+                        <td>-</td>\n                     </tr>\n                     % endif\n                 </tbody>"}, {"sha": "d3f5fffe30296edf581d13d7263c921ae048ea85", "filename": "data/interfaces/default/welcome.html", "status": "modified", "additions": 8, "deletions": 2, "changes": 10, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fwelcome.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fwelcome.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fwelcome.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -519,7 +519,10 @@ <h3>Setup Complete!</h3>\n     });\n \n     function OAuthPreFunction() {\n-        $(\"#pms-token-status\").html('<i class=\"fa fa-refresh fa-spin\"></i>&nbsp; Waiting for authentication...').fadeIn('fast');\n+        $(\"#pms-token-status\").html('<i class=\"fa fa-refresh fa-spin\"></i>&nbsp; Waiting for authentication... <span id=\"pms-token-timer\"></span>').fadeIn('fast');\n+    }\n+    function OAuthPollCallback(counter) {\n+        $(\"#pms-token-timer\").html(moment.duration(counter, 'seconds').format('*mm:ss'));\n     }\n     function OAuthSuccessCallback(authToken) {\n         $.post('save_pms_token', { token: authToken, client_id: $('#pms_client_id').val() }, function () {\n@@ -532,9 +535,12 @@ <h3>Setup Complete!</h3>\n     function OAuthErrorCallback() {\n         $(\"#pms-token-status\").html('<i class=\"fa fa-exclamation-circle\"></i>&nbsp; Error communicating with Plex.tv.').fadeIn('fast');\n     }\n+    function OAuthMaxRetryCallback() {\n+        $(\"#pms-token-status\").html('<i class=\"fa fa-exclamation-circle\"></i>&nbsp; Authentication timed out. Please try again.').fadeIn('fast');\n+    }\n \n     $('#sign-in-plex').click(function() {\n-        PlexOAuth(OAuthSuccessCallback, OAuthErrorCallback, OAuthPreFunction, $('#pms_client_id').val(uuidv4()).val());\n+        PlexOAuth(OAuthSuccessCallback, OAuthErrorCallback, OAuthMaxRetryCallback, OAuthPollCallback, OAuthPreFunction, $('#pms_client_id').val(uuidv4()).val());\n     });\n });\n </script>"}, {"sha": "1725cd2a12b51f75faf9aab24500d6d6f852d6f5", "filename": "data/interfaces/default/xml_shortcut.html", "status": "modified", "additions": 7, "deletions": 1, "changes": 8, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fxml_shortcut.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fdefault%2Fxml_shortcut.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fdefault%2Fxml_shortcut.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -48,6 +48,9 @@\n <script src=\"${http_root}js/platform.min.js\"></script>\n <script src=\"${http_root}js/script.js${cache_param}\"></script>\n <script>\n+    function OAuthPreFunction() {\n+        $('#sign-in-alert').hide();\n+    }\n     function OAuthSuccessCallback(authToken) {\n         var url = new URL($('#xml_url').val());\n         url.searchParams.append('X-Plex-Token', authToken);\n@@ -56,9 +59,12 @@\n     function OAuthErrorCallback() {\n         $('#sign-in-alert').text('Error communicating with Plex.tv.').show();\n     }\n+    function OAuthMaxRetryCallback() {\n+        $('#sign-in-alert').text('Authentication timed out. Please try again.').show();\n+    }\n \n     $('#sign-in-plex').click(function() {\n-        PlexOAuth(OAuthSuccessCallback, OAuthErrorCallback);\n+        PlexOAuth(OAuthSuccessCallback, OAuthErrorCallback, OAuthMaxRetryCallback, null, OAuthPreFunction);\n     });\n </script>\n </body>"}, {"sha": "58c3ba6d7e40bfa4b3411260f1a70a77594e6d97", "filename": "data/interfaces/newsletters/recently_added.html", "status": "modified", "additions": 1, "deletions": 1, "changes": 2, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fnewsletters%2Frecently_added.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fnewsletters%2Frecently_added.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fnewsletters%2Frecently_added.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -564,7 +564,7 @@\n                     <tr>\n                         <td class=\"wrapper\" style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;box-sizing: border-box;padding: 5px;overflow: auto;\">\n                             <div class=\"sub-header-bar\" style=\"margin-left: auto;margin-right: auto;font-size: 30px;text-align: center;width: 200px;border-top: 1px solid #E5A00D;margin-top: 15px;margin-bottom: 25px;\"></div>\n-                            <div class=\"body-message\" style=\"font-size: 20px;text-align: center;width: 80%;margin-left: auto;margin-right: auto;\">${'<br>'.join(l for l in message.splitlines()) | n}</div>\n+                            <div class=\"body-message\" style=\"font-size: 20px;text-align: center;width: 80%;margin-left: auto;margin-right: auto;\">${message if '<' in message and '>' in message else '<br>'.join(l for l in message.splitlines()) | n}</div>\n                         </td>\n                     </tr>\n                     % endif"}, {"sha": "72cee64a8a9326f1b9a692e17b77eb12747f45fb", "filename": "data/interfaces/newsletters/recently_added.internal.html", "status": "modified", "additions": 1, "deletions": 1, "changes": 2, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fnewsletters%2Frecently_added.internal.html", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/data%2Finterfaces%2Fnewsletters%2Frecently_added.internal.html", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/data%2Finterfaces%2Fnewsletters%2Frecently_added.internal.html?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -564,7 +564,7 @@\n                     <tr>\n                         <td class=\"wrapper\">\n                             <div class=\"sub-header-bar\"></div>\n-                            <div class=\"body-message\">${'<br>'.join(l for l in message.splitlines()) | n}</div>\n+                            <div class=\"body-message\">${message if '<' in message and '>' in message else '<br>'.join(l for l in message.splitlines()) | n}</div>\n                         </td>\n                     </tr>\n                     % endif"}, {"sha": "a0152a7d12abc2db37fb26e764a61e0c894a43f3", "filename": "lib/annotated_doc/__init__.py", "status": "added", "additions": 3, "deletions": 0, "changes": 3, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fannotated_doc%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fannotated_doc%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fannotated_doc%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,3 @@\n+from .main import Doc as Doc\n+\n+__version__ = \"0.0.4\""}, {"sha": "7063c59e4500a1d02bfc9b41887f9e95f8163507", "filename": "lib/annotated_doc/main.py", "status": "added", "additions": 36, "deletions": 0, "changes": 36, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fannotated_doc%2Fmain.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fannotated_doc%2Fmain.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fannotated_doc%2Fmain.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,36 @@\n+class Doc:\n+    \"\"\"Define the documentation of a type annotation using `Annotated`, to be\n+        used in class attributes, function and method parameters, return values,\n+        and variables.\n+\n+    The value should be a positional-only string literal to allow static tools\n+    like editors and documentation generators to use it.\n+\n+    This complements docstrings.\n+\n+    The string value passed is available in the attribute `documentation`.\n+\n+    Example:\n+\n+    ```Python\n+    from typing import Annotated\n+    from annotated_doc import Doc\n+\n+    def hi(name: Annotated[str, Doc(\"Who to say hi to\")]) -> None:\n+        print(f\"Hi, {name}!\")\n+    ```\n+    \"\"\"\n+\n+    def __init__(self, documentation: str, /) -> None:\n+        self.documentation = documentation\n+\n+    def __repr__(self) -> str:\n+        return f\"Doc({self.documentation!r})\"\n+\n+    def __hash__(self) -> int:\n+        return hash(self.documentation)\n+\n+    def __eq__(self, other: object) -> bool:\n+        if not isinstance(other, Doc):\n+            return NotImplemented\n+        return self.documentation == other.documentation"}, {"sha": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", "filename": "lib/annotated_doc/py.typed", "status": "renamed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fannotated_doc%2Fpy.typed", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fannotated_doc%2Fpy.typed", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fannotated_doc%2Fpy.typed?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "previous_filename": "lib/annotated_types/py.typed"}, {"sha": "2f989504caa131c634ab51bcea435f3196a546bf", "filename": "lib/annotated_types/__init__.py", "status": "removed", "additions": 0, "deletions": 396, "changes": 396, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fannotated_types%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fannotated_types%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fannotated_types%2F__init__.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,396 +0,0 @@\n-import math\n-import sys\n-from dataclasses import dataclass\n-from datetime import timezone\n-from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, SupportsFloat, SupportsIndex, TypeVar, Union\n-\n-if sys.version_info < (3, 8):\n-    from typing_extensions import Protocol, runtime_checkable\n-else:\n-    from typing import Protocol, runtime_checkable\n-\n-if sys.version_info < (3, 9):\n-    from typing_extensions import Annotated, Literal\n-else:\n-    from typing import Annotated, Literal\n-\n-if sys.version_info < (3, 10):\n-    EllipsisType = type(Ellipsis)\n-    KW_ONLY = {}\n-    SLOTS = {}\n-else:\n-    from types import EllipsisType\n-\n-    KW_ONLY = {\"kw_only\": True}\n-    SLOTS = {\"slots\": True}\n-\n-\n-__all__ = (\n-    'BaseMetadata',\n-    'GroupedMetadata',\n-    'Gt',\n-    'Ge',\n-    'Lt',\n-    'Le',\n-    'Interval',\n-    'MultipleOf',\n-    'MinLen',\n-    'MaxLen',\n-    'Len',\n-    'Timezone',\n-    'Predicate',\n-    'LowerCase',\n-    'UpperCase',\n-    'IsDigits',\n-    'IsFinite',\n-    'IsNotFinite',\n-    'IsNan',\n-    'IsNotNan',\n-    'IsInfinite',\n-    'IsNotInfinite',\n-    'doc',\n-    'DocInfo',\n-    '__version__',\n-)\n-\n-__version__ = '0.6.0'\n-\n-\n-T = TypeVar('T')\n-\n-\n-# arguments that start with __ are considered\n-# positional only\n-# see https://peps.python.org/pep-0484/#positional-only-arguments\n-\n-\n-class SupportsGt(Protocol):\n-    def __gt__(self: T, __other: T) -> bool:\n-        ...\n-\n-\n-class SupportsGe(Protocol):\n-    def __ge__(self: T, __other: T) -> bool:\n-        ...\n-\n-\n-class SupportsLt(Protocol):\n-    def __lt__(self: T, __other: T) -> bool:\n-        ...\n-\n-\n-class SupportsLe(Protocol):\n-    def __le__(self: T, __other: T) -> bool:\n-        ...\n-\n-\n-class SupportsMod(Protocol):\n-    def __mod__(self: T, __other: T) -> T:\n-        ...\n-\n-\n-class SupportsDiv(Protocol):\n-    def __div__(self: T, __other: T) -> T:\n-        ...\n-\n-\n-class BaseMetadata:\n-    \"\"\"Base class for all metadata.\n-\n-    This exists mainly so that implementers\n-    can do `isinstance(..., BaseMetadata)` while traversing field annotations.\n-    \"\"\"\n-\n-    __slots__ = ()\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class Gt(BaseMetadata):\n-    \"\"\"Gt(gt=x) implies that the value must be greater than x.\n-\n-    It can be used with any type that supports the ``>`` operator,\n-    including numbers, dates and times, strings, sets, and so on.\n-    \"\"\"\n-\n-    gt: SupportsGt\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class Ge(BaseMetadata):\n-    \"\"\"Ge(ge=x) implies that the value must be greater than or equal to x.\n-\n-    It can be used with any type that supports the ``>=`` operator,\n-    including numbers, dates and times, strings, sets, and so on.\n-    \"\"\"\n-\n-    ge: SupportsGe\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class Lt(BaseMetadata):\n-    \"\"\"Lt(lt=x) implies that the value must be less than x.\n-\n-    It can be used with any type that supports the ``<`` operator,\n-    including numbers, dates and times, strings, sets, and so on.\n-    \"\"\"\n-\n-    lt: SupportsLt\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class Le(BaseMetadata):\n-    \"\"\"Le(le=x) implies that the value must be less than or equal to x.\n-\n-    It can be used with any type that supports the ``<=`` operator,\n-    including numbers, dates and times, strings, sets, and so on.\n-    \"\"\"\n-\n-    le: SupportsLe\n-\n-\n-@runtime_checkable\n-class GroupedMetadata(Protocol):\n-    \"\"\"A grouping of multiple BaseMetadata objects.\n-\n-    `GroupedMetadata` on its own is not metadata and has no meaning.\n-    All it the the constraint and metadata should be fully expressable\n-    in terms of the `BaseMetadata`'s returned by `GroupedMetadata.__iter__()`.\n-\n-    Concrete implementations should override `GroupedMetadata.__iter__()`\n-    to add their own metadata.\n-    For example:\n-\n-    >>> @dataclass\n-    >>> class Field(GroupedMetadata):\n-    >>>     gt: float | None = None\n-    >>>     description: str | None = None\n-    ...\n-    >>>     def __iter__(self) -> Iterable[BaseMetadata]:\n-    >>>         if self.gt is not None:\n-    >>>             yield Gt(self.gt)\n-    >>>         if self.description is not None:\n-    >>>             yield Description(self.gt)\n-\n-    Also see the implementation of `Interval` below for an example.\n-\n-    Parsers should recognize this and unpack it so that it can be used\n-    both with and without unpacking:\n-\n-    - `Annotated[int, Field(...)]` (parser must unpack Field)\n-    - `Annotated[int, *Field(...)]` (PEP-646)\n-    \"\"\"  # noqa: trailing-whitespace\n-\n-    @property\n-    def __is_annotated_types_grouped_metadata__(self) -> Literal[True]:\n-        return True\n-\n-    def __iter__(self) -> Iterator[BaseMetadata]:\n-        ...\n-\n-    if not TYPE_CHECKING:\n-        __slots__ = ()  # allow subclasses to use slots\n-\n-        def __init_subclass__(cls, *args: Any, **kwargs: Any) -> None:\n-            # Basic ABC like functionality without the complexity of an ABC\n-            super().__init_subclass__(*args, **kwargs)\n-            if cls.__iter__ is GroupedMetadata.__iter__:\n-                raise TypeError(\"Can't subclass GroupedMetadata without implementing __iter__\")\n-\n-        def __iter__(self) -> Iterator[BaseMetadata]:  # noqa: F811\n-            raise NotImplementedError  # more helpful than \"None has no attribute...\" type errors\n-\n-\n-@dataclass(frozen=True, **KW_ONLY, **SLOTS)\n-class Interval(GroupedMetadata):\n-    \"\"\"Interval can express inclusive or exclusive bounds with a single object.\n-\n-    It accepts keyword arguments ``gt``, ``ge``, ``lt``, and/or ``le``, which\n-    are interpreted the same way as the single-bound constraints.\n-    \"\"\"\n-\n-    gt: Union[SupportsGt, None] = None\n-    ge: Union[SupportsGe, None] = None\n-    lt: Union[SupportsLt, None] = None\n-    le: Union[SupportsLe, None] = None\n-\n-    def __iter__(self) -> Iterator[BaseMetadata]:\n-        \"\"\"Unpack an Interval into zero or more single-bounds.\"\"\"\n-        if self.gt is not None:\n-            yield Gt(self.gt)\n-        if self.ge is not None:\n-            yield Ge(self.ge)\n-        if self.lt is not None:\n-            yield Lt(self.lt)\n-        if self.le is not None:\n-            yield Le(self.le)\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class MultipleOf(BaseMetadata):\n-    \"\"\"MultipleOf(multiple_of=x) might be interpreted in two ways:\n-\n-    1. Python semantics, implying ``value % multiple_of == 0``, or\n-    2. JSONschema semantics, where ``int(value / multiple_of) == value / multiple_of``\n-\n-    We encourage users to be aware of these two common interpretations,\n-    and libraries to carefully document which they implement.\n-    \"\"\"\n-\n-    multiple_of: Union[SupportsDiv, SupportsMod]\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class MinLen(BaseMetadata):\n-    \"\"\"\n-    MinLen() implies minimum inclusive length,\n-    e.g. ``len(value) >= min_length``.\n-    \"\"\"\n-\n-    min_length: Annotated[int, Ge(0)]\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class MaxLen(BaseMetadata):\n-    \"\"\"\n-    MaxLen() implies maximum inclusive length,\n-    e.g. ``len(value) <= max_length``.\n-    \"\"\"\n-\n-    max_length: Annotated[int, Ge(0)]\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class Len(GroupedMetadata):\n-    \"\"\"\n-    Len() implies that ``min_length <= len(value) <= max_length``.\n-\n-    Upper bound may be omitted or ``None`` to indicate no upper length bound.\n-    \"\"\"\n-\n-    min_length: Annotated[int, Ge(0)] = 0\n-    max_length: Optional[Annotated[int, Ge(0)]] = None\n-\n-    def __iter__(self) -> Iterator[BaseMetadata]:\n-        \"\"\"Unpack a Len into zone or more single-bounds.\"\"\"\n-        if self.min_length > 0:\n-            yield MinLen(self.min_length)\n-        if self.max_length is not None:\n-            yield MaxLen(self.max_length)\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class Timezone(BaseMetadata):\n-    \"\"\"Timezone(tz=...) requires a datetime to be aware (or ``tz=None``, naive).\n-\n-    ``Annotated[datetime, Timezone(None)]`` must be a naive datetime.\n-    ``Timezone[...]`` (the ellipsis literal) expresses that the datetime must be\n-    tz-aware but any timezone is allowed.\n-\n-    You may also pass a specific timezone string or timezone object such as\n-    ``Timezone(timezone.utc)`` or ``Timezone(\"Africa/Abidjan\")`` to express that\n-    you only allow a specific timezone, though we note that this is often\n-    a symptom of poor design.\n-    \"\"\"\n-\n-    tz: Union[str, timezone, EllipsisType, None]\n-\n-\n-@dataclass(frozen=True, **SLOTS)\n-class Predicate(BaseMetadata):\n-    \"\"\"``Predicate(func: Callable)`` implies `func(value)` is truthy for valid values.\n-\n-    Users should prefer statically inspectable metadata, but if you need the full\n-    power and flexibility of arbitrary runtime predicates... here it is.\n-\n-    We provide a few predefined predicates for common string constraints:\n-    ``IsLower = Predicate(str.islower)``, ``IsUpper = Predicate(str.isupper)``, and\n-    ``IsDigit = Predicate(str.isdigit)``. Users are encouraged to use methods which\n-    can be given special handling, and avoid indirection like ``lambda s: s.lower()``.\n-\n-    Some libraries might have special logic to handle certain predicates, e.g. by\n-    checking for `str.isdigit` and using its presence to both call custom logic to\n-    enforce digit-only strings, and customise some generated external schema.\n-\n-    We do not specify what behaviour should be expected for predicates that raise\n-    an exception.  For example `Annotated[int, Predicate(str.isdigit)]` might silently\n-    skip invalid constraints, or statically raise an error; or it might try calling it\n-    and then propogate or discard the resulting exception.\n-    \"\"\"\n-\n-    func: Callable[[Any], bool]\n-\n-\n-@dataclass\n-class Not:\n-    func: Callable[[Any], bool]\n-\n-    def __call__(self, __v: Any) -> bool:\n-        return not self.func(__v)\n-\n-\n-_StrType = TypeVar(\"_StrType\", bound=str)\n-\n-LowerCase = Annotated[_StrType, Predicate(str.islower)]\n-\"\"\"\n-Return True if the string is a lowercase string, False otherwise.\n-\n-A string is lowercase if all cased characters in the string are lowercase and there is at least one cased character in the string.\n-\"\"\"  # noqa: E501\n-UpperCase = Annotated[_StrType, Predicate(str.isupper)]\n-\"\"\"\n-Return True if the string is an uppercase string, False otherwise.\n-\n-A string is uppercase if all cased characters in the string are uppercase and there is at least one cased character in the string.\n-\"\"\"  # noqa: E501\n-IsDigits = Annotated[_StrType, Predicate(str.isdigit)]\n-\"\"\"\n-Return True if the string is a digit string, False otherwise.\n-\n-A string is a digit string if all characters in the string are digits and there is at least one character in the string.\n-\"\"\"  # noqa: E501\n-IsAscii = Annotated[_StrType, Predicate(str.isascii)]\n-\"\"\"\n-Return True if all characters in the string are ASCII, False otherwise.\n-\n-ASCII characters have code points in the range U+0000-U+007F. Empty string is ASCII too.\n-\"\"\"\n-\n-_NumericType = TypeVar('_NumericType', bound=Union[SupportsFloat, SupportsIndex])\n-IsFinite = Annotated[_NumericType, Predicate(math.isfinite)]\n-\"\"\"Return True if x is neither an infinity nor a NaN, and False otherwise.\"\"\"\n-IsNotFinite = Annotated[_NumericType, Predicate(Not(math.isfinite))]\n-\"\"\"Return True if x is one of infinity or NaN, and False otherwise\"\"\"\n-IsNan = Annotated[_NumericType, Predicate(math.isnan)]\n-\"\"\"Return True if x is a NaN (not a number), and False otherwise.\"\"\"\n-IsNotNan = Annotated[_NumericType, Predicate(Not(math.isnan))]\n-\"\"\"Return True if x is anything but NaN (not a number), and False otherwise.\"\"\"\n-IsInfinite = Annotated[_NumericType, Predicate(math.isinf)]\n-\"\"\"Return True if x is a positive or negative infinity, and False otherwise.\"\"\"\n-IsNotInfinite = Annotated[_NumericType, Predicate(Not(math.isinf))]\n-\"\"\"Return True if x is neither a positive or negative infinity, and False otherwise.\"\"\"\n-\n-try:\n-    from typing_extensions import DocInfo, doc  # type: ignore [attr-defined]\n-except ImportError:\n-\n-    @dataclass(frozen=True, **SLOTS)\n-    class DocInfo:  # type: ignore [no-redef]\n-        \"\"\" \"\n-        The return value of doc(), mainly to be used by tools that want to extract the\n-        Annotated documentation at runtime.\n-        \"\"\"\n-\n-        documentation: str\n-        \"\"\"The documentation string passed to doc().\"\"\"\n-\n-    def doc(\n-        documentation: str,\n-    ) -> DocInfo:\n-        \"\"\"\n-        Add documentation to a type annotation inside of Annotated.\n-\n-        For example:\n-\n-        >>> def hi(name: Annotated[int, doc(\"The name of the user\")]) -> None: ...\n-        \"\"\"\n-        return DocInfo(documentation)"}, {"sha": "f54df700283bb31f60106443af0e54c8cfbdb834", "filename": "lib/annotated_types/test_cases.py", "status": "removed", "additions": 0, "deletions": 147, "changes": 147, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fannotated_types%2Ftest_cases.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fannotated_types%2Ftest_cases.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fannotated_types%2Ftest_cases.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,147 +0,0 @@\n-import math\n-import sys\n-from datetime import date, datetime, timedelta, timezone\n-from decimal import Decimal\n-from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Set, Tuple\n-\n-if sys.version_info < (3, 9):\n-    from typing_extensions import Annotated\n-else:\n-    from typing import Annotated\n-\n-import annotated_types as at\n-\n-\n-class Case(NamedTuple):\n-    \"\"\"\n-    A test case for `annotated_types`.\n-    \"\"\"\n-\n-    annotation: Any\n-    valid_cases: Iterable[Any]\n-    invalid_cases: Iterable[Any]\n-\n-\n-def cases() -> Iterable[Case]:\n-    # Gt, Ge, Lt, Le\n-    yield Case(Annotated[int, at.Gt(4)], (5, 6, 1000), (4, 0, -1))\n-    yield Case(Annotated[float, at.Gt(0.5)], (0.6, 0.7, 0.8, 0.9), (0.5, 0.0, -0.1))\n-    yield Case(\n-        Annotated[datetime, at.Gt(datetime(2000, 1, 1))],\n-        [datetime(2000, 1, 2), datetime(2000, 1, 3)],\n-        [datetime(2000, 1, 1), datetime(1999, 12, 31)],\n-    )\n-    yield Case(\n-        Annotated[datetime, at.Gt(date(2000, 1, 1))],\n-        [date(2000, 1, 2), date(2000, 1, 3)],\n-        [date(2000, 1, 1), date(1999, 12, 31)],\n-    )\n-    yield Case(\n-        Annotated[datetime, at.Gt(Decimal('1.123'))],\n-        [Decimal('1.1231'), Decimal('123')],\n-        [Decimal('1.123'), Decimal('0')],\n-    )\n-\n-    yield Case(Annotated[int, at.Ge(4)], (4, 5, 6, 1000, 4), (0, -1))\n-    yield Case(Annotated[float, at.Ge(0.5)], (0.5, 0.6, 0.7, 0.8, 0.9), (0.4, 0.0, -0.1))\n-    yield Case(\n-        Annotated[datetime, at.Ge(datetime(2000, 1, 1))],\n-        [datetime(2000, 1, 2), datetime(2000, 1, 3)],\n-        [datetime(1998, 1, 1), datetime(1999, 12, 31)],\n-    )\n-\n-    yield Case(Annotated[int, at.Lt(4)], (0, -1), (4, 5, 6, 1000, 4))\n-    yield Case(Annotated[float, at.Lt(0.5)], (0.4, 0.0, -0.1), (0.5, 0.6, 0.7, 0.8, 0.9))\n-    yield Case(\n-        Annotated[datetime, at.Lt(datetime(2000, 1, 1))],\n-        [datetime(1999, 12, 31), datetime(1999, 12, 31)],\n-        [datetime(2000, 1, 2), datetime(2000, 1, 3)],\n-    )\n-\n-    yield Case(Annotated[int, at.Le(4)], (4, 0, -1), (5, 6, 1000))\n-    yield Case(Annotated[float, at.Le(0.5)], (0.5, 0.0, -0.1), (0.6, 0.7, 0.8, 0.9))\n-    yield Case(\n-        Annotated[datetime, at.Le(datetime(2000, 1, 1))],\n-        [datetime(2000, 1, 1), datetime(1999, 12, 31)],\n-        [datetime(2000, 1, 2), datetime(2000, 1, 3)],\n-    )\n-\n-    # Interval\n-    yield Case(Annotated[int, at.Interval(gt=4)], (5, 6, 1000), (4, 0, -1))\n-    yield Case(Annotated[int, at.Interval(gt=4, lt=10)], (5, 6), (4, 10, 1000, 0, -1))\n-    yield Case(Annotated[float, at.Interval(ge=0.5, le=1)], (0.5, 0.9, 1), (0.49, 1.1))\n-    yield Case(\n-        Annotated[datetime, at.Interval(gt=datetime(2000, 1, 1), le=datetime(2000, 1, 3))],\n-        [datetime(2000, 1, 2), datetime(2000, 1, 3)],\n-        [datetime(2000, 1, 1), datetime(2000, 1, 4)],\n-    )\n-\n-    yield Case(Annotated[int, at.MultipleOf(multiple_of=3)], (0, 3, 9), (1, 2, 4))\n-    yield Case(Annotated[float, at.MultipleOf(multiple_of=0.5)], (0, 0.5, 1, 1.5), (0.4, 1.1))\n-\n-    # lengths\n-\n-    yield Case(Annotated[str, at.MinLen(3)], ('123', '1234', 'x' * 10), ('', '1', '12'))\n-    yield Case(Annotated[str, at.Len(3)], ('123', '1234', 'x' * 10), ('', '1', '12'))\n-    yield Case(Annotated[List[int], at.MinLen(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2]))\n-    yield Case(Annotated[List[int], at.Len(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2]))\n-\n-    yield Case(Annotated[str, at.MaxLen(4)], ('', '1234'), ('12345', 'x' * 10))\n-    yield Case(Annotated[str, at.Len(0, 4)], ('', '1234'), ('12345', 'x' * 10))\n-    yield Case(Annotated[List[str], at.MaxLen(4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10))\n-    yield Case(Annotated[List[str], at.Len(0, 4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10))\n-\n-    yield Case(Annotated[str, at.Len(3, 5)], ('123', '12345'), ('', '1', '12', '123456', 'x' * 10))\n-    yield Case(Annotated[str, at.Len(3, 3)], ('123',), ('12', '1234'))\n-\n-    yield Case(Annotated[Dict[int, int], at.Len(2, 3)], [{1: 1, 2: 2}], [{}, {1: 1}, {1: 1, 2: 2, 3: 3, 4: 4}])\n-    yield Case(Annotated[Set[int], at.Len(2, 3)], ({1, 2}, {1, 2, 3}), (set(), {1}, {1, 2, 3, 4}))\n-    yield Case(Annotated[Tuple[int, ...], at.Len(2, 3)], ((1, 2), (1, 2, 3)), ((), (1,), (1, 2, 3, 4)))\n-\n-    # Timezone\n-\n-    yield Case(\n-        Annotated[datetime, at.Timezone(None)], [datetime(2000, 1, 1)], [datetime(2000, 1, 1, tzinfo=timezone.utc)]\n-    )\n-    yield Case(\n-        Annotated[datetime, at.Timezone(...)], [datetime(2000, 1, 1, tzinfo=timezone.utc)], [datetime(2000, 1, 1)]\n-    )\n-    yield Case(\n-        Annotated[datetime, at.Timezone(timezone.utc)],\n-        [datetime(2000, 1, 1, tzinfo=timezone.utc)],\n-        [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))],\n-    )\n-    yield Case(\n-        Annotated[datetime, at.Timezone('Europe/London')],\n-        [datetime(2000, 1, 1, tzinfo=timezone(timedelta(0), name='Europe/London'))],\n-        [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))],\n-    )\n-\n-    # predicate types\n-\n-    yield Case(at.LowerCase[str], ['abc', 'foobar'], ['', 'A', 'Boom'])\n-    yield Case(at.UpperCase[str], ['ABC', 'DEFO'], ['', 'a', 'abc', 'AbC'])\n-    yield Case(at.IsDigits[str], ['123'], ['', 'ab', 'a1b2'])\n-    yield Case(at.IsAscii[str], ['123', 'foo bar'], ['\u00a3100', '\ud83d\ude0a', 'whatever \ud83d\udc40'])\n-\n-    yield Case(Annotated[int, at.Predicate(lambda x: x % 2 == 0)], [0, 2, 4], [1, 3, 5])\n-\n-    yield Case(at.IsFinite[float], [1.23], [math.nan, math.inf, -math.inf])\n-    yield Case(at.IsNotFinite[float], [math.nan, math.inf], [1.23])\n-    yield Case(at.IsNan[float], [math.nan], [1.23, math.inf])\n-    yield Case(at.IsNotNan[float], [1.23, math.inf], [math.nan])\n-    yield Case(at.IsInfinite[float], [math.inf], [math.nan, 1.23])\n-    yield Case(at.IsNotInfinite[float], [math.nan, 1.23], [math.inf])\n-\n-    # check stacked predicates\n-    yield Case(at.IsInfinite[Annotated[float, at.Predicate(lambda x: x > 0)]], [math.inf], [-math.inf, 1.23, math.nan])\n-\n-    # doc\n-    yield Case(Annotated[int, at.doc(\"A number\")], [1, 2], [])\n-\n-    # custom GroupedMetadata\n-    class MyCustomGroupedMetadata(at.GroupedMetadata):\n-        def __iter__(self) -> Iterator[at.Predicate]:\n-            yield at.Predicate(lambda x: float(x).is_integer())\n-\n-    yield Case(Annotated[float, MyCustomGroupedMetadata()], [0, 2.0], [0.01, 1.5])"}, {"sha": "475135fe2b80ccd593a46602020b30245d6c0373", "filename": "lib/apscheduler/__init__.py", "status": "modified", "additions": 8, "deletions": 7, "changes": 15, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,10 +1,11 @@\n-from pkg_resources import get_distribution, DistributionNotFound\n+import importlib.metadata as importlib_metadata\n+import sys\n \n try:\n-    release = get_distribution('APScheduler').version.split('-')[0]\n-except DistributionNotFound:\n-    release = '3.5.0'\n+    release = importlib_metadata.version(\"APScheduler\").split(\"-\")[0]\n+except importlib_metadata.PackageNotFoundError:\n+    release = \"3.5.0\"\n \n-version_info = tuple(int(x) if x.isdigit() else x for x in release.split('.'))\n-version = __version__ = '.'.join(str(x) for x in version_info[:3])\n-del get_distribution, DistributionNotFound\n+version_info = tuple(int(x) if x.isdigit() else x for x in release.split(\".\"))\n+version = __version__ = \".\".join(str(x) for x in version_info[:3])\n+del sys, importlib_metadata"}, {"sha": "a4c11aa0ce1bde88c61be86cb24bcf7045c1ba12", "filename": "lib/apscheduler/events.py", "status": "modified", "additions": 80, "deletions": 40, "changes": 120, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fevents.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fevents.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fevents.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,36 +1,68 @@\n-__all__ = ('EVENT_SCHEDULER_STARTED', 'EVENT_SCHEDULER_SHUTDOWN', 'EVENT_SCHEDULER_PAUSED',\n-           'EVENT_SCHEDULER_RESUMED', 'EVENT_EXECUTOR_ADDED', 'EVENT_EXECUTOR_REMOVED',\n-           'EVENT_JOBSTORE_ADDED', 'EVENT_JOBSTORE_REMOVED', 'EVENT_ALL_JOBS_REMOVED',\n-           'EVENT_JOB_ADDED', 'EVENT_JOB_REMOVED', 'EVENT_JOB_MODIFIED', 'EVENT_JOB_EXECUTED',\n-           'EVENT_JOB_ERROR', 'EVENT_JOB_MISSED', 'EVENT_JOB_SUBMITTED', 'EVENT_JOB_MAX_INSTANCES',\n-           'SchedulerEvent', 'JobEvent', 'JobExecutionEvent', 'JobSubmissionEvent')\n-\n-\n-EVENT_SCHEDULER_STARTED = EVENT_SCHEDULER_START = 2 ** 0\n-EVENT_SCHEDULER_SHUTDOWN = 2 ** 1\n-EVENT_SCHEDULER_PAUSED = 2 ** 2\n-EVENT_SCHEDULER_RESUMED = 2 ** 3\n-EVENT_EXECUTOR_ADDED = 2 ** 4\n-EVENT_EXECUTOR_REMOVED = 2 ** 5\n-EVENT_JOBSTORE_ADDED = 2 ** 6\n-EVENT_JOBSTORE_REMOVED = 2 ** 7\n-EVENT_ALL_JOBS_REMOVED = 2 ** 8\n-EVENT_JOB_ADDED = 2 ** 9\n-EVENT_JOB_REMOVED = 2 ** 10\n-EVENT_JOB_MODIFIED = 2 ** 11\n-EVENT_JOB_EXECUTED = 2 ** 12\n-EVENT_JOB_ERROR = 2 ** 13\n-EVENT_JOB_MISSED = 2 ** 14\n-EVENT_JOB_SUBMITTED = 2 ** 15\n-EVENT_JOB_MAX_INSTANCES = 2 ** 16\n-EVENT_ALL = (EVENT_SCHEDULER_STARTED | EVENT_SCHEDULER_SHUTDOWN | EVENT_SCHEDULER_PAUSED |\n-             EVENT_SCHEDULER_RESUMED | EVENT_EXECUTOR_ADDED | EVENT_EXECUTOR_REMOVED |\n-             EVENT_JOBSTORE_ADDED | EVENT_JOBSTORE_REMOVED | EVENT_ALL_JOBS_REMOVED |\n-             EVENT_JOB_ADDED | EVENT_JOB_REMOVED | EVENT_JOB_MODIFIED | EVENT_JOB_EXECUTED |\n-             EVENT_JOB_ERROR | EVENT_JOB_MISSED | EVENT_JOB_SUBMITTED | EVENT_JOB_MAX_INSTANCES)\n-\n-\n-class SchedulerEvent(object):\n+__all__ = (\n+    \"EVENT_ALL\",\n+    \"EVENT_ALL_JOBS_REMOVED\",\n+    \"EVENT_EXECUTOR_ADDED\",\n+    \"EVENT_EXECUTOR_REMOVED\",\n+    \"EVENT_JOBSTORE_ADDED\",\n+    \"EVENT_JOBSTORE_REMOVED\",\n+    \"EVENT_JOB_ADDED\",\n+    \"EVENT_JOB_ERROR\",\n+    \"EVENT_JOB_EXECUTED\",\n+    \"EVENT_JOB_MAX_INSTANCES\",\n+    \"EVENT_JOB_MISSED\",\n+    \"EVENT_JOB_MODIFIED\",\n+    \"EVENT_JOB_REMOVED\",\n+    \"EVENT_JOB_SUBMITTED\",\n+    \"EVENT_SCHEDULER_PAUSED\",\n+    \"EVENT_SCHEDULER_RESUMED\",\n+    \"EVENT_SCHEDULER_SHUTDOWN\",\n+    \"EVENT_SCHEDULER_STARTED\",\n+    \"JobEvent\",\n+    \"JobExecutionEvent\",\n+    \"JobSubmissionEvent\",\n+    \"SchedulerEvent\",\n+)\n+\n+\n+EVENT_SCHEDULER_STARTED = EVENT_SCHEDULER_START = 2**0\n+EVENT_SCHEDULER_SHUTDOWN = 2**1\n+EVENT_SCHEDULER_PAUSED = 2**2\n+EVENT_SCHEDULER_RESUMED = 2**3\n+EVENT_EXECUTOR_ADDED = 2**4\n+EVENT_EXECUTOR_REMOVED = 2**5\n+EVENT_JOBSTORE_ADDED = 2**6\n+EVENT_JOBSTORE_REMOVED = 2**7\n+EVENT_ALL_JOBS_REMOVED = 2**8\n+EVENT_JOB_ADDED = 2**9\n+EVENT_JOB_REMOVED = 2**10\n+EVENT_JOB_MODIFIED = 2**11\n+EVENT_JOB_EXECUTED = 2**12\n+EVENT_JOB_ERROR = 2**13\n+EVENT_JOB_MISSED = 2**14\n+EVENT_JOB_SUBMITTED = 2**15\n+EVENT_JOB_MAX_INSTANCES = 2**16\n+EVENT_ALL = (\n+    EVENT_SCHEDULER_STARTED\n+    | EVENT_SCHEDULER_SHUTDOWN\n+    | EVENT_SCHEDULER_PAUSED\n+    | EVENT_SCHEDULER_RESUMED\n+    | EVENT_EXECUTOR_ADDED\n+    | EVENT_EXECUTOR_REMOVED\n+    | EVENT_JOBSTORE_ADDED\n+    | EVENT_JOBSTORE_REMOVED\n+    | EVENT_ALL_JOBS_REMOVED\n+    | EVENT_JOB_ADDED\n+    | EVENT_JOB_REMOVED\n+    | EVENT_JOB_MODIFIED\n+    | EVENT_JOB_EXECUTED\n+    | EVENT_JOB_ERROR\n+    | EVENT_JOB_MISSED\n+    | EVENT_JOB_SUBMITTED\n+    | EVENT_JOB_MAX_INSTANCES\n+)\n+\n+\n+class SchedulerEvent:\n     \"\"\"\n     An event that concerns the scheduler itself.\n \n@@ -39,12 +71,12 @@ class SchedulerEvent(object):\n     \"\"\"\n \n     def __init__(self, code, alias=None):\n-        super(SchedulerEvent, self).__init__()\n+        super().__init__()\n         self.code = code\n         self.alias = alias\n \n     def __repr__(self):\n-        return '<%s (code=%d)>' % (self.__class__.__name__, self.code)\n+        return f\"<self.__class__.__name__ (code={self.code})>\"\n \n \n class JobEvent(SchedulerEvent):\n@@ -57,7 +89,7 @@ class JobEvent(SchedulerEvent):\n     \"\"\"\n \n     def __init__(self, code, job_id, jobstore):\n-        super(JobEvent, self).__init__(code)\n+        super().__init__(code)\n         self.code = code\n         self.job_id = job_id\n         self.jobstore = jobstore\n@@ -71,7 +103,7 @@ class JobSubmissionEvent(JobEvent):\n     \"\"\"\n \n     def __init__(self, code, job_id, jobstore, scheduled_run_times):\n-        super(JobSubmissionEvent, self).__init__(code, job_id, jobstore)\n+        super().__init__(code, job_id, jobstore)\n         self.scheduled_run_times = scheduled_run_times\n \n \n@@ -85,9 +117,17 @@ class JobExecutionEvent(JobEvent):\n     :ivar traceback: a formatted traceback for the exception\n     \"\"\"\n \n-    def __init__(self, code, job_id, jobstore, scheduled_run_time, retval=None, exception=None,\n-                 traceback=None):\n-        super(JobExecutionEvent, self).__init__(code, job_id, jobstore)\n+    def __init__(\n+        self,\n+        code,\n+        job_id,\n+        jobstore,\n+        scheduled_run_time,\n+        retval=None,\n+        exception=None,\n+        traceback=None,\n+    ):\n+        super().__init__(code, job_id, jobstore)\n         self.scheduled_run_time = scheduled_run_time\n         self.retval = retval\n         self.exception = exception"}, {"sha": "cc7eb651fc278a727c5be208ab33ba128eb9fd9c", "filename": "lib/apscheduler/executors/asyncio.py", "status": "modified", "additions": 8, "deletions": 8, "changes": 16, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Fasyncio.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Fasyncio.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fexecutors%2Fasyncio.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,9 +1,6 @@\n-from __future__ import absolute_import\n-\n import sys\n \n-from apscheduler.executors.base import BaseExecutor, run_job\n-from apscheduler.executors.base_py3 import run_coroutine_job\n+from apscheduler.executors.base import BaseExecutor, run_coroutine_job, run_job\n from apscheduler.util import iscoroutinefunction_partial\n \n \n@@ -19,7 +16,7 @@ class AsyncIOExecutor(BaseExecutor):\n     \"\"\"\n \n     def start(self, scheduler, alias):\n-        super(AsyncIOExecutor, self).start(scheduler, alias)\n+        super().start(scheduler, alias)\n         self._eventloop = scheduler._eventloop\n         self._pending_futures = set()\n \n@@ -42,11 +39,14 @@ def callback(f):\n                 self._run_job_success(job.id, events)\n \n         if iscoroutinefunction_partial(job.func):\n-            coro = run_coroutine_job(job, job._jobstore_alias, run_times, self._logger.name)\n+            coro = run_coroutine_job(\n+                job, job._jobstore_alias, run_times, self._logger.name\n+            )\n             f = self._eventloop.create_task(coro)\n         else:\n-            f = self._eventloop.run_in_executor(None, run_job, job, job._jobstore_alias, run_times,\n-                                                self._logger.name)\n+            f = self._eventloop.run_in_executor(\n+                None, run_job, job, job._jobstore_alias, run_times, self._logger.name\n+            )\n \n         f.add_done_callback(callback)\n         self._pending_futures.add(f)"}, {"sha": "6decbb56c3a4a8e07e030dc436defb385207b929", "filename": "lib/apscheduler/executors/base.py", "status": "modified", "additions": 90, "deletions": 31, "changes": 121, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Fbase.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Fbase.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fexecutors%2Fbase.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,33 +1,36 @@\n+import logging\n+import sys\n+import traceback\n from abc import ABCMeta, abstractmethod\n from collections import defaultdict\n-from datetime import datetime, timedelta\n+from datetime import datetime, timedelta, timezone\n from traceback import format_tb\n-import logging\n-import sys\n-\n-from pytz import utc\n-import six\n \n from apscheduler.events import (\n-    JobExecutionEvent, EVENT_JOB_MISSED, EVENT_JOB_ERROR, EVENT_JOB_EXECUTED)\n+    EVENT_JOB_ERROR,\n+    EVENT_JOB_EXECUTED,\n+    EVENT_JOB_MISSED,\n+    JobExecutionEvent,\n+)\n \n \n class MaxInstancesReachedError(Exception):\n     def __init__(self, job):\n-        super(MaxInstancesReachedError, self).__init__(\n-            'Job \"%s\" has already reached its maximum number of instances (%d)' %\n-            (job.id, job.max_instances))\n+        super().__init__(\n+            f'Job \"{job.id}\" has already reached its maximum number of instances '\n+            f\"({job.max_instances})\"\n+        )\n \n \n-class BaseExecutor(six.with_metaclass(ABCMeta, object)):\n+class BaseExecutor(metaclass=ABCMeta):\n     \"\"\"Abstract base class that defines the interface that every executor must implement.\"\"\"\n \n     _scheduler = None\n     _lock = None\n-    _logger = logging.getLogger('apscheduler.executors')\n+    _logger = logging.getLogger(\"apscheduler.executors\")\n \n     def __init__(self):\n-        super(BaseExecutor, self).__init__()\n+        super().__init__()\n         self._instances = defaultdict(lambda: 0)\n \n     def start(self, scheduler, alias):\n@@ -42,7 +45,7 @@ def start(self, scheduler, alias):\n         \"\"\"\n         self._scheduler = scheduler\n         self._lock = scheduler._create_lock()\n-        self._logger = logging.getLogger('apscheduler.executors.%s' % alias)\n+        self._logger = logging.getLogger(f\"apscheduler.executors.{alias}\")\n \n     def shutdown(self, wait=True):\n         \"\"\"\n@@ -63,7 +66,7 @@ def submit_job(self, job, run_times):\n             allowed instances for this job has been reached\n \n         \"\"\"\n-        assert self._lock is not None, 'This executor has not been started yet'\n+        assert self._lock is not None, \"This executor has not been started yet\"\n         with self._lock:\n             if self._instances[job.id] >= job.max_instances:\n                 raise MaxInstancesReachedError(job)\n@@ -97,7 +100,7 @@ def _run_job_error(self, job_id, exc, traceback=None):\n                 del self._instances[job_id]\n \n         exc_info = (exc.__class__, exc, traceback)\n-        self._logger.error('Error running job %s', job_id, exc_info=exc_info)\n+        self._logger.error(\"Error running job %s\", job_id, exc_info=exc_info)\n \n \n def run_job(job, jobstore_alias, run_times, logger_name):\n@@ -112,11 +115,14 @@ def run_job(job, jobstore_alias, run_times, logger_name):\n         # See if the job missed its run time window, and handle\n         # possible misfires accordingly\n         if job.misfire_grace_time is not None:\n-            difference = datetime.now(utc) - run_time\n+            difference = datetime.now(timezone.utc) - run_time\n             grace_time = timedelta(seconds=job.misfire_grace_time)\n             if difference > grace_time:\n-                events.append(JobExecutionEvent(EVENT_JOB_MISSED, job.id, jobstore_alias,\n-                                                run_time))\n+                events.append(\n+                    JobExecutionEvent(\n+                        EVENT_JOB_MISSED, job.id, jobstore_alias, run_time\n+                    )\n+                )\n                 logger.warning('Run time of job \"%s\" was missed by %s', job, difference)\n                 continue\n \n@@ -125,22 +131,75 @@ def run_job(job, jobstore_alias, run_times, logger_name):\n             retval = job.func(*job.args, **job.kwargs)\n         except BaseException:\n             exc, tb = sys.exc_info()[1:]\n-            formatted_tb = ''.join(format_tb(tb))\n-            events.append(JobExecutionEvent(EVENT_JOB_ERROR, job.id, jobstore_alias, run_time,\n-                                            exception=exc, traceback=formatted_tb))\n+            formatted_tb = \"\".join(format_tb(tb))\n+            events.append(\n+                JobExecutionEvent(\n+                    EVENT_JOB_ERROR,\n+                    job.id,\n+                    jobstore_alias,\n+                    run_time,\n+                    exception=exc,\n+                    traceback=formatted_tb,\n+                )\n+            )\n             logger.exception('Job \"%s\" raised an exception', job)\n \n             # This is to prevent cyclic references that would lead to memory leaks\n-            if six.PY2:\n-                sys.exc_clear()\n-                del tb\n-            else:\n-                import traceback\n-                traceback.clear_frames(tb)\n-                del tb\n+            traceback.clear_frames(tb)\n+            del tb\n+        else:\n+            events.append(\n+                JobExecutionEvent(\n+                    EVENT_JOB_EXECUTED, job.id, jobstore_alias, run_time, retval=retval\n+                )\n+            )\n+            logger.info('Job \"%s\" executed successfully', job)\n+\n+    return events\n+\n+\n+async def run_coroutine_job(job, jobstore_alias, run_times, logger_name):\n+    \"\"\"Coroutine version of run_job().\"\"\"\n+    events = []\n+    logger = logging.getLogger(logger_name)\n+    for run_time in run_times:\n+        # See if the job missed its run time window, and handle possible misfires accordingly\n+        if job.misfire_grace_time is not None:\n+            difference = datetime.now(timezone.utc) - run_time\n+            grace_time = timedelta(seconds=job.misfire_grace_time)\n+            if difference > grace_time:\n+                events.append(\n+                    JobExecutionEvent(\n+                        EVENT_JOB_MISSED, job.id, jobstore_alias, run_time\n+                    )\n+                )\n+                logger.warning('Run time of job \"%s\" was missed by %s', job, difference)\n+                continue\n+\n+        logger.info('Running job \"%s\" (scheduled at %s)', job, run_time)\n+        try:\n+            retval = await job.func(*job.args, **job.kwargs)\n+        except BaseException:\n+            exc, tb = sys.exc_info()[1:]\n+            formatted_tb = \"\".join(format_tb(tb))\n+            events.append(\n+                JobExecutionEvent(\n+                    EVENT_JOB_ERROR,\n+                    job.id,\n+                    jobstore_alias,\n+                    run_time,\n+                    exception=exc,\n+                    traceback=formatted_tb,\n+                )\n+            )\n+            logger.exception('Job \"%s\" raised an exception', job)\n+            traceback.clear_frames(tb)\n         else:\n-            events.append(JobExecutionEvent(EVENT_JOB_EXECUTED, job.id, jobstore_alias, run_time,\n-                                            retval=retval))\n+            events.append(\n+                JobExecutionEvent(\n+                    EVENT_JOB_EXECUTED, job.id, jobstore_alias, run_time, retval=retval\n+                )\n+            )\n             logger.info('Job \"%s\" executed successfully', job)\n \n     return events"}, {"sha": "7111d2aec53a39d102f95d1120eb905fb5fe05cb", "filename": "lib/apscheduler/executors/base_py3.py", "status": "removed", "additions": 0, "deletions": 43, "changes": 43, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fapscheduler%2Fexecutors%2Fbase_py3.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fapscheduler%2Fexecutors%2Fbase_py3.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fexecutors%2Fbase_py3.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,43 +0,0 @@\n-import logging\n-import sys\n-import traceback\n-from datetime import datetime, timedelta\n-from traceback import format_tb\n-\n-from pytz import utc\n-\n-from apscheduler.events import (\n-    JobExecutionEvent, EVENT_JOB_MISSED, EVENT_JOB_ERROR, EVENT_JOB_EXECUTED)\n-\n-\n-async def run_coroutine_job(job, jobstore_alias, run_times, logger_name):\n-    \"\"\"Coroutine version of run_job().\"\"\"\n-    events = []\n-    logger = logging.getLogger(logger_name)\n-    for run_time in run_times:\n-        # See if the job missed its run time window, and handle possible misfires accordingly\n-        if job.misfire_grace_time is not None:\n-            difference = datetime.now(utc) - run_time\n-            grace_time = timedelta(seconds=job.misfire_grace_time)\n-            if difference > grace_time:\n-                events.append(JobExecutionEvent(EVENT_JOB_MISSED, job.id, jobstore_alias,\n-                                                run_time))\n-                logger.warning('Run time of job \"%s\" was missed by %s', job, difference)\n-                continue\n-\n-        logger.info('Running job \"%s\" (scheduled at %s)', job, run_time)\n-        try:\n-            retval = await job.func(*job.args, **job.kwargs)\n-        except BaseException:\n-            exc, tb = sys.exc_info()[1:]\n-            formatted_tb = ''.join(format_tb(tb))\n-            events.append(JobExecutionEvent(EVENT_JOB_ERROR, job.id, jobstore_alias, run_time,\n-                                            exception=exc, traceback=formatted_tb))\n-            logger.exception('Job \"%s\" raised an exception', job)\n-            traceback.clear_frames(tb)\n-        else:\n-            events.append(JobExecutionEvent(EVENT_JOB_EXECUTED, job.id, jobstore_alias, run_time,\n-                                            retval=retval))\n-            logger.info('Job \"%s\" executed successfully', job)\n-\n-    return events"}, {"sha": "385be40c7815ea6571fac5ace3531fef51886c6d", "filename": "lib/apscheduler/executors/gevent.py", "status": "modified", "additions": 5, "deletions": 6, "changes": 11, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Fgevent.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Fgevent.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fexecutors%2Fgevent.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,13 +1,11 @@\n-from __future__ import absolute_import\n import sys\n \n from apscheduler.executors.base import BaseExecutor, run_job\n \n-\n try:\n     import gevent\n-except ImportError:  # pragma: nocover\n-    raise ImportError('GeventExecutor requires gevent installed')\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"GeventExecutor requires gevent installed\") from exc\n \n \n class GeventExecutor(BaseExecutor):\n@@ -26,5 +24,6 @@ def callback(greenlet):\n             else:\n                 self._run_job_success(job.id, events)\n \n-        gevent.spawn(run_job, job, job._jobstore_alias, run_times, self._logger.name).\\\n-            link(callback)\n+        gevent.spawn(\n+            run_job, job, job._jobstore_alias, run_times, self._logger.name\n+        ).link(callback)"}, {"sha": "166de7c7dd2fe521a1ce49ea7caaebf3c4a26e43", "filename": "lib/apscheduler/executors/pool.py", "status": "modified", "additions": 31, "deletions": 20, "changes": 51, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Fpool.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Fpool.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fexecutors%2Fpool.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,36 +1,32 @@\n-from abc import abstractmethod\n import concurrent.futures\n+import multiprocessing\n+from abc import abstractmethod\n+from concurrent.futures.process import BrokenProcessPool\n \n from apscheduler.executors.base import BaseExecutor, run_job\n \n-try:\n-    from concurrent.futures.process import BrokenProcessPool\n-except ImportError:\n-    BrokenProcessPool = None\n-\n \n class BasePoolExecutor(BaseExecutor):\n     @abstractmethod\n     def __init__(self, pool):\n-        super(BasePoolExecutor, self).__init__()\n+        super().__init__()\n         self._pool = pool\n \n     def _do_submit_job(self, job, run_times):\n         def callback(f):\n-            exc, tb = (f.exception_info() if hasattr(f, 'exception_info') else\n-                       (f.exception(), getattr(f.exception(), '__traceback__', None)))\n+            exc, tb = (\n+                f.exception_info()\n+                if hasattr(f, \"exception_info\")\n+                else (f.exception(), getattr(f.exception(), \"__traceback__\", None))\n+            )\n             if exc:\n                 self._run_job_error(job.id, exc, tb)\n             else:\n                 self._run_job_success(job.id, f.result())\n \n-        try:\n-            f = self._pool.submit(run_job, job, job._jobstore_alias, run_times, self._logger.name)\n-        except BrokenProcessPool:\n-            self._logger.warning('Process pool is broken; replacing pool with a fresh instance')\n-            self._pool = self._pool.__class__(self._pool._max_workers)\n-            f = self._pool.submit(run_job, job, job._jobstore_alias, run_times, self._logger.name)\n-\n+        f = self._pool.submit(\n+            run_job, job, job._jobstore_alias, run_times, self._logger.name\n+        )\n         f.add_done_callback(callback)\n \n     def shutdown(self, wait=True):\n@@ -51,7 +47,7 @@ class ThreadPoolExecutor(BasePoolExecutor):\n     def __init__(self, max_workers=10, pool_kwargs=None):\n         pool_kwargs = pool_kwargs or {}\n         pool = concurrent.futures.ThreadPoolExecutor(int(max_workers), **pool_kwargs)\n-        super(ThreadPoolExecutor, self).__init__(pool)\n+        super().__init__(pool)\n \n \n class ProcessPoolExecutor(BasePoolExecutor):\n@@ -66,6 +62,21 @@ class ProcessPoolExecutor(BasePoolExecutor):\n     \"\"\"\n \n     def __init__(self, max_workers=10, pool_kwargs=None):\n-        pool_kwargs = pool_kwargs or {}\n-        pool = concurrent.futures.ProcessPoolExecutor(int(max_workers), **pool_kwargs)\n-        super(ProcessPoolExecutor, self).__init__(pool)\n+        self.pool_kwargs = pool_kwargs or {}\n+        self.pool_kwargs.setdefault(\"mp_context\", multiprocessing.get_context(\"spawn\"))\n+        pool = concurrent.futures.ProcessPoolExecutor(\n+            int(max_workers), **self.pool_kwargs\n+        )\n+        super().__init__(pool)\n+\n+    def _do_submit_job(self, job, run_times):\n+        try:\n+            super()._do_submit_job(job, run_times)\n+        except BrokenProcessPool:\n+            self._logger.warning(\n+                \"Process pool is broken; replacing pool with a fresh instance\"\n+            )\n+            self._pool = self._pool.__class__(\n+                self._pool._max_workers, **self.pool_kwargs\n+            )\n+            super()._do_submit_job(job, run_times)"}, {"sha": "46789bd94a725d9692e875caef58f6c57676bcae", "filename": "lib/apscheduler/executors/tornado.py", "status": "modified", "additions": 10, "deletions": 15, "changes": 25, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Ftornado.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Ftornado.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fexecutors%2Ftornado.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,18 +1,10 @@\n-from __future__ import absolute_import\n-\n import sys\n from concurrent.futures import ThreadPoolExecutor\n \n from tornado.gen import convert_yielded\n \n-from apscheduler.executors.base import BaseExecutor, run_job\n-\n-try:\n-    from apscheduler.executors.base_py3 import run_coroutine_job\n-    from apscheduler.util import iscoroutinefunction_partial\n-except ImportError:\n-    def iscoroutinefunction_partial(func):\n-        return False\n+from apscheduler.executors.base import BaseExecutor, run_coroutine_job, run_job\n+from apscheduler.util import iscoroutinefunction_partial\n \n \n class TornadoExecutor(BaseExecutor):\n@@ -28,11 +20,11 @@ class TornadoExecutor(BaseExecutor):\n     \"\"\"\n \n     def __init__(self, max_workers=10):\n-        super(TornadoExecutor, self).__init__()\n+        super().__init__()\n         self.executor = ThreadPoolExecutor(max_workers)\n \n     def start(self, scheduler, alias):\n-        super(TornadoExecutor, self).start(scheduler, alias)\n+        super().start(scheduler, alias)\n         self._ioloop = scheduler._ioloop\n \n     def _do_submit_job(self, job, run_times):\n@@ -45,10 +37,13 @@ def callback(f):\n                 self._run_job_success(job.id, events)\n \n         if iscoroutinefunction_partial(job.func):\n-            f = run_coroutine_job(job, job._jobstore_alias, run_times, self._logger.name)\n+            f = run_coroutine_job(\n+                job, job._jobstore_alias, run_times, self._logger.name\n+            )\n         else:\n-            f = self.executor.submit(run_job, job, job._jobstore_alias, run_times,\n-                                     self._logger.name)\n+            f = self.executor.submit(\n+                run_job, job, job._jobstore_alias, run_times, self._logger.name\n+            )\n \n         f = convert_yielded(f)\n         f.add_done_callback(callback)"}, {"sha": "710b20ae39350ba8cfbd53ad4890138035064d4c", "filename": "lib/apscheduler/executors/twisted.py", "status": "modified", "additions": 3, "deletions": 4, "changes": 7, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Ftwisted.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fexecutors%2Ftwisted.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fexecutors%2Ftwisted.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,5 +1,3 @@\n-from __future__ import absolute_import\n-\n from apscheduler.executors.base import BaseExecutor, run_job\n \n \n@@ -11,7 +9,7 @@ class TwistedExecutor(BaseExecutor):\n     \"\"\"\n \n     def start(self, scheduler, alias):\n-        super(TwistedExecutor, self).start(scheduler, alias)\n+        super().start(scheduler, alias)\n         self._reactor = scheduler._reactor\n \n     def _do_submit_job(self, job, run_times):\n@@ -22,4 +20,5 @@ def callback(success, result):\n                 self._run_job_error(job.id, result.value, result.tb)\n \n         self._reactor.getThreadPool().callInThreadWithCallback(\n-            callback, run_job, job, job._jobstore_alias, run_times, self._logger.name)\n+            callback, run_job, job, job._jobstore_alias, run_times, self._logger.name\n+        )"}, {"sha": "38ac305b9814a859aaf20bb0efe47ccc362ceb63", "filename": "lib/apscheduler/job.py", "status": "modified", "additions": 146, "deletions": 115, "changes": 261, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjob.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjob.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fjob.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,20 +1,22 @@\n-from inspect import ismethod, isclass\n+from collections.abc import Iterable, Mapping\n+from datetime import timezone\n+from inspect import isclass, ismethod\n from uuid import uuid4\n \n-import six\n-\n from apscheduler.triggers.base import BaseTrigger\n from apscheduler.util import (\n-    ref_to_obj, obj_to_ref, datetime_repr, repr_escape, get_callable_name, check_callable_args,\n-    convert_to_datetime)\n+    check_callable_args,\n+    convert_to_datetime,\n+    datetime_repr,\n+    get_callable_name,\n+    obj_to_ref,\n+    ref_to_obj,\n+)\n \n-try:\n-    from collections.abc import Iterable, Mapping\n-except ImportError:\n-    from collections import Iterable, Mapping\n+UTC = timezone.utc\n \n \n-class Job(object):\n+class Job:\n     \"\"\"\n     Contains the options given when scheduling callables and its current schedule and other state.\n     This class should never be instantiated by the user.\n@@ -38,12 +40,26 @@ class Job(object):\n         :ref:`missed-job-executions` section in the documentation for an in-depth explanation.\n     \"\"\"\n \n-    __slots__ = ('_scheduler', '_jobstore_alias', 'id', 'trigger', 'executor', 'func', 'func_ref',\n-                 'args', 'kwargs', 'name', 'misfire_grace_time', 'coalesce', 'max_instances',\n-                 'next_run_time', '__weakref__')\n+    __slots__ = (\n+        \"__weakref__\",\n+        \"_jobstore_alias\",\n+        \"_scheduler\",\n+        \"args\",\n+        \"coalesce\",\n+        \"executor\",\n+        \"func\",\n+        \"func_ref\",\n+        \"id\",\n+        \"kwargs\",\n+        \"max_instances\",\n+        \"misfire_grace_time\",\n+        \"name\",\n+        \"next_run_time\",\n+        \"trigger\",\n+    )\n \n     def __init__(self, scheduler, id=None, **kwargs):\n-        super(Job, self).__init__()\n+        super().__init__()\n         self._scheduler = scheduler\n         self._jobstore_alias = None\n         self._modify(id=id or uuid4().hex, **kwargs)\n@@ -71,7 +87,9 @@ def reschedule(self, trigger, **trigger_args):\n         :return Job: this job instance\n \n         \"\"\"\n-        self._scheduler.reschedule_job(self.id, self._jobstore_alias, trigger, **trigger_args)\n+        self._scheduler.reschedule_job(\n+            self.id, self._jobstore_alias, trigger, **trigger_args\n+        )\n         return self\n \n     def pause(self):\n@@ -130,7 +148,7 @@ def _get_run_times(self, now):\n         \"\"\"\n         run_times = []\n         next_run_time = self.next_run_time\n-        while next_run_time and next_run_time <= now:\n+        while next_run_time and next_run_time.astimezone(UTC) <= now.astimezone(UTC):\n             run_times.append(next_run_time)\n             next_run_time = self.trigger.get_next_fire_time(next_run_time, now)\n \n@@ -144,20 +162,20 @@ def _modify(self, **changes):\n         \"\"\"\n         approved = {}\n \n-        if 'id' in changes:\n-            value = changes.pop('id')\n-            if not isinstance(value, six.string_types):\n+        if \"id\" in changes:\n+            value = changes.pop(\"id\")\n+            if not isinstance(value, str):\n                 raise TypeError(\"id must be a nonempty string\")\n-            if hasattr(self, 'id'):\n-                raise ValueError('The job ID may not be changed')\n-            approved['id'] = value\n+            if hasattr(self, \"id\"):\n+                raise ValueError(\"The job ID may not be changed\")\n+            approved[\"id\"] = value\n \n-        if 'func' in changes or 'args' in changes or 'kwargs' in changes:\n-            func = changes.pop('func') if 'func' in changes else self.func\n-            args = changes.pop('args') if 'args' in changes else self.args\n-            kwargs = changes.pop('kwargs') if 'kwargs' in changes else self.kwargs\n+        if \"func\" in changes or \"args\" in changes or \"kwargs\" in changes:\n+            func = changes.pop(\"func\") if \"func\" in changes else self.func\n+            args = changes.pop(\"args\") if \"args\" in changes else self.args\n+            kwargs = changes.pop(\"kwargs\") if \"kwargs\" in changes else self.kwargs\n \n-            if isinstance(func, six.string_types):\n+            if isinstance(func, str):\n                 func_ref = func\n                 func = ref_to_obj(func)\n             elif callable(func):\n@@ -167,136 +185,149 @@ def _modify(self, **changes):\n                     # If this happens, this Job won't be serializable\n                     func_ref = None\n             else:\n-                raise TypeError('func must be a callable or a textual reference to one')\n+                raise TypeError(\"func must be a callable or a textual reference to one\")\n \n-            if not hasattr(self, 'name') and changes.get('name', None) is None:\n-                changes['name'] = get_callable_name(func)\n+            if not hasattr(self, \"name\") and changes.get(\"name\", None) is None:\n+                changes[\"name\"] = get_callable_name(func)\n \n-            if isinstance(args, six.string_types) or not isinstance(args, Iterable):\n-                raise TypeError('args must be a non-string iterable')\n-            if isinstance(kwargs, six.string_types) or not isinstance(kwargs, Mapping):\n-                raise TypeError('kwargs must be a dict-like object')\n+            if isinstance(args, str) or not isinstance(args, Iterable):\n+                raise TypeError(\"args must be a non-string iterable\")\n+            if isinstance(kwargs, str) or not isinstance(kwargs, Mapping):\n+                raise TypeError(\"kwargs must be a dict-like object\")\n \n             check_callable_args(func, args, kwargs)\n \n-            approved['func'] = func\n-            approved['func_ref'] = func_ref\n-            approved['args'] = args\n-            approved['kwargs'] = kwargs\n+            approved[\"func\"] = func\n+            approved[\"func_ref\"] = func_ref\n+            approved[\"args\"] = args\n+            approved[\"kwargs\"] = kwargs\n \n-        if 'name' in changes:\n-            value = changes.pop('name')\n-            if not value or not isinstance(value, six.string_types):\n+        if \"name\" in changes:\n+            value = changes.pop(\"name\")\n+            if not value or not isinstance(value, str):\n                 raise TypeError(\"name must be a nonempty string\")\n-            approved['name'] = value\n-\n-        if 'misfire_grace_time' in changes:\n-            value = changes.pop('misfire_grace_time')\n-            if value is not None and (not isinstance(value, six.integer_types) or value <= 0):\n-                raise TypeError('misfire_grace_time must be either None or a positive integer')\n-            approved['misfire_grace_time'] = value\n-\n-        if 'coalesce' in changes:\n-            value = bool(changes.pop('coalesce'))\n-            approved['coalesce'] = value\n-\n-        if 'max_instances' in changes:\n-            value = changes.pop('max_instances')\n-            if not isinstance(value, six.integer_types) or value <= 0:\n-                raise TypeError('max_instances must be a positive integer')\n-            approved['max_instances'] = value\n-\n-        if 'trigger' in changes:\n-            trigger = changes.pop('trigger')\n+            approved[\"name\"] = value\n+\n+        if \"misfire_grace_time\" in changes:\n+            value = changes.pop(\"misfire_grace_time\")\n+            if value is not None and (not isinstance(value, int) or value <= 0):\n+                raise TypeError(\n+                    \"misfire_grace_time must be either None or a positive integer\"\n+                )\n+            approved[\"misfire_grace_time\"] = value\n+\n+        if \"coalesce\" in changes:\n+            value = bool(changes.pop(\"coalesce\"))\n+            approved[\"coalesce\"] = value\n+\n+        if \"max_instances\" in changes:\n+            value = changes.pop(\"max_instances\")\n+            if not isinstance(value, int) or value <= 0:\n+                raise TypeError(\"max_instances must be a positive integer\")\n+            approved[\"max_instances\"] = value\n+\n+        if \"trigger\" in changes:\n+            trigger = changes.pop(\"trigger\")\n             if not isinstance(trigger, BaseTrigger):\n-                raise TypeError('Expected a trigger instance, got %s instead' %\n-                                trigger.__class__.__name__)\n+                raise TypeError(\n+                    f\"Expected a trigger instance, got {trigger.__class__.__name__} instead\"\n+                )\n \n-            approved['trigger'] = trigger\n+            approved[\"trigger\"] = trigger\n \n-        if 'executor' in changes:\n-            value = changes.pop('executor')\n-            if not isinstance(value, six.string_types):\n-                raise TypeError('executor must be a string')\n-            approved['executor'] = value\n+        if \"executor\" in changes:\n+            value = changes.pop(\"executor\")\n+            if not isinstance(value, str):\n+                raise TypeError(\"executor must be a string\")\n+            approved[\"executor\"] = value\n \n-        if 'next_run_time' in changes:\n-            value = changes.pop('next_run_time')\n-            approved['next_run_time'] = convert_to_datetime(value, self._scheduler.timezone,\n-                                                            'next_run_time')\n+        if \"next_run_time\" in changes:\n+            value = changes.pop(\"next_run_time\")\n+            approved[\"next_run_time\"] = convert_to_datetime(\n+                value, self._scheduler.timezone, \"next_run_time\"\n+            )\n \n         if changes:\n-            raise AttributeError('The following are not modifiable attributes of Job: %s' %\n-                                 ', '.join(changes))\n+            raise AttributeError(\n+                \"The following are not modifiable attributes of Job: {}\".format(\n+                    \", \".join(changes)\n+                )\n+            )\n \n-        for key, value in six.iteritems(approved):\n+        for key, value in approved.items():\n             setattr(self, key, value)\n \n     def __getstate__(self):\n         # Don't allow this Job to be serialized if the function reference could not be determined\n         if not self.func_ref:\n             raise ValueError(\n-                'This Job cannot be serialized since the reference to its callable (%r) could not '\n-                'be determined. Consider giving a textual reference (module:function name) '\n-                'instead.' % (self.func,))\n+                f\"This Job cannot be serialized since the reference to its callable ({self.func!r}) could not \"\n+                \"be determined. Consider giving a textual reference (module:function name) \"\n+                \"instead.\"\n+            )\n \n         # Instance methods cannot survive serialization as-is, so store the \"self\" argument\n         # explicitly\n         func = self.func\n-        if ismethod(func) and not isclass(func.__self__) and obj_to_ref(func) == self.func_ref:\n+        if (\n+            ismethod(func)\n+            and not isclass(func.__self__)\n+            and obj_to_ref(func) == self.func_ref\n+        ):\n             args = (func.__self__,) + tuple(self.args)\n         else:\n             args = self.args\n \n         return {\n-            'version': 1,\n-            'id': self.id,\n-            'func': self.func_ref,\n-            'trigger': self.trigger,\n-            'executor': self.executor,\n-            'args': args,\n-            'kwargs': self.kwargs,\n-            'name': self.name,\n-            'misfire_grace_time': self.misfire_grace_time,\n-            'coalesce': self.coalesce,\n-            'max_instances': self.max_instances,\n-            'next_run_time': self.next_run_time\n+            \"version\": 1,\n+            \"id\": self.id,\n+            \"func\": self.func_ref,\n+            \"trigger\": self.trigger,\n+            \"executor\": self.executor,\n+            \"args\": args,\n+            \"kwargs\": self.kwargs,\n+            \"name\": self.name,\n+            \"misfire_grace_time\": self.misfire_grace_time,\n+            \"coalesce\": self.coalesce,\n+            \"max_instances\": self.max_instances,\n+            \"next_run_time\": self.next_run_time,\n         }\n \n     def __setstate__(self, state):\n-        if state.get('version', 1) > 1:\n-            raise ValueError('Job has version %s, but only version 1 can be handled' %\n-                             state['version'])\n+        if state.get(\"version\", 1) > 1:\n+            raise ValueError(\n+                f\"Job has version {state['version']}, but only version 1 can be handled\"\n+            )\n \n-        self.id = state['id']\n-        self.func_ref = state['func']\n+        self.id = state[\"id\"]\n+        self.func_ref = state[\"func\"]\n         self.func = ref_to_obj(self.func_ref)\n-        self.trigger = state['trigger']\n-        self.executor = state['executor']\n-        self.args = state['args']\n-        self.kwargs = state['kwargs']\n-        self.name = state['name']\n-        self.misfire_grace_time = state['misfire_grace_time']\n-        self.coalesce = state['coalesce']\n-        self.max_instances = state['max_instances']\n-        self.next_run_time = state['next_run_time']\n+        self.trigger = state[\"trigger\"]\n+        self.executor = state[\"executor\"]\n+        self.args = state[\"args\"]\n+        self.kwargs = state[\"kwargs\"]\n+        self.name = state[\"name\"]\n+        self.misfire_grace_time = state[\"misfire_grace_time\"]\n+        self.coalesce = state[\"coalesce\"]\n+        self.max_instances = state[\"max_instances\"]\n+        self.next_run_time = state[\"next_run_time\"]\n \n     def __eq__(self, other):\n         if isinstance(other, Job):\n             return self.id == other.id\n         return NotImplemented\n \n     def __repr__(self):\n-        return '<Job (id=%s name=%s)>' % (repr_escape(self.id), repr_escape(self.name))\n+        return f\"<Job (id={self.id} name={self.name})>\"\n \n     def __str__(self):\n-        return repr_escape(self.__unicode__())\n-\n-    def __unicode__(self):\n-        if hasattr(self, 'next_run_time'):\n-            status = ('next run at: ' + datetime_repr(self.next_run_time) if\n-                      self.next_run_time else 'paused')\n+        if hasattr(self, \"next_run_time\"):\n+            status = (\n+                \"next run at: \" + datetime_repr(self.next_run_time)\n+                if self.next_run_time\n+                else \"paused\"\n+            )\n         else:\n-            status = 'pending'\n+            status = \"pending\"\n \n-        return u'%s (trigger: %s, %s)' % (self.name, self.trigger, status)\n+        return f\"{self.name} (trigger: {self.trigger}, {status})\""}, {"sha": "01cabd1ec9e2af2e30394426b0e9daae28d9df96", "filename": "lib/apscheduler/jobstores/base.py", "status": "modified", "additions": 11, "deletions": 13, "changes": 24, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fbase.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fbase.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fjobstores%2Fbase.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,22 +1,19 @@\n-from abc import ABCMeta, abstractmethod\n import logging\n-\n-import six\n+from abc import ABCMeta, abstractmethod\n \n \n class JobLookupError(KeyError):\n     \"\"\"Raised when the job store cannot find a job for update or removal.\"\"\"\n \n     def __init__(self, job_id):\n-        super(JobLookupError, self).__init__(u'No job by the id of %s was found' % job_id)\n+        super().__init__(f\"No job by the id of {job_id} was found\")\n \n \n class ConflictingIdError(KeyError):\n     \"\"\"Raised when the uniqueness of job IDs is being violated.\"\"\"\n \n     def __init__(self, job_id):\n-        super(ConflictingIdError, self).__init__(\n-            u'Job identifier (%s) conflicts with an existing job' % job_id)\n+        super().__init__(f\"Job identifier ({job_id}) conflicts with an existing job\")\n \n \n class TransientJobError(ValueError):\n@@ -26,17 +23,18 @@ class TransientJobError(ValueError):\n     \"\"\"\n \n     def __init__(self, job_id):\n-        super(TransientJobError, self).__init__(\n-            u'Job (%s) cannot be added to this job store because a reference to the callable '\n-            u'could not be determined.' % job_id)\n+        super().__init__(\n+            f\"Job ({job_id}) cannot be added to this job store because a reference to the callable \"\n+            \"could not be determined.\"\n+        )\n \n \n-class BaseJobStore(six.with_metaclass(ABCMeta)):\n+class BaseJobStore(metaclass=ABCMeta):\n     \"\"\"Abstract base class that defines the interface that every job store must implement.\"\"\"\n \n     _scheduler = None\n     _alias = None\n-    _logger = logging.getLogger('apscheduler.jobstores')\n+    _logger = logging.getLogger(\"apscheduler.jobstores\")\n \n     def start(self, scheduler, alias):\n         \"\"\"\n@@ -50,7 +48,7 @@ def start(self, scheduler, alias):\n \n         self._scheduler = scheduler\n         self._alias = alias\n-        self._logger = logging.getLogger('apscheduler.jobstores.%s' % alias)\n+        self._logger = logging.getLogger(f\"apscheduler.jobstores.{alias}\")\n \n     def shutdown(self):\n         \"\"\"Frees any resources still bound to this job store.\"\"\"\n@@ -140,4 +138,4 @@ def remove_all_jobs(self):\n         \"\"\"Removes all jobs from this store.\"\"\"\n \n     def __repr__(self):\n-        return '<%s>' % self.__class__.__name__\n+        return f\"<{self.__class__.__name__}>\""}, {"sha": "3fe74ff168403ffade1d813fd93bfe725f3d393a", "filename": "lib/apscheduler/jobstores/etcd.py", "status": "added", "additions": 170, "deletions": 0, "changes": 170, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fetcd.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fetcd.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fjobstores%2Fetcd.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,170 @@\n+import pickle\n+from datetime import datetime, timezone\n+\n+from apscheduler.job import Job\n+from apscheduler.jobstores.base import BaseJobStore, ConflictingIdError, JobLookupError\n+from apscheduler.util import (\n+    datetime_to_utc_timestamp,\n+    maybe_ref,\n+    utc_timestamp_to_datetime,\n+)\n+\n+try:\n+    from etcd3 import Etcd3Client\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"EtcdJobStore requires etcd3 be installed\") from exc\n+\n+\n+class EtcdJobStore(BaseJobStore):\n+    \"\"\"\n+    Stores jobs in a etcd. Any leftover keyword arguments are directly passed to\n+    etcd3's `etcd3.client\n+    <https://python-etcd3.readthedocs.io/en/latest/readme.html>`_.\n+\n+    Plugin alias: ``etcd``\n+\n+    :param str path: path to store jobs in\n+    :param client: a :class:`~etcd3.client.etcd3` instance to use instead of\n+        providing connection arguments\n+    :param int pickle_protocol: pickle protocol level to use (for serialization), defaults to the\n+        highest available\n+    \"\"\"\n+\n+    def __init__(\n+        self,\n+        path=\"/apscheduler\",\n+        client=None,\n+        close_connection_on_exit=False,\n+        pickle_protocol=pickle.DEFAULT_PROTOCOL,\n+        **connect_args,\n+    ):\n+        super().__init__()\n+        self.pickle_protocol = pickle_protocol\n+        self.close_connection_on_exit = close_connection_on_exit\n+\n+        if not path:\n+            raise ValueError('The \"path\" parameter must not be empty')\n+\n+        self.path = path\n+\n+        if client:\n+            self.client = maybe_ref(client)\n+        else:\n+            self.client = Etcd3Client(**connect_args)\n+\n+    def lookup_job(self, job_id):\n+        node_path = self.path + \"/\" + str(job_id)\n+        try:\n+            content, _ = self.client.get(node_path)\n+            content = pickle.loads(content)\n+            job = self._reconstitute_job(content[\"job_state\"])\n+            return job\n+        except BaseException:\n+            return None\n+\n+    def get_due_jobs(self, now):\n+        timestamp = datetime_to_utc_timestamp(now)\n+        jobs = [\n+            job_record[\"job\"]\n+            for job_record in self._get_jobs()\n+            if job_record[\"next_run_time\"] is not None\n+            and job_record[\"next_run_time\"] <= timestamp\n+        ]\n+        return jobs\n+\n+    def get_next_run_time(self):\n+        next_runs = [\n+            job_record[\"next_run_time\"]\n+            for job_record in self._get_jobs()\n+            if job_record[\"next_run_time\"] is not None\n+        ]\n+        return utc_timestamp_to_datetime(min(next_runs)) if len(next_runs) > 0 else None\n+\n+    def get_all_jobs(self):\n+        jobs = [job_record[\"job\"] for job_record in self._get_jobs()]\n+        self._fix_paused_jobs_sorting(jobs)\n+        return jobs\n+\n+    def add_job(self, job):\n+        node_path = self.path + \"/\" + str(job.id)\n+        value = {\n+            \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+            \"job_state\": job.__getstate__(),\n+        }\n+        data = pickle.dumps(value, self.pickle_protocol)\n+        status = self.client.put_if_not_exists(node_path, value=data)\n+        if not status:\n+            raise ConflictingIdError(job.id)\n+\n+    def update_job(self, job):\n+        node_path = self.path + \"/\" + str(job.id)\n+        changes = {\n+            \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+            \"job_state\": job.__getstate__(),\n+        }\n+        data = pickle.dumps(changes, self.pickle_protocol)\n+        status, _ = self.client.transaction(\n+            compare=[self.client.transactions.version(node_path) > 0],\n+            success=[self.client.transactions.put(node_path, value=data)],\n+            failure=[],\n+        )\n+        if not status:\n+            raise JobLookupError(job.id)\n+\n+    def remove_job(self, job_id):\n+        node_path = self.path + \"/\" + str(job_id)\n+        status, _ = self.client.transaction(\n+            compare=[self.client.transactions.version(node_path) > 0],\n+            success=[self.client.transactions.delete(node_path)],\n+            failure=[],\n+        )\n+        if not status:\n+            raise JobLookupError(job_id)\n+\n+    def remove_all_jobs(self):\n+        self.client.delete_prefix(self.path)\n+\n+    def shutdown(self):\n+        self.client.close()\n+\n+    def _reconstitute_job(self, job_state):\n+        job_state = job_state\n+        job = Job.__new__(Job)\n+        job.__setstate__(job_state)\n+        job._scheduler = self._scheduler\n+        job._jobstore_alias = self._alias\n+        return job\n+\n+    def _get_jobs(self):\n+        jobs = []\n+        failed_job_ids = []\n+        all_ids = list(self.client.get_prefix(self.path))\n+\n+        for doc, _ in all_ids:\n+            try:\n+                content = pickle.loads(doc)\n+                job_record = {\n+                    \"next_run_time\": content[\"next_run_time\"],\n+                    \"job\": self._reconstitute_job(content[\"job_state\"]),\n+                }\n+                jobs.append(job_record)\n+            except BaseException:\n+                content = pickle.loads(doc)\n+                failed_id = content[\"job_state\"][\"id\"]\n+                failed_job_ids.append(failed_id)\n+                self._logger.exception(\n+                    'Unable to restore job \"%s\" -- removing it', failed_id\n+                )\n+\n+        if failed_job_ids:\n+            for failed_id in failed_job_ids:\n+                self.remove_job(failed_id)\n+        paused_sort_key = datetime(9999, 12, 31, tzinfo=timezone.utc)\n+        return sorted(\n+            jobs,\n+            key=lambda job_record: job_record[\"job\"].next_run_time or paused_sort_key,\n+        )\n+\n+    def __repr__(self):\n+        self._logger.exception(\"<%s (client=%s)>\", self.__class__.__name__, self.client)\n+        return f\"<{self.__class__.__name__} (client={self.client})>\""}, {"sha": "8103cfdc3ac5854c0cb5f4719013ccf1577c165a", "filename": "lib/apscheduler/jobstores/memory.py", "status": "modified", "additions": 4, "deletions": 6, "changes": 10, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fmemory.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fmemory.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fjobstores%2Fmemory.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,6 +1,4 @@\n-from __future__ import absolute_import\n-\n-from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError\n+from apscheduler.jobstores.base import BaseJobStore, ConflictingIdError, JobLookupError\n from apscheduler.util import datetime_to_utc_timestamp\n \n \n@@ -12,7 +10,7 @@ class MemoryJobStore(BaseJobStore):\n     \"\"\"\n \n     def __init__(self):\n-        super(MemoryJobStore, self).__init__()\n+        super().__init__()\n         # list of (job, timestamp), sorted by next_run_time and job id (ascending)\n         self._jobs = []\n         self._jobs_index = {}  # id -> (job, timestamp) lookup table\n@@ -89,11 +87,11 @@ def _get_job_index(self, timestamp, job_id):\n \n         \"\"\"\n         lo, hi = 0, len(self._jobs)\n-        timestamp = float('inf') if timestamp is None else timestamp\n+        timestamp = float(\"inf\") if timestamp is None else timestamp\n         while lo < hi:\n             mid = (lo + hi) // 2\n             mid_job, mid_timestamp = self._jobs[mid]\n-            mid_timestamp = float('inf') if mid_timestamp is None else mid_timestamp\n+            mid_timestamp = float(\"inf\") if mid_timestamp is None else mid_timestamp\n             if mid_timestamp > timestamp:\n                 hi = mid\n             elif mid_timestamp < timestamp:"}, {"sha": "102c0bd0807062177b0af26d8c3a93612f5644c1", "filename": "lib/apscheduler/jobstores/mongodb.py", "status": "modified", "additions": 60, "deletions": 43, "changes": 103, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fmongodb.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fmongodb.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fjobstores%2Fmongodb.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,21 +1,20 @@\n-from __future__ import absolute_import\n+import pickle\n import warnings\n \n-from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError\n-from apscheduler.util import maybe_ref, datetime_to_utc_timestamp, utc_timestamp_to_datetime\n from apscheduler.job import Job\n-\n-try:\n-    import cPickle as pickle\n-except ImportError:  # pragma: nocover\n-    import pickle\n+from apscheduler.jobstores.base import BaseJobStore, ConflictingIdError, JobLookupError\n+from apscheduler.util import (\n+    datetime_to_utc_timestamp,\n+    maybe_ref,\n+    utc_timestamp_to_datetime,\n+)\n \n try:\n     from bson.binary import Binary\n+    from pymongo import ASCENDING, MongoClient\n     from pymongo.errors import DuplicateKeyError\n-    from pymongo import MongoClient, ASCENDING\n-except ImportError:  # pragma: nocover\n-    raise ImportError('MongoDBJobStore requires PyMongo installed')\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"MongoDBJobStore requires PyMongo installed\") from exc\n \n \n class MongoDBJobStore(BaseJobStore):\n@@ -34,9 +33,15 @@ class MongoDBJobStore(BaseJobStore):\n         highest available\n     \"\"\"\n \n-    def __init__(self, database='apscheduler', collection='jobs', client=None,\n-                 pickle_protocol=pickle.HIGHEST_PROTOCOL, **connect_args):\n-        super(MongoDBJobStore, self).__init__()\n+    def __init__(\n+        self,\n+        database=\"apscheduler\",\n+        collection=\"jobs\",\n+        client=None,\n+        pickle_protocol=pickle.HIGHEST_PROTOCOL,\n+        **connect_args,\n+    ):\n+        super().__init__()\n         self.pickle_protocol = pickle_protocol\n \n         if not database:\n@@ -47,34 +52,40 @@ def __init__(self, database='apscheduler', collection='jobs', client=None,\n         if client:\n             self.client = maybe_ref(client)\n         else:\n-            connect_args.setdefault('w', 1)\n+            connect_args.setdefault(\"w\", 1)\n             self.client = MongoClient(**connect_args)\n \n         self.collection = self.client[database][collection]\n \n     def start(self, scheduler, alias):\n-        super(MongoDBJobStore, self).start(scheduler, alias)\n-        self.collection.create_index('next_run_time', sparse=True)\n+        super().start(scheduler, alias)\n+        self.collection.create_index(\"next_run_time\", sparse=True)\n \n     @property\n     def connection(self):\n-        warnings.warn('The \"connection\" member is deprecated -- use \"client\" instead',\n-                      DeprecationWarning)\n+        warnings.warn(\n+            'The \"connection\" member is deprecated -- use \"client\" instead',\n+            DeprecationWarning,\n+        )\n         return self.client\n \n     def lookup_job(self, job_id):\n-        document = self.collection.find_one(job_id, ['job_state'])\n-        return self._reconstitute_job(document['job_state']) if document else None\n+        document = self.collection.find_one(job_id, [\"job_state\"])\n+        return self._reconstitute_job(document[\"job_state\"]) if document else None\n \n     def get_due_jobs(self, now):\n         timestamp = datetime_to_utc_timestamp(now)\n-        return self._get_jobs({'next_run_time': {'$lte': timestamp}})\n+        return self._get_jobs({\"next_run_time\": {\"$lte\": timestamp}})\n \n     def get_next_run_time(self):\n-        document = self.collection.find_one({'next_run_time': {'$ne': None}},\n-                                            projection=['next_run_time'],\n-                                            sort=[('next_run_time', ASCENDING)])\n-        return utc_timestamp_to_datetime(document['next_run_time']) if document else None\n+        document = self.collection.find_one(\n+            {\"next_run_time\": {\"$ne\": None}},\n+            projection=[\"next_run_time\"],\n+            sort=[(\"next_run_time\", ASCENDING)],\n+        )\n+        return (\n+            utc_timestamp_to_datetime(document[\"next_run_time\"]) if document else None\n+        )\n \n     def get_all_jobs(self):\n         jobs = self._get_jobs({})\n@@ -83,25 +94,29 @@ def get_all_jobs(self):\n \n     def add_job(self, job):\n         try:\n-            self.collection.insert_one({\n-                '_id': job.id,\n-                'next_run_time': datetime_to_utc_timestamp(job.next_run_time),\n-                'job_state': Binary(pickle.dumps(job.__getstate__(), self.pickle_protocol))\n-            })\n+            self.collection.insert_one(\n+                {\n+                    \"_id\": job.id,\n+                    \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+                    \"job_state\": Binary(\n+                        pickle.dumps(job.__getstate__(), self.pickle_protocol)\n+                    ),\n+                }\n+            )\n         except DuplicateKeyError:\n             raise ConflictingIdError(job.id)\n \n     def update_job(self, job):\n         changes = {\n-            'next_run_time': datetime_to_utc_timestamp(job.next_run_time),\n-            'job_state': Binary(pickle.dumps(job.__getstate__(), self.pickle_protocol))\n+            \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+            \"job_state\": Binary(pickle.dumps(job.__getstate__(), self.pickle_protocol)),\n         }\n-        result = self.collection.update_one({'_id': job.id}, {'$set': changes})\n+        result = self.collection.update_one({\"_id\": job.id}, {\"$set\": changes})\n         if result and result.matched_count == 0:\n             raise JobLookupError(job.id)\n \n     def remove_job(self, job_id):\n-        result = self.collection.delete_one({'_id': job_id})\n+        result = self.collection.delete_one({\"_id\": job_id})\n         if result and result.deleted_count == 0:\n             raise JobLookupError(job_id)\n \n@@ -122,20 +137,22 @@ def _reconstitute_job(self, job_state):\n     def _get_jobs(self, conditions):\n         jobs = []\n         failed_job_ids = []\n-        for document in self.collection.find(conditions, ['_id', 'job_state'],\n-                                             sort=[('next_run_time', ASCENDING)]):\n+        for document in self.collection.find(\n+            conditions, [\"_id\", \"job_state\"], sort=[(\"next_run_time\", ASCENDING)]\n+        ):\n             try:\n-                jobs.append(self._reconstitute_job(document['job_state']))\n+                jobs.append(self._reconstitute_job(document[\"job_state\"]))\n             except BaseException:\n-                self._logger.exception('Unable to restore job \"%s\" -- removing it',\n-                                       document['_id'])\n-                failed_job_ids.append(document['_id'])\n+                self._logger.exception(\n+                    'Unable to restore job \"%s\" -- removing it', document[\"_id\"]\n+                )\n+                failed_job_ids.append(document[\"_id\"])\n \n         # Remove all the jobs we failed to restore\n         if failed_job_ids:\n-            self.collection.delete_many({'_id': {'$in': failed_job_ids}})\n+            self.collection.delete_many({\"_id\": {\"$in\": failed_job_ids}})\n \n         return jobs\n \n     def __repr__(self):\n-        return '<%s (client=%s)>' % (self.__class__.__name__, self.client)\n+        return f\"<{self.__class__.__name__} (client={self.client})>\""}, {"sha": "528285fb988ea92ef970b688bbd197c02bd82595", "filename": "lib/apscheduler/jobstores/redis.py", "status": "modified", "additions": 40, "deletions": 30, "changes": 70, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fredis.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fredis.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fjobstores%2Fredis.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,22 +1,14 @@\n-from __future__ import absolute_import\n-from datetime import datetime\n+import pickle\n+from datetime import datetime, timezone\n \n-from pytz import utc\n-import six\n-\n-from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError\n-from apscheduler.util import datetime_to_utc_timestamp, utc_timestamp_to_datetime\n from apscheduler.job import Job\n-\n-try:\n-    import cPickle as pickle\n-except ImportError:  # pragma: nocover\n-    import pickle\n+from apscheduler.jobstores.base import BaseJobStore, ConflictingIdError, JobLookupError\n+from apscheduler.util import datetime_to_utc_timestamp, utc_timestamp_to_datetime\n \n try:\n     from redis import Redis\n-except ImportError:  # pragma: nocover\n-    raise ImportError('RedisJobStore requires redis installed')\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"RedisJobStore requires redis installed\") from exc\n \n \n class RedisJobStore(BaseJobStore):\n@@ -33,9 +25,15 @@ class RedisJobStore(BaseJobStore):\n         highest available\n     \"\"\"\n \n-    def __init__(self, db=0, jobs_key='apscheduler.jobs', run_times_key='apscheduler.run_times',\n-                 pickle_protocol=pickle.HIGHEST_PROTOCOL, **connect_args):\n-        super(RedisJobStore, self).__init__()\n+    def __init__(\n+        self,\n+        db=0,\n+        jobs_key=\"apscheduler.jobs\",\n+        run_times_key=\"apscheduler.run_times\",\n+        pickle_protocol=pickle.HIGHEST_PROTOCOL,\n+        **connect_args,\n+    ):\n+        super().__init__()\n \n         if db is None:\n             raise ValueError('The \"db\" parameter must not be empty')\n@@ -58,7 +56,7 @@ def get_due_jobs(self, now):\n         job_ids = self.redis.zrangebyscore(self.run_times_key, 0, timestamp)\n         if job_ids:\n             job_states = self.redis.hmget(self.jobs_key, *job_ids)\n-            return self._reconstitute_jobs(six.moves.zip(job_ids, job_states))\n+            return self._reconstitute_jobs(zip(job_ids, job_states))\n         return []\n \n     def get_next_run_time(self):\n@@ -68,8 +66,8 @@ def get_next_run_time(self):\n \n     def get_all_jobs(self):\n         job_states = self.redis.hgetall(self.jobs_key)\n-        jobs = self._reconstitute_jobs(six.iteritems(job_states))\n-        paused_sort_key = datetime(9999, 12, 31, tzinfo=utc)\n+        jobs = self._reconstitute_jobs(job_states.items())\n+        paused_sort_key = datetime(9999, 12, 31, tzinfo=timezone.utc)\n         return sorted(jobs, key=lambda job: job.next_run_time or paused_sort_key)\n \n     def add_job(self, job):\n@@ -78,11 +76,16 @@ def add_job(self, job):\n \n         with self.redis.pipeline() as pipe:\n             pipe.multi()\n-            pipe.hset(self.jobs_key, job.id, pickle.dumps(job.__getstate__(),\n-                                                          self.pickle_protocol))\n+            pipe.hset(\n+                self.jobs_key,\n+                job.id,\n+                pickle.dumps(job.__getstate__(), self.pickle_protocol),\n+            )\n             if job.next_run_time:\n-                pipe.zadd(self.run_times_key,\n-                          {job.id: datetime_to_utc_timestamp(job.next_run_time)})\n+                pipe.zadd(\n+                    self.run_times_key,\n+                    {job.id: datetime_to_utc_timestamp(job.next_run_time)},\n+                )\n \n             pipe.execute()\n \n@@ -91,11 +94,16 @@ def update_job(self, job):\n             raise JobLookupError(job.id)\n \n         with self.redis.pipeline() as pipe:\n-            pipe.hset(self.jobs_key, job.id, pickle.dumps(job.__getstate__(),\n-                                                          self.pickle_protocol))\n+            pipe.hset(\n+                self.jobs_key,\n+                job.id,\n+                pickle.dumps(job.__getstate__(), self.pickle_protocol),\n+            )\n             if job.next_run_time:\n-                pipe.zadd(self.run_times_key,\n-                          {job.id: datetime_to_utc_timestamp(job.next_run_time)})\n+                pipe.zadd(\n+                    self.run_times_key,\n+                    {job.id: datetime_to_utc_timestamp(job.next_run_time)},\n+                )\n             else:\n                 pipe.zrem(self.run_times_key, job.id)\n \n@@ -134,7 +142,9 @@ def _reconstitute_jobs(self, job_states):\n             try:\n                 jobs.append(self._reconstitute_job(job_state))\n             except BaseException:\n-                self._logger.exception('Unable to restore job \"%s\" -- removing it', job_id)\n+                self._logger.exception(\n+                    'Unable to restore job \"%s\" -- removing it', job_id\n+                )\n                 failed_job_ids.append(job_id)\n \n         # Remove all the jobs we failed to restore\n@@ -147,4 +157,4 @@ def _reconstitute_jobs(self, job_states):\n         return jobs\n \n     def __repr__(self):\n-        return '<%s>' % self.__class__.__name__\n+        return f\"<{self.__class__.__name__}>\""}, {"sha": "d78290b1fd865d19c5407a25d8f6afc02e9f4cf9", "filename": "lib/apscheduler/jobstores/rethinkdb.py", "status": "modified", "additions": 57, "deletions": 39, "changes": 96, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Frethinkdb.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Frethinkdb.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fjobstores%2Frethinkdb.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,18 +1,17 @@\n-from __future__ import absolute_import\n+import pickle\n \n-from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError\n-from apscheduler.util import maybe_ref, datetime_to_utc_timestamp, utc_timestamp_to_datetime\n from apscheduler.job import Job\n-\n-try:\n-    import cPickle as pickle\n-except ImportError:  # pragma: nocover\n-    import pickle\n+from apscheduler.jobstores.base import BaseJobStore, ConflictingIdError, JobLookupError\n+from apscheduler.util import (\n+    datetime_to_utc_timestamp,\n+    maybe_ref,\n+    utc_timestamp_to_datetime,\n+)\n \n try:\n     from rethinkdb import RethinkDB\n-except ImportError:  # pragma: nocover\n-    raise ImportError('RethinkDBJobStore requires rethinkdb installed')\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"RethinkDBJobStore requires rethinkdb installed\") from exc\n \n \n class RethinkDBJobStore(BaseJobStore):\n@@ -30,9 +29,15 @@ class RethinkDBJobStore(BaseJobStore):\n         highest available\n     \"\"\"\n \n-    def __init__(self, database='apscheduler', table='jobs', client=None,\n-                 pickle_protocol=pickle.HIGHEST_PROTOCOL, **connect_args):\n-        super(RethinkDBJobStore, self).__init__()\n+    def __init__(\n+        self,\n+        database=\"apscheduler\",\n+        table=\"jobs\",\n+        client=None,\n+        pickle_protocol=pickle.HIGHEST_PROTOCOL,\n+        **connect_args,\n+    ):\n+        super().__init__()\n \n         if not database:\n             raise ValueError('The \"database\" parameter must not be empty')\n@@ -49,7 +54,7 @@ def __init__(self, database='apscheduler', table='jobs', client=None,\n         self.conn = None\n \n     def start(self, scheduler, alias):\n-        super(RethinkDBJobStore, self).start(scheduler, alias)\n+        super().start(scheduler, alias)\n \n         if self.client:\n             self.conn = maybe_ref(self.client)\n@@ -62,24 +67,27 @@ def start(self, scheduler, alias):\n         if self.table_name not in self.r.table_list().run(self.conn):\n             self.r.table_create(self.table_name).run(self.conn)\n \n-        if 'next_run_time' not in self.r.table(self.table_name).index_list().run(self.conn):\n-            self.r.table(self.table_name).index_create('next_run_time').run(self.conn)\n+        if \"next_run_time\" not in self.r.table(self.table_name).index_list().run(\n+            self.conn\n+        ):\n+            self.r.table(self.table_name).index_create(\"next_run_time\").run(self.conn)\n \n         self.table = self.r.db(self.database).table(self.table_name)\n \n     def lookup_job(self, job_id):\n-        results = list(self.table.get_all(job_id).pluck('job_state').run(self.conn))\n-        return self._reconstitute_job(results[0]['job_state']) if results else None\n+        results = list(self.table.get_all(job_id).pluck(\"job_state\").run(self.conn))\n+        return self._reconstitute_job(results[0][\"job_state\"]) if results else None\n \n     def get_due_jobs(self, now):\n-        return self._get_jobs(self.r.row['next_run_time'] <= datetime_to_utc_timestamp(now))\n+        return self._get_jobs(\n+            self.r.row[\"next_run_time\"] <= datetime_to_utc_timestamp(now)\n+        )\n \n     def get_next_run_time(self):\n         results = list(\n-            self.table\n-            .filter(self.r.row['next_run_time'] != None)  # noqa\n-            .order_by(self.r.asc('next_run_time'))\n-            .map(lambda x: x['next_run_time'])\n+            self.table.filter(self.r.row[\"next_run_time\"] != None)\n+            .order_by(self.r.asc(\"next_run_time\"))\n+            .map(lambda x: x[\"next_run_time\"])\n             .limit(1)\n             .run(self.conn)\n         )\n@@ -92,27 +100,31 @@ def get_all_jobs(self):\n \n     def add_job(self, job):\n         job_dict = {\n-            'id': job.id,\n-            'next_run_time': datetime_to_utc_timestamp(job.next_run_time),\n-            'job_state': self.r.binary(pickle.dumps(job.__getstate__(), self.pickle_protocol))\n+            \"id\": job.id,\n+            \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+            \"job_state\": self.r.binary(\n+                pickle.dumps(job.__getstate__(), self.pickle_protocol)\n+            ),\n         }\n         results = self.table.insert(job_dict).run(self.conn)\n-        if results['errors'] > 0:\n+        if results[\"errors\"] > 0:\n             raise ConflictingIdError(job.id)\n \n     def update_job(self, job):\n         changes = {\n-            'next_run_time': datetime_to_utc_timestamp(job.next_run_time),\n-            'job_state': self.r.binary(pickle.dumps(job.__getstate__(), self.pickle_protocol))\n+            \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+            \"job_state\": self.r.binary(\n+                pickle.dumps(job.__getstate__(), self.pickle_protocol)\n+            ),\n         }\n         results = self.table.get_all(job.id).update(changes).run(self.conn)\n         skipped = False in map(lambda x: results[x] == 0, results.keys())\n-        if results['skipped'] > 0 or results['errors'] > 0 or not skipped:\n+        if results[\"skipped\"] > 0 or results[\"errors\"] > 0 or not skipped:\n             raise JobLookupError(job.id)\n \n     def remove_job(self, job_id):\n         results = self.table.get_all(job_id).delete().run(self.conn)\n-        if results['deleted'] + results['skipped'] != 1:\n+        if results[\"deleted\"] + results[\"skipped\"] != 1:\n             raise JobLookupError(job_id)\n \n     def remove_all_jobs(self):\n@@ -132,24 +144,30 @@ def _reconstitute_job(self, job_state):\n     def _get_jobs(self, predicate=None):\n         jobs = []\n         failed_job_ids = []\n-        query = (self.table.filter(self.r.row['next_run_time'] != None).filter(predicate)  # noqa\n-                 if predicate else self.table)\n-        query = query.order_by('next_run_time', 'id').pluck('id', 'job_state')\n+        query = (\n+            self.table.filter(self.r.row[\"next_run_time\"] != None).filter(predicate)\n+            if predicate\n+            else self.table\n+        )\n+        query = query.order_by(\"next_run_time\", \"id\").pluck(\"id\", \"job_state\")\n \n         for document in query.run(self.conn):\n             try:\n-                jobs.append(self._reconstitute_job(document['job_state']))\n+                jobs.append(self._reconstitute_job(document[\"job_state\"]))\n             except Exception:\n-                self._logger.exception('Unable to restore job \"%s\" -- removing it', document['id'])\n-                failed_job_ids.append(document['id'])\n+                self._logger.exception(\n+                    'Unable to restore job \"%s\" -- removing it', document[\"id\"]\n+                )\n+                failed_job_ids.append(document[\"id\"])\n \n         # Remove all the jobs we failed to restore\n         if failed_job_ids:\n             self.r.expr(failed_job_ids).for_each(\n-                lambda job_id: self.table.get_all(job_id).delete()).run(self.conn)\n+                lambda job_id: self.table.get_all(job_id).delete()\n+            ).run(self.conn)\n \n         return jobs\n \n     def __repr__(self):\n         connection = self.conn\n-        return '<%s (connection=%s)>' % (self.__class__.__name__, connection)\n+        return f\"<{self.__class__.__name__} (connection={connection})>\""}, {"sha": "9866acf5693ab00cb07612f54f8089e71befba7c", "filename": "lib/apscheduler/jobstores/sqlalchemy.py", "status": "modified", "additions": 71, "deletions": 38, "changes": 109, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fsqlalchemy.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fsqlalchemy.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fjobstores%2Fsqlalchemy.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,21 +1,29 @@\n-from __future__ import absolute_import\n+import pickle\n \n-from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError\n-from apscheduler.util import maybe_ref, datetime_to_utc_timestamp, utc_timestamp_to_datetime\n from apscheduler.job import Job\n-\n-try:\n-    import cPickle as pickle\n-except ImportError:  # pragma: nocover\n-    import pickle\n+from apscheduler.jobstores.base import BaseJobStore, ConflictingIdError, JobLookupError\n+from apscheduler.util import (\n+    datetime_to_utc_timestamp,\n+    maybe_ref,\n+    utc_timestamp_to_datetime,\n+)\n \n try:\n     from sqlalchemy import (\n-        create_engine, Table, Column, MetaData, Unicode, Float, LargeBinary, select, and_)\n+        Column,\n+        Float,\n+        LargeBinary,\n+        MetaData,\n+        Table,\n+        Unicode,\n+        and_,\n+        create_engine,\n+        select,\n+    )\n     from sqlalchemy.exc import IntegrityError\n     from sqlalchemy.sql.expression import null\n-except ImportError:  # pragma: nocover\n-    raise ImportError('SQLAlchemyJobStore requires SQLAlchemy installed')\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"SQLAlchemyJobStore requires SQLAlchemy installed\") from exc\n \n \n class SQLAlchemyJobStore(BaseJobStore):\n@@ -40,9 +48,17 @@ class SQLAlchemyJobStore(BaseJobStore):\n         (ignored if ``engine`` is given)\n     \"\"\"\n \n-    def __init__(self, url=None, engine=None, tablename='apscheduler_jobs', metadata=None,\n-                 pickle_protocol=pickle.HIGHEST_PROTOCOL, tableschema=None, engine_options=None):\n-        super(SQLAlchemyJobStore, self).__init__()\n+    def __init__(\n+        self,\n+        url=None,\n+        engine=None,\n+        tablename=\"apscheduler_jobs\",\n+        metadata=None,\n+        pickle_protocol=pickle.HIGHEST_PROTOCOL,\n+        tableschema=None,\n+        engine_options=None,\n+    ):\n+        super().__init__()\n         self.pickle_protocol = pickle_protocol\n         metadata = maybe_ref(metadata) or MetaData()\n \n@@ -56,15 +72,16 @@ def __init__(self, url=None, engine=None, tablename='apscheduler_jobs', metadata\n         # 191 = max key length in MySQL for InnoDB/utf8mb4 tables,\n         # 25 = precision that translates to an 8-byte float\n         self.jobs_t = Table(\n-            tablename, metadata,\n-            Column('id', Unicode(191), primary_key=True),\n-            Column('next_run_time', Float(25), index=True),\n-            Column('job_state', LargeBinary, nullable=False),\n-            schema=tableschema\n+            tablename,\n+            metadata,\n+            Column(\"id\", Unicode(191), primary_key=True),\n+            Column(\"next_run_time\", Float(25), index=True),\n+            Column(\"job_state\", LargeBinary, nullable=False),\n+            schema=tableschema,\n         )\n \n     def start(self, scheduler, alias):\n-        super(SQLAlchemyJobStore, self).start(scheduler, alias)\n+        super().start(scheduler, alias)\n         self.jobs_t.create(self.engine, True)\n \n     def lookup_job(self, job_id):\n@@ -78,9 +95,12 @@ def get_due_jobs(self, now):\n         return self._get_jobs(self.jobs_t.c.next_run_time <= timestamp)\n \n     def get_next_run_time(self):\n-        selectable = select(self.jobs_t.c.next_run_time).\\\n-            where(self.jobs_t.c.next_run_time != null()).\\\n-            order_by(self.jobs_t.c.next_run_time).limit(1)\n+        selectable = (\n+            select(self.jobs_t.c.next_run_time)\n+            .where(self.jobs_t.c.next_run_time != null())\n+            .order_by(self.jobs_t.c.next_run_time)\n+            .limit(1)\n+        )\n         with self.engine.begin() as connection:\n             next_run_time = connection.execute(selectable).scalar()\n             return utc_timestamp_to_datetime(next_run_time)\n@@ -91,22 +111,30 @@ def get_all_jobs(self):\n         return jobs\n \n     def add_job(self, job):\n-        insert = self.jobs_t.insert().values(**{\n-            'id': job.id,\n-            'next_run_time': datetime_to_utc_timestamp(job.next_run_time),\n-            'job_state': pickle.dumps(job.__getstate__(), self.pickle_protocol)\n-        })\n+        insert = self.jobs_t.insert().values(\n+            **{\n+                \"id\": job.id,\n+                \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+                \"job_state\": pickle.dumps(job.__getstate__(), self.pickle_protocol),\n+            }\n+        )\n         with self.engine.begin() as connection:\n             try:\n                 connection.execute(insert)\n             except IntegrityError:\n                 raise ConflictingIdError(job.id)\n \n     def update_job(self, job):\n-        update = self.jobs_t.update().values(**{\n-            'next_run_time': datetime_to_utc_timestamp(job.next_run_time),\n-            'job_state': pickle.dumps(job.__getstate__(), self.pickle_protocol)\n-        }).where(self.jobs_t.c.id == job.id)\n+        update = (\n+            self.jobs_t.update()\n+            .values(\n+                **{\n+                    \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+                    \"job_state\": pickle.dumps(job.__getstate__(), self.pickle_protocol),\n+                }\n+            )\n+            .where(self.jobs_t.c.id == job.id)\n+        )\n         with self.engine.begin() as connection:\n             result = connection.execute(update)\n             if result.rowcount == 0:\n@@ -129,7 +157,7 @@ def shutdown(self):\n \n     def _reconstitute_job(self, job_state):\n         job_state = pickle.loads(job_state)\n-        job_state['jobstore'] = self\n+        job_state[\"jobstore\"] = self\n         job = Job.__new__(Job)\n         job.__setstate__(job_state)\n         job._scheduler = self._scheduler\n@@ -138,24 +166,29 @@ def _reconstitute_job(self, job_state):\n \n     def _get_jobs(self, *conditions):\n         jobs = []\n-        selectable = select(self.jobs_t.c.id, self.jobs_t.c.job_state).\\\n-            order_by(self.jobs_t.c.next_run_time)\n+        selectable = select(self.jobs_t.c.id, self.jobs_t.c.job_state).order_by(\n+            self.jobs_t.c.next_run_time\n+        )\n         selectable = selectable.where(and_(*conditions)) if conditions else selectable\n         failed_job_ids = set()\n         with self.engine.begin() as connection:\n             for row in connection.execute(selectable):\n                 try:\n                     jobs.append(self._reconstitute_job(row.job_state))\n                 except BaseException:\n-                    self._logger.exception('Unable to restore job \"%s\" -- removing it', row.id)\n+                    self._logger.exception(\n+                        'Unable to restore job \"%s\" -- removing it', row.id\n+                    )\n                     failed_job_ids.add(row.id)\n \n             # Remove all the jobs we failed to restore\n             if failed_job_ids:\n-                delete = self.jobs_t.delete().where(self.jobs_t.c.id.in_(failed_job_ids))\n+                delete = self.jobs_t.delete().where(\n+                    self.jobs_t.c.id.in_(failed_job_ids)\n+                )\n                 connection.execute(delete)\n \n         return jobs\n \n     def __repr__(self):\n-        return '<%s (url=%s)>' % (self.__class__.__name__, self.engine.url)\n+        return f\"<{self.__class__.__name__} (url={self.engine.url})>\""}, {"sha": "687fbc2a5ebadcf5cfd90c58b4223beeb5d30bd2", "filename": "lib/apscheduler/jobstores/zookeeper.py", "status": "modified", "additions": 58, "deletions": 39, "changes": 97, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fzookeeper.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fjobstores%2Fzookeeper.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fjobstores%2Fzookeeper.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,23 +1,20 @@\n-from __future__ import absolute_import\n+import pickle\n+from datetime import datetime, timezone\n \n-from datetime import datetime\n+from kazoo.exceptions import NodeExistsError, NoNodeError\n \n-from pytz import utc\n-from kazoo.exceptions import NoNodeError, NodeExistsError\n-\n-from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError\n-from apscheduler.util import maybe_ref, datetime_to_utc_timestamp, utc_timestamp_to_datetime\n from apscheduler.job import Job\n-\n-try:\n-    import cPickle as pickle\n-except ImportError:  # pragma: nocover\n-    import pickle\n+from apscheduler.jobstores.base import BaseJobStore, ConflictingIdError, JobLookupError\n+from apscheduler.util import (\n+    datetime_to_utc_timestamp,\n+    maybe_ref,\n+    utc_timestamp_to_datetime,\n+)\n \n try:\n     from kazoo.client import KazooClient\n-except ImportError:  # pragma: nocover\n-    raise ImportError('ZooKeeperJobStore requires Kazoo installed')\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"ZooKeeperJobStore requires Kazoo installed\") from exc\n \n \n class ZooKeeperJobStore(BaseJobStore):\n@@ -35,9 +32,15 @@ class ZooKeeperJobStore(BaseJobStore):\n         highest available\n     \"\"\"\n \n-    def __init__(self, path='/apscheduler', client=None, close_connection_on_exit=False,\n-                 pickle_protocol=pickle.HIGHEST_PROTOCOL, **connect_args):\n-        super(ZooKeeperJobStore, self).__init__()\n+    def __init__(\n+        self,\n+        path=\"/apscheduler\",\n+        client=None,\n+        close_connection_on_exit=False,\n+        pickle_protocol=pickle.HIGHEST_PROTOCOL,\n+        **connect_args,\n+    ):\n+        super().__init__()\n         self.pickle_protocol = pickle_protocol\n         self.close_connection_on_exit = close_connection_on_exit\n \n@@ -58,7 +61,7 @@ def _ensure_paths(self):\n         self._ensured_path = True\n \n     def start(self, scheduler, alias):\n-        super(ZooKeeperJobStore, self).start(scheduler, alias)\n+        super().start(scheduler, alias)\n         if not self.client.connected:\n             self.client.start()\n \n@@ -68,33 +71,40 @@ def lookup_job(self, job_id):\n         try:\n             content, _ = self.client.get(node_path)\n             doc = pickle.loads(content)\n-            job = self._reconstitute_job(doc['job_state'])\n+            job = self._reconstitute_job(doc[\"job_state\"])\n             return job\n         except BaseException:\n             return None\n \n     def get_due_jobs(self, now):\n         timestamp = datetime_to_utc_timestamp(now)\n-        jobs = [job_def['job'] for job_def in self._get_jobs()\n-                if job_def['next_run_time'] is not None and job_def['next_run_time'] <= timestamp]\n+        jobs = [\n+            job_def[\"job\"]\n+            for job_def in self._get_jobs()\n+            if job_def[\"next_run_time\"] is not None\n+            and job_def[\"next_run_time\"] <= timestamp\n+        ]\n         return jobs\n \n     def get_next_run_time(self):\n-        next_runs = [job_def['next_run_time'] for job_def in self._get_jobs()\n-                     if job_def['next_run_time'] is not None]\n+        next_runs = [\n+            job_def[\"next_run_time\"]\n+            for job_def in self._get_jobs()\n+            if job_def[\"next_run_time\"] is not None\n+        ]\n         return utc_timestamp_to_datetime(min(next_runs)) if len(next_runs) > 0 else None\n \n     def get_all_jobs(self):\n-        jobs = [job_def['job'] for job_def in self._get_jobs()]\n+        jobs = [job_def[\"job\"] for job_def in self._get_jobs()]\n         self._fix_paused_jobs_sorting(jobs)\n         return jobs\n \n     def add_job(self, job):\n         self._ensure_paths()\n         node_path = self.path + \"/\" + str(job.id)\n         value = {\n-            'next_run_time': datetime_to_utc_timestamp(job.next_run_time),\n-            'job_state': job.__getstate__()\n+            \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+            \"job_state\": job.__getstate__(),\n         }\n         data = pickle.dumps(value, self.pickle_protocol)\n         try:\n@@ -106,8 +116,8 @@ def update_job(self, job):\n         self._ensure_paths()\n         node_path = self.path + \"/\" + str(job.id)\n         changes = {\n-            'next_run_time': datetime_to_utc_timestamp(job.next_run_time),\n-            'job_state': job.__getstate__()\n+            \"next_run_time\": datetime_to_utc_timestamp(job.next_run_time),\n+            \"job_state\": job.__getstate__(),\n         }\n         data = pickle.dumps(changes, self.pickle_protocol)\n         try:\n@@ -154,25 +164,34 @@ def _get_jobs(self):\n                 content, _ = self.client.get(node_path)\n                 doc = pickle.loads(content)\n                 job_def = {\n-                    'job_id': node_name,\n-                    'next_run_time': doc['next_run_time'] if doc['next_run_time'] else None,\n-                    'job_state': doc['job_state'],\n-                    'job': self._reconstitute_job(doc['job_state']),\n-                    'creation_time': _.ctime\n+                    \"job_id\": node_name,\n+                    \"next_run_time\": doc[\"next_run_time\"]\n+                    if doc[\"next_run_time\"]\n+                    else None,\n+                    \"job_state\": doc[\"job_state\"],\n+                    \"job\": self._reconstitute_job(doc[\"job_state\"]),\n+                    \"creation_time\": _.ctime,\n                 }\n                 jobs.append(job_def)\n             except BaseException:\n-                self._logger.exception('Unable to restore job \"%s\" -- removing it' % node_name)\n+                self._logger.exception(\n+                    'Unable to restore job \"%s\" -- removing it', node_name\n+                )\n                 failed_job_ids.append(node_name)\n \n         # Remove all the jobs we failed to restore\n         if failed_job_ids:\n             for failed_id in failed_job_ids:\n                 self.remove_job(failed_id)\n-        paused_sort_key = datetime(9999, 12, 31, tzinfo=utc)\n-        return sorted(jobs, key=lambda job_def: (job_def['job'].next_run_time or paused_sort_key,\n-                                                 job_def['creation_time']))\n+        paused_sort_key = datetime(9999, 12, 31, tzinfo=timezone.utc)\n+        return sorted(\n+            jobs,\n+            key=lambda job_def: (\n+                job_def[\"job\"].next_run_time or paused_sort_key,\n+                job_def[\"creation_time\"],\n+            ),\n+        )\n \n     def __repr__(self):\n-        self._logger.exception('<%s (client=%s)>' % (self.__class__.__name__, self.client))\n-        return '<%s (client=%s)>' % (self.__class__.__name__, self.client)\n+        self._logger.exception(\"<%s (client=%s)>\", self.__class__.__name__, self.client)\n+        return f\"<{self.__class__.__name__} (client={self.client})>\""}, {"sha": "c17cc298906260022179f36dfeb554d7ec7531ac", "filename": "lib/apscheduler/schedulers/__init__.py", "status": "modified", "additions": 2, "deletions": 2, "changes": 4, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fschedulers%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -2,11 +2,11 @@ class SchedulerAlreadyRunningError(Exception):\n     \"\"\"Raised when attempting to start or configure the scheduler when it's already running.\"\"\"\n \n     def __str__(self):\n-        return 'Scheduler is already running'\n+        return \"Scheduler is already running\"\n \n \n class SchedulerNotRunningError(Exception):\n     \"\"\"Raised when attempting to shutdown the scheduler when it's not running.\"\"\"\n \n     def __str__(self):\n-        return 'Scheduler is not running'\n+        return \"Scheduler is not running\""}, {"sha": "7fb3fa60b0a4ffab31cbcb9ebb1ab3bd9a6c1703", "filename": "lib/apscheduler/schedulers/asyncio.py", "status": "modified", "additions": 18, "deletions": 9, "changes": 27, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fasyncio.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fasyncio.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fschedulers%2Fasyncio.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,7 +1,7 @@\n-from __future__ import absolute_import\n import asyncio\n-from functools import wraps, partial\n+from functools import partial, wraps\n \n+from apscheduler.schedulers import SchedulerNotRunningError\n from apscheduler.schedulers.base import BaseScheduler\n from apscheduler.util import maybe_ref\n \n@@ -11,6 +11,7 @@ def run_in_event_loop(func):\n     def wrapper(self, *args, **kwargs):\n         wrapped = partial(func, self, *args, **kwargs)\n         self._eventloop.call_soon_threadsafe(wrapped)\n+\n     return wrapper\n \n \n@@ -31,19 +32,26 @@ class AsyncIOScheduler(BaseScheduler):\n     _timeout = None\n \n     def start(self, paused=False):\n-        if not self._eventloop:\n-            self._eventloop = asyncio.get_event_loop()\n+        if not self._eventloop or self._eventloop.is_closed():\n+            self._eventloop = asyncio.get_running_loop()\n \n-        super(AsyncIOScheduler, self).start(paused)\n+        super().start(paused)\n \n     @run_in_event_loop\n-    def shutdown(self, wait=True):\n-        super(AsyncIOScheduler, self).shutdown(wait)\n+    def _shutdown(self, wait=True):\n+        super().shutdown(wait)\n         self._stop_timer()\n+        self._eventloop = None\n+\n+    def shutdown(self, wait=True):\n+        if not self.running:\n+            raise SchedulerNotRunningError\n+\n+        self._shutdown(wait)\n \n     def _configure(self, config):\n-        self._eventloop = maybe_ref(config.pop('event_loop', None))\n-        super(AsyncIOScheduler, self)._configure(config)\n+        self._eventloop = maybe_ref(config.pop(\"event_loop\", None))\n+        super()._configure(config)\n \n     def _start_timer(self, wait_seconds):\n         self._stop_timer()\n@@ -63,4 +71,5 @@ def wakeup(self):\n \n     def _create_default_executor(self):\n         from apscheduler.executors.asyncio import AsyncIOExecutor\n+\n         return AsyncIOExecutor()"}, {"sha": "7d8d1bc70aa57c4259323c087ee5959c4969b4ed", "filename": "lib/apscheduler/schedulers/background.py", "status": "modified", "additions": 7, "deletions": 8, "changes": 15, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fbackground.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fbackground.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fschedulers%2Fbackground.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,6 +1,4 @@\n-from __future__ import absolute_import\n-\n-from threading import Thread, Event\n+from threading import Event, Thread\n \n from apscheduler.schedulers.base import BaseScheduler\n from apscheduler.schedulers.blocking import BlockingScheduler\n@@ -25,19 +23,20 @@ class BackgroundScheduler(BlockingScheduler):\n     _thread = None\n \n     def _configure(self, config):\n-        self._daemon = asbool(config.pop('daemon', True))\n-        super(BackgroundScheduler, self)._configure(config)\n+        self._daemon = asbool(config.pop(\"daemon\", True))\n+        super()._configure(config)\n \n     def start(self, *args, **kwargs):\n         if self._event is None or self._event.is_set():\n             self._event = Event()\n \n         BaseScheduler.start(self, *args, **kwargs)\n-        self._thread = Thread(target=self._main_loop, name='APScheduler')\n-        self._thread.daemon = self._daemon\n+        self._thread = Thread(\n+            target=self._main_loop, name=\"APScheduler\", daemon=self._daemon\n+        )\n         self._thread.start()\n \n     def shutdown(self, *args, **kwargs):\n-        super(BackgroundScheduler, self).shutdown(*args, **kwargs)\n+        super().shutdown(*args, **kwargs)\n         self._thread.join()\n         del self._thread"}, {"sha": "7d713c7562afe8ef814ac15e718fdada3ae539f9", "filename": "lib/apscheduler/schedulers/base.py", "status": "modified", "additions": 412, "deletions": 174, "changes": 586, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fbase.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fbase.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fschedulers%2Fbase.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,35 +1,54 @@\n-from __future__ import print_function\n-\n+import sys\n+import warnings\n from abc import ABCMeta, abstractmethod\n-from threading import RLock\n+from collections.abc import Mapping, MutableMapping\n+from contextlib import ExitStack\n from datetime import datetime, timedelta\n+from importlib.metadata import entry_points\n from logging import getLogger\n-import warnings\n-import sys\n+from threading import TIMEOUT_MAX, RLock\n \n-from pkg_resources import iter_entry_points\n from tzlocal import get_localzone\n-import six\n \n-from apscheduler.schedulers import SchedulerAlreadyRunningError, SchedulerNotRunningError\n-from apscheduler.executors.base import MaxInstancesReachedError, BaseExecutor\n+from apscheduler.events import (\n+    EVENT_ALL,\n+    EVENT_ALL_JOBS_REMOVED,\n+    EVENT_EXECUTOR_ADDED,\n+    EVENT_EXECUTOR_REMOVED,\n+    EVENT_JOB_ADDED,\n+    EVENT_JOB_MAX_INSTANCES,\n+    EVENT_JOB_MODIFIED,\n+    EVENT_JOB_REMOVED,\n+    EVENT_JOB_SUBMITTED,\n+    EVENT_JOBSTORE_ADDED,\n+    EVENT_JOBSTORE_REMOVED,\n+    EVENT_SCHEDULER_PAUSED,\n+    EVENT_SCHEDULER_RESUMED,\n+    EVENT_SCHEDULER_SHUTDOWN,\n+    EVENT_SCHEDULER_STARTED,\n+    JobEvent,\n+    JobSubmissionEvent,\n+    SchedulerEvent,\n+)\n+from apscheduler.executors.base import BaseExecutor, MaxInstancesReachedError\n from apscheduler.executors.pool import ThreadPoolExecutor\n-from apscheduler.jobstores.base import ConflictingIdError, JobLookupError, BaseJobStore\n-from apscheduler.jobstores.memory import MemoryJobStore\n from apscheduler.job import Job\n+from apscheduler.jobstores.base import BaseJobStore, ConflictingIdError, JobLookupError\n+from apscheduler.jobstores.memory import MemoryJobStore\n+from apscheduler.schedulers import (\n+    SchedulerAlreadyRunningError,\n+    SchedulerNotRunningError,\n+)\n from apscheduler.triggers.base import BaseTrigger\n from apscheduler.util import (\n-    asbool, asint, astimezone, maybe_ref, timedelta_seconds, undefined, TIMEOUT_MAX)\n-from apscheduler.events import (\n-    SchedulerEvent, JobEvent, JobSubmissionEvent, EVENT_SCHEDULER_START, EVENT_SCHEDULER_SHUTDOWN,\n-    EVENT_JOBSTORE_ADDED, EVENT_JOBSTORE_REMOVED, EVENT_ALL, EVENT_JOB_MODIFIED, EVENT_JOB_REMOVED,\n-    EVENT_JOB_ADDED, EVENT_EXECUTOR_ADDED, EVENT_EXECUTOR_REMOVED, EVENT_ALL_JOBS_REMOVED,\n-    EVENT_JOB_SUBMITTED, EVENT_JOB_MAX_INSTANCES, EVENT_SCHEDULER_RESUMED, EVENT_SCHEDULER_PAUSED)\n-\n-try:\n-    from collections.abc import MutableMapping\n-except ImportError:\n-    from collections import MutableMapping\n+    asbool,\n+    asint,\n+    astimezone,\n+    maybe_ref,\n+    obj_to_ref,\n+    ref_to_obj,\n+    undefined,\n+)\n \n #: constant indicating a scheduler's stopped state\n STATE_STOPPED = 0\n@@ -39,7 +58,7 @@\n STATE_PAUSED = 2\n \n \n-class BaseScheduler(six.with_metaclass(ABCMeta)):\n+class BaseScheduler(metaclass=ABCMeta):\n     \"\"\"\n     Abstract base class for all schedulers.\n \n@@ -63,19 +82,38 @@ class BaseScheduler(six.with_metaclass(ABCMeta)):\n     .. seealso:: :ref:`scheduler-config`\n     \"\"\"\n \n-    _trigger_plugins = dict((ep.name, ep) for ep in iter_entry_points('apscheduler.triggers'))\n+    # The `group=...` API is only available in the backport, used in <=3.7, and in std>=3.10.\n+    if (3, 8) <= sys.version_info < (3, 10):\n+        _trigger_plugins = {\n+            ep.name: ep for ep in entry_points()[\"apscheduler.triggers\"]\n+        }\n+        _executor_plugins = {\n+            ep.name: ep for ep in entry_points()[\"apscheduler.executors\"]\n+        }\n+        _jobstore_plugins = {\n+            ep.name: ep for ep in entry_points()[\"apscheduler.jobstores\"]\n+        }\n+    else:\n+        _trigger_plugins = {\n+            ep.name: ep for ep in entry_points(group=\"apscheduler.triggers\")\n+        }\n+        _executor_plugins = {\n+            ep.name: ep for ep in entry_points(group=\"apscheduler.executors\")\n+        }\n+        _jobstore_plugins = {\n+            ep.name: ep for ep in entry_points(group=\"apscheduler.jobstores\")\n+        }\n+\n     _trigger_classes = {}\n-    _executor_plugins = dict((ep.name, ep) for ep in iter_entry_points('apscheduler.executors'))\n     _executor_classes = {}\n-    _jobstore_plugins = dict((ep.name, ep) for ep in iter_entry_points('apscheduler.jobstores'))\n     _jobstore_classes = {}\n \n     #\n     # Public API\n     #\n \n     def __init__(self, gconfig={}, **options):\n-        super(BaseScheduler, self).__init__()\n+        super().__init__()\n         self._executors = {}\n         self._executors_lock = self._create_lock()\n         self._jobstores = {}\n@@ -87,11 +125,13 @@ def __init__(self, gconfig={}, **options):\n         self.configure(gconfig, **options)\n \n     def __getstate__(self):\n-        raise TypeError(\"Schedulers cannot be serialized. Ensure that you are not passing a \"\n-                        \"scheduler instance as an argument to a job, or scheduling an instance \"\n-                        \"method where the instance contains a scheduler as an attribute.\")\n+        raise TypeError(\n+            \"Schedulers cannot be serialized. Ensure that you are not passing a \"\n+            \"scheduler instance as an argument to a job, or scheduling an instance \"\n+            \"method where the instance contains a scheduler as an attribute.\"\n+        )\n \n-    def configure(self, gconfig={}, prefix='apscheduler.', **options):\n+    def configure(self, gconfig={}, prefix=\"apscheduler.\", **options):\n         \"\"\"\n         Reconfigures the scheduler with the given options.\n \n@@ -111,14 +151,17 @@ def configure(self, gconfig={}, prefix='apscheduler.', **options):\n         # global configuration dict\n         if prefix:\n             prefixlen = len(prefix)\n-            gconfig = dict((key[prefixlen:], value) for key, value in six.iteritems(gconfig)\n-                           if key.startswith(prefix))\n+            gconfig = dict(\n+                (key[prefixlen:], value)\n+                for key, value in gconfig.items()\n+                if key.startswith(prefix)\n+            )\n \n         # Create a structure from the dotted options\n         # (e.g. \"a.b.c = d\" -> {'a': {'b': {'c': 'd'}}})\n         config = {}\n-        for key, value in six.iteritems(gconfig):\n-            parts = key.split('.')\n+        for key, value in gconfig.items():\n+            parts = key.split(\".\")\n             parent = config\n             key = parts.pop(0)\n             while parts:\n@@ -146,20 +189,20 @@ def start(self, paused=False):\n \n         with self._executors_lock:\n             # Create a default executor if nothing else is configured\n-            if 'default' not in self._executors:\n-                self.add_executor(self._create_default_executor(), 'default')\n+            if \"default\" not in self._executors:\n+                self.add_executor(self._create_default_executor(), \"default\")\n \n             # Start all the executors\n-            for alias, executor in six.iteritems(self._executors):\n+            for alias, executor in self._executors.items():\n                 executor.start(self, alias)\n \n         with self._jobstores_lock:\n             # Create a default job store if nothing else is configured\n-            if 'default' not in self._jobstores:\n-                self.add_jobstore(self._create_default_jobstore(), 'default')\n+            if \"default\" not in self._jobstores:\n+                self.add_jobstore(self._create_default_jobstore(), \"default\")\n \n             # Start all the job stores\n-            for alias, store in six.iteritems(self._jobstores):\n+            for alias, store in self._jobstores.items():\n                 store.start(self, alias)\n \n             # Schedule all pending jobs\n@@ -168,8 +211,8 @@ def start(self, paused=False):\n             del self._pending_jobs[:]\n \n         self.state = STATE_PAUSED if paused else STATE_RUNNING\n-        self._logger.info('Scheduler started')\n-        self._dispatch_event(SchedulerEvent(EVENT_SCHEDULER_START))\n+        self._logger.info(\"Scheduler started\")\n+        self._dispatch_event(SchedulerEvent(EVENT_SCHEDULER_STARTED))\n \n         if not paused:\n             self.wakeup()\n@@ -192,14 +235,14 @@ def shutdown(self, wait=True):\n \n         # Shut down all executors\n         with self._executors_lock, self._jobstores_lock:\n-            for executor in six.itervalues(self._executors):\n+            for executor in self._executors.values():\n                 executor.shutdown(wait)\n \n             # Shut down all job stores\n-            for jobstore in six.itervalues(self._jobstores):\n+            for jobstore in self._jobstores.values():\n                 jobstore.shutdown()\n \n-        self._logger.info('Scheduler has been shut down')\n+        self._logger.info(\"Scheduler has been shut down\")\n         self._dispatch_event(SchedulerEvent(EVENT_SCHEDULER_SHUTDOWN))\n \n     def pause(self):\n@@ -214,7 +257,7 @@ def pause(self):\n             raise SchedulerNotRunningError\n         elif self.state == STATE_RUNNING:\n             self.state = STATE_PAUSED\n-            self._logger.info('Paused scheduler job processing')\n+            self._logger.info(\"Paused scheduler job processing\")\n             self._dispatch_event(SchedulerEvent(EVENT_SCHEDULER_PAUSED))\n \n     def resume(self):\n@@ -223,7 +266,7 @@ def resume(self):\n             raise SchedulerNotRunningError\n         elif self.state == STATE_PAUSED:\n             self.state = STATE_RUNNING\n-            self._logger.info('Resumed scheduler job processing')\n+            self._logger.info(\"Resumed scheduler job processing\")\n             self._dispatch_event(SchedulerEvent(EVENT_SCHEDULER_RESUMED))\n             self.wakeup()\n \n@@ -237,7 +280,7 @@ def running(self):\n         \"\"\"\n         return self.state != STATE_STOPPED\n \n-    def add_executor(self, executor, alias='default', **executor_opts):\n+    def add_executor(self, executor, alias=\"default\", **executor_opts):\n         \"\"\"\n         Adds an executor to this scheduler.\n \n@@ -252,17 +295,20 @@ def add_executor(self, executor, alias='default', **executor_opts):\n         \"\"\"\n         with self._executors_lock:\n             if alias in self._executors:\n-                raise ValueError('This scheduler already has an executor by the alias of \"%s\"' %\n-                                 alias)\n+                raise ValueError(\n+                    f'This scheduler already has an executor by the alias of \"{alias}\"'\n+                )\n \n             if isinstance(executor, BaseExecutor):\n                 self._executors[alias] = executor\n-            elif isinstance(executor, six.string_types):\n+            elif isinstance(executor, str):\n                 self._executors[alias] = executor = self._create_plugin_instance(\n-                    'executor', executor, executor_opts)\n+                    \"executor\", executor, executor_opts\n+                )\n             else:\n-                raise TypeError('Expected an executor instance or a string, got %s instead' %\n-                                executor.__class__.__name__)\n+                raise TypeError(\n+                    f\"Expected an executor instance or a string, got {executor.__class__.__name__} instead\"\n+                )\n \n             # Start the executor right away if the scheduler is running\n             if self.state != STATE_STOPPED:\n@@ -288,7 +334,7 @@ def remove_executor(self, alias, shutdown=True):\n \n         self._dispatch_event(SchedulerEvent(EVENT_EXECUTOR_REMOVED, alias))\n \n-    def add_jobstore(self, jobstore, alias='default', **jobstore_opts):\n+    def add_jobstore(self, jobstore, alias=\"default\", **jobstore_opts):\n         \"\"\"\n         Adds a job store to this scheduler.\n \n@@ -302,17 +348,20 @@ def add_jobstore(self, jobstore, alias='default', **jobstore_opts):\n         \"\"\"\n         with self._jobstores_lock:\n             if alias in self._jobstores:\n-                raise ValueError('This scheduler already has a job store by the alias of \"%s\"' %\n-                                 alias)\n+                raise ValueError(\n+                    f'This scheduler already has a job store by the alias of \"{alias}\"'\n+                )\n \n             if isinstance(jobstore, BaseJobStore):\n                 self._jobstores[alias] = jobstore\n-            elif isinstance(jobstore, six.string_types):\n+            elif isinstance(jobstore, str):\n                 self._jobstores[alias] = jobstore = self._create_plugin_instance(\n-                    'jobstore', jobstore, jobstore_opts)\n+                    \"jobstore\", jobstore, jobstore_opts\n+                )\n             else:\n-                raise TypeError('Expected a job store instance or a string, got %s instead' %\n-                                jobstore.__class__.__name__)\n+                raise TypeError(\n+                    f\"Expected a job store instance or a string, got {jobstore.__class__.__name__} instead\"\n+                )\n \n             # Start the job store right away if the scheduler isn't stopped\n             if self.state != STATE_STOPPED:\n@@ -371,10 +420,23 @@ def remove_listener(self, callback):\n                 if callback == cb:\n                     del self._listeners[i]\n \n-    def add_job(self, func, trigger=None, args=None, kwargs=None, id=None, name=None,\n-                misfire_grace_time=undefined, coalesce=undefined, max_instances=undefined,\n-                next_run_time=undefined, jobstore='default', executor='default',\n-                replace_existing=False, **trigger_args):\n+    def add_job(\n+        self,\n+        func,\n+        trigger=None,\n+        args=None,\n+        kwargs=None,\n+        id=None,\n+        name=None,\n+        misfire_grace_time=undefined,\n+        coalesce=undefined,\n+        max_instances=undefined,\n+        next_run_time=undefined,\n+        jobstore=\"default\",\n+        executor=\"default\",\n+        replace_existing=False,\n+        **trigger_args,\n+    ):\n         \"\"\"\n         add_job(func, trigger=None, args=None, kwargs=None, id=None, \\\n             name=None, misfire_grace_time=undefined, coalesce=undefined, \\\n@@ -421,37 +483,51 @@ def add_job(self, func, trigger=None, args=None, kwargs=None, id=None, name=None\n \n         \"\"\"\n         job_kwargs = {\n-            'trigger': self._create_trigger(trigger, trigger_args),\n-            'executor': executor,\n-            'func': func,\n-            'args': tuple(args) if args is not None else (),\n-            'kwargs': dict(kwargs) if kwargs is not None else {},\n-            'id': id,\n-            'name': name,\n-            'misfire_grace_time': misfire_grace_time,\n-            'coalesce': coalesce,\n-            'max_instances': max_instances,\n-            'next_run_time': next_run_time\n+            \"trigger\": self._create_trigger(trigger, trigger_args),\n+            \"executor\": executor,\n+            \"func\": func,\n+            \"args\": tuple(args) if args is not None else (),\n+            \"kwargs\": dict(kwargs) if kwargs is not None else {},\n+            \"id\": id,\n+            \"name\": name,\n+            \"misfire_grace_time\": misfire_grace_time,\n+            \"coalesce\": coalesce,\n+            \"max_instances\": max_instances,\n+            \"next_run_time\": next_run_time,\n         }\n-        job_kwargs = dict((key, value) for key, value in six.iteritems(job_kwargs) if\n-                          value is not undefined)\n+        job_kwargs = dict(\n+            (key, value) for key, value in job_kwargs.items() if value is not undefined\n+        )\n         job = Job(self, **job_kwargs)\n \n         # Don't really add jobs to job stores before the scheduler is up and running\n         with self._jobstores_lock:\n             if self.state == STATE_STOPPED:\n                 self._pending_jobs.append((job, jobstore, replace_existing))\n-                self._logger.info('Adding job tentatively -- it will be properly scheduled when '\n-                                  'the scheduler starts')\n+                self._logger.info(\n+                    \"Adding job tentatively -- it will be properly scheduled when \"\n+                    \"the scheduler starts\"\n+                )\n             else:\n                 self._real_add_job(job, jobstore, replace_existing)\n \n         return job\n \n-    def scheduled_job(self, trigger, args=None, kwargs=None, id=None, name=None,\n-                      misfire_grace_time=undefined, coalesce=undefined, max_instances=undefined,\n-                      next_run_time=undefined, jobstore='default', executor='default',\n-                      **trigger_args):\n+    def scheduled_job(\n+        self,\n+        trigger,\n+        args=None,\n+        kwargs=None,\n+        id=None,\n+        name=None,\n+        misfire_grace_time=undefined,\n+        coalesce=undefined,\n+        max_instances=undefined,\n+        next_run_time=undefined,\n+        jobstore=\"default\",\n+        executor=\"default\",\n+        **trigger_args,\n+    ):\n         \"\"\"\n         scheduled_job(trigger, args=None, kwargs=None, id=None, \\\n             name=None, misfire_grace_time=undefined, \\\n@@ -466,10 +542,26 @@ def scheduled_job(self, trigger, args=None, kwargs=None, id=None, name=None,\n         store. The scheduler cannot, however, enforce this requirement.\n \n         \"\"\"\n+\n         def inner(func):\n-            self.add_job(func, trigger, args, kwargs, id, name, misfire_grace_time, coalesce,\n-                         max_instances, next_run_time, jobstore, executor, True, **trigger_args)\n+            self.add_job(\n+                func,\n+                trigger,\n+                args,\n+                kwargs,\n+                id,\n+                name,\n+                misfire_grace_time,\n+                coalesce,\n+                max_instances,\n+                next_run_time,\n+                jobstore,\n+                executor,\n+                True,\n+                **trigger_args,\n+            )\n             return func\n+\n         return inner\n \n     def modify_job(self, job_id, jobstore=None, **changes):\n@@ -512,7 +604,9 @@ def reschedule_job(self, job_id, jobstore=None, trigger=None, **trigger_args):\n         trigger = self._create_trigger(trigger, trigger_args)\n         now = datetime.now(self.timezone)\n         next_run_time = trigger.get_next_fire_time(None, now)\n-        return self.modify_job(job_id, jobstore, trigger=trigger, next_run_time=next_run_time)\n+        return self.modify_job(\n+            job_id, jobstore, trigger=trigger, next_run_time=next_run_time\n+        )\n \n     def pause_job(self, job_id, jobstore=None):\n         \"\"\"\n@@ -558,9 +652,12 @@ def get_jobs(self, jobstore=None, pending=None):\n \n         \"\"\"\n         if pending is not None:\n-            warnings.warn('The \"pending\" option is deprecated -- get_jobs() always returns '\n-                          'scheduled jobs if the scheduler has been started and pending jobs '\n-                          'otherwise', DeprecationWarning)\n+            warnings.warn(\n+                'The \"pending\" option is deprecated -- get_jobs() always returns '\n+                \"scheduled jobs if the scheduler has been started and pending jobs \"\n+                \"otherwise\",\n+                DeprecationWarning,\n+            )\n \n         with self._jobstores_lock:\n             jobs = []\n@@ -569,7 +666,7 @@ def get_jobs(self, jobstore=None, pending=None):\n                     if jobstore is None or alias == jobstore:\n                         jobs.append(job)\n             else:\n-                for alias, store in six.iteritems(self._jobstores):\n+                for alias, store in self._jobstores.items():\n                     if jobstore is None or alias == jobstore:\n                         jobs.extend(store.get_all_jobs())\n \n@@ -612,7 +709,7 @@ def remove_job(self, job_id, jobstore=None):\n             else:\n                 # Otherwise, try to remove it from each store until it succeeds or we run out of\n                 # stores to check\n-                for alias, store in six.iteritems(self._jobstores):\n+                for alias, store in self._jobstores.items():\n                     if jobstore in (None, alias):\n                         try:\n                             store.remove_job(job_id)\n@@ -628,7 +725,7 @@ def remove_job(self, job_id, jobstore=None):\n         event = JobEvent(EVENT_JOB_REMOVED, job_id, jobstore_alias)\n         self._dispatch_event(event)\n \n-        self._logger.info('Removed job %s', job_id)\n+        self._logger.info(\"Removed job %s\", job_id)\n \n     def remove_all_jobs(self, jobstore=None):\n         \"\"\"\n@@ -640,12 +737,15 @@ def remove_all_jobs(self, jobstore=None):\n         with self._jobstores_lock:\n             if self.state == STATE_STOPPED:\n                 if jobstore:\n-                    self._pending_jobs = [pending for pending in self._pending_jobs if\n-                                          pending[1] != jobstore]\n+                    self._pending_jobs = [\n+                        pending\n+                        for pending in self._pending_jobs\n+                        if pending[1] != jobstore\n+                    ]\n                 else:\n                     self._pending_jobs = []\n             else:\n-                for alias, store in six.iteritems(self._jobstores):\n+                for alias, store in self._jobstores.items():\n                     if jobstore in (None, alias):\n                         store.remove_all_jobs()\n \n@@ -666,23 +766,123 @@ def print_jobs(self, jobstore=None, out=None):\n         out = out or sys.stdout\n         with self._jobstores_lock:\n             if self.state == STATE_STOPPED:\n-                print(u'Pending jobs:', file=out)\n+                print(\"Pending jobs:\", file=out)\n                 if self._pending_jobs:\n                     for job, jobstore_alias, replace_existing in self._pending_jobs:\n                         if jobstore in (None, jobstore_alias):\n-                            print(u'    %s' % job, file=out)\n+                            print(f\"    {job}\", file=out)\n                 else:\n-                    print(u'    No pending jobs', file=out)\n+                    print(\"    No pending jobs\", file=out)\n             else:\n-                for alias, store in sorted(six.iteritems(self._jobstores)):\n+                for alias, store in sorted(self._jobstores.items()):\n                     if jobstore in (None, alias):\n-                        print(u'Jobstore %s:' % alias, file=out)\n+                        print(f\"Jobstore {alias}:\", file=out)\n                         jobs = store.get_all_jobs()\n                         if jobs:\n                             for job in jobs:\n-                                print(u'    %s' % job, file=out)\n+                                print(f\"    {job}\", file=out)\n                         else:\n-                            print(u'    No scheduled jobs', file=out)\n+                            print(\"    No scheduled jobs\", file=out)\n+\n+    def export_jobs(self, outfile, jobstore=None):\n+        \"\"\"\n+        Export stored jobs as JSON.\n+\n+        :param outfile: either a file object opened in text write mode (\"w\"), or a path\n+            to the target file\n+        :param jobstore: alias of the job store to export jobs from (if omitted, export\n+            from all configured job stores)\n+\n+        \"\"\"\n+        import json\n+        import pickle\n+        from base64 import b64encode\n+\n+        from apscheduler import version\n+\n+        if self.state == STATE_STOPPED:\n+            raise RuntimeError(\n+                \"the scheduler must have been started for job export to work\"\n+            )\n+\n+        def encode_with_pickle(obj):\n+            return b64encode(pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)).decode(\"ascii\")\n+\n+        def json_default(obj):\n+            if hasattr(obj, \"__getstate__\") and hasattr(obj, \"__setstate__\"):\n+                state = obj.__getstate__()\n+                if isinstance(state, Mapping):\n+                    return {\n+                        \"__apscheduler_class__\": obj_to_ref(obj.__class__),\n+                        \"__apscheduler_state__\": state,\n+                    }\n+\n+            return {\"__apscheduler_pickle__\": encode_with_pickle(obj)}\n+\n+        with self._jobstores_lock:\n+            all_jobs = [\n+                job\n+                for alias, store in self._jobstores.items()\n+                for job in store.get_all_jobs()\n+                if jobstore in (None, alias)\n+            ]\n+\n+        with ExitStack() as stack:\n+            if not hasattr(outfile, \"write\"):\n+                outfile = stack.enter_context(open(outfile, \"w\"))\n+\n+            json.dump(\n+                {\n+                    \"version\": 1,\n+                    \"scheduler_version\": version,\n+                    \"jobs\": [job.__getstate__() for job in all_jobs],\n+                },\n+                outfile,\n+                default=json_default,\n+            )\n+\n+    def import_jobs(self, infile, jobstore=\"default\"):\n+        \"\"\"\n+        Import jobs previously exported via :meth:`export_jobs.\n+\n+        :param infile: either a file object opened in text read mode (\"r\") or a path to\n+            a JSON file containing previously exported jobs\n+        :param jobstore: the alias of the job store to import the jobs to\n+\n+        \"\"\"\n+        import json\n+        import pickle\n+        from base64 import b64decode\n+\n+        def json_object_hook(dct):\n+            if pickle_data := dct.get(\"__apscheduler_pickle__\"):\n+                return pickle.loads(b64decode(pickle_data))\n+\n+            if obj_class := dct.get(\"__apscheduler_class__\"):\n+                if obj_state := dct.get(\"__apscheduler_state__\"):\n+                    obj_class = ref_to_obj(obj_class)\n+                    obj = object.__new__(obj_class)\n+                    obj.__setstate__(obj_state)\n+                    return obj\n+\n+            return dct\n+\n+        jobstore = self._jobstores[jobstore]\n+        with ExitStack() as stack:\n+            if not hasattr(infile, \"read\"):\n+                infile = stack.enter_context(open(infile))\n+\n+            data = json.load(infile, object_hook=json_object_hook)\n+            if not isinstance(data, dict):\n+                raise ValueError()\n+\n+            if (version := data.get(\"version\", None)) != 1:\n+                raise ValueError(f\"unrecognized version: {version}\")\n+\n+            for job_state in data[\"jobs\"]:\n+                job = object.__new__(Job)\n+                job.__setstate__(job_state)\n+                jobstore.add_job(job)\n \n     @abstractmethod\n     def wakeup(self):\n@@ -697,65 +897,68 @@ def wakeup(self):\n \n     def _configure(self, config):\n         # Set general options\n-        self._logger = maybe_ref(config.pop('logger', None)) or getLogger('apscheduler.scheduler')\n-        self.timezone = astimezone(config.pop('timezone', None)) or get_localzone()\n-        self.jobstore_retry_interval = float(config.pop('jobstore_retry_interval', 10))\n+        self._logger = maybe_ref(config.pop(\"logger\", None)) or getLogger(\n+            \"apscheduler.scheduler\"\n+        )\n+        self.timezone = astimezone(config.pop(\"timezone\", None)) or get_localzone()\n+        self.jobstore_retry_interval = float(config.pop(\"jobstore_retry_interval\", 10))\n \n         # Set the job defaults\n-        job_defaults = config.get('job_defaults', {})\n+        job_defaults = config.get(\"job_defaults\", {})\n         self._job_defaults = {\n-            'misfire_grace_time': asint(job_defaults.get('misfire_grace_time', 1)),\n-            'coalesce': asbool(job_defaults.get('coalesce', True)),\n-            'max_instances': asint(job_defaults.get('max_instances', 1))\n+            \"misfire_grace_time\": asint(job_defaults.get(\"misfire_grace_time\", 1)),\n+            \"coalesce\": asbool(job_defaults.get(\"coalesce\", True)),\n+            \"max_instances\": asint(job_defaults.get(\"max_instances\", 1)),\n         }\n \n         # Configure executors\n         self._executors.clear()\n-        for alias, value in six.iteritems(config.get('executors', {})):\n+        for alias, value in config.get(\"executors\", {}).items():\n             if isinstance(value, BaseExecutor):\n                 self.add_executor(value, alias)\n             elif isinstance(value, MutableMapping):\n-                executor_class = value.pop('class', None)\n-                plugin = value.pop('type', None)\n+                executor_class = value.pop(\"class\", None)\n+                plugin = value.pop(\"type\", None)\n                 if plugin:\n-                    executor = self._create_plugin_instance('executor', plugin, value)\n+                    executor = self._create_plugin_instance(\"executor\", plugin, value)\n                 elif executor_class:\n                     cls = maybe_ref(executor_class)\n                     executor = cls(**value)\n                 else:\n                     raise ValueError(\n-                        'Cannot create executor \"%s\" -- either \"type\" or \"class\" must be defined' %\n-                        alias)\n+                        f'Cannot create executor \"{alias}\" -- either \"type\" or \"class\" must be defined'\n+                    )\n \n                 self.add_executor(executor, alias)\n             else:\n                 raise TypeError(\n-                    \"Expected executor instance or dict for executors['%s'], got %s instead\" %\n-                    (alias, value.__class__.__name__))\n+                    f\"Expected executor instance or dict for executors['{alias}'], got {value.__class__.__name__} instead\"\n+                )\n \n         # Configure job stores\n         self._jobstores.clear()\n-        for alias, value in six.iteritems(config.get('jobstores', {})):\n+        for alias, value in config.get(\"jobstores\", {}).items():\n             if isinstance(value, BaseJobStore):\n                 self.add_jobstore(value, alias)\n             elif isinstance(value, MutableMapping):\n-                jobstore_class = value.pop('class', None)\n-                plugin = value.pop('type', None)\n+                jobstore_class = value.pop(\"class\", None)\n+                plugin = value.pop(\"type\", None)\n                 if plugin:\n-                    jobstore = self._create_plugin_instance('jobstore', plugin, value)\n+                    jobstore = self._create_plugin_instance(\"jobstore\", plugin, value)\n                 elif jobstore_class:\n                     cls = maybe_ref(jobstore_class)\n                     jobstore = cls(**value)\n                 else:\n                     raise ValueError(\n-                        'Cannot create job store \"%s\" -- either \"type\" or \"class\" must be '\n-                        'defined' % alias)\n+                        f'Cannot create job store \"{alias}\" -- either \"type\" or \"class\" must be '\n+                        \"defined\"\n+                    )\n \n                 self.add_jobstore(jobstore, alias)\n             else:\n                 raise TypeError(\n-                    \"Expected job store instance or dict for jobstores['%s'], got %s instead\" %\n-                    (alias, value.__class__.__name__))\n+                    f\"Expected job store instance or dict for jobstores['{alias}'], got {value.__class__.__name__} instead\"\n+                )\n \n     def _create_default_executor(self):\n         \"\"\"Creates a default executor store, specific to the particular scheduler type.\"\"\"\n@@ -777,7 +980,7 @@ def _lookup_executor(self, alias):\n         try:\n             return self._executors[alias]\n         except KeyError:\n-            raise KeyError('No such executor: %s' % alias)\n+            raise KeyError(f\"No such executor: {alias}\")\n \n     def _lookup_jobstore(self, alias):\n         \"\"\"\n@@ -791,7 +994,7 @@ def _lookup_jobstore(self, alias):\n         try:\n             return self._jobstores[alias]\n         except KeyError:\n-            raise KeyError('No such job store: %s' % alias)\n+            raise KeyError(f\"No such job store: {alias}\")\n \n     def _lookup_job(self, job_id, jobstore_alias):\n         \"\"\"\n@@ -811,7 +1014,7 @@ def _lookup_job(self, job_id, jobstore_alias):\n                     return job, None\n         else:\n             # Look in all job stores\n-            for alias, store in six.iteritems(self._jobstores):\n+            for alias, store in self._jobstores.items():\n                 if jobstore_alias in (None, alias):\n                     job = store.lookup_job(job_id)\n                     if job is not None:\n@@ -834,15 +1037,17 @@ def _dispatch_event(self, event):\n                 try:\n                     cb(event)\n                 except BaseException:\n-                    self._logger.exception('Error notifying listener')\n+                    self._logger.exception(\"Error notifying listener\")\n \n     def _check_uwsgi(self):\n         \"\"\"Check if we're running under uWSGI with threads disabled.\"\"\"\n-        uwsgi_module = sys.modules.get('uwsgi')\n-        if not getattr(uwsgi_module, 'has_threads', True):\n-            raise RuntimeError('The scheduler seems to be running under uWSGI, but threads have '\n-                               'been disabled. You must run uWSGI with the --enable-threads '\n-                               'option for the scheduler to work.')\n+        uwsgi_module = sys.modules.get(\"uwsgi\")\n+        if not getattr(uwsgi_module, \"has_threads\", True):\n+            raise RuntimeError(\n+                \"The scheduler seems to be running under uWSGI, but threads have \"\n+                \"been disabled. You must run uWSGI with the --enable-threads \"\n+                \"option for the scheduler to work.\"\n+            )\n \n     def _real_add_job(self, job, jobstore_alias, replace_existing):\n         \"\"\"\n@@ -853,14 +1058,14 @@ def _real_add_job(self, job, jobstore_alias, replace_existing):\n         \"\"\"\n         # Fill in undefined values with defaults\n         replacements = {}\n-        for key, value in six.iteritems(self._job_defaults):\n+        for key, value in self._job_defaults.items():\n             if not hasattr(job, key):\n                 replacements[key] = value\n \n         # Calculate the next run time if there is none defined\n-        if not hasattr(job, 'next_run_time'):\n+        if not hasattr(job, \"next_run_time\"):\n             now = datetime.now(self.timezone)\n-            replacements['next_run_time'] = job.trigger.get_next_fire_time(None, now)\n+            replacements[\"next_run_time\"] = job.trigger.get_next_fire_time(None, now)\n \n         # Apply any replacements\n         job._modify(**replacements)\n@@ -891,9 +1096,9 @@ def _real_add_job(self, job, jobstore_alias, replace_existing):\n     def _create_plugin_instance(self, type_, alias, constructor_kwargs):\n         \"\"\"Creates an instance of the given plugin type, loading the plugin first if necessary.\"\"\"\n         plugin_container, class_container, base_class = {\n-            'trigger': (self._trigger_plugins, self._trigger_classes, BaseTrigger),\n-            'jobstore': (self._jobstore_plugins, self._jobstore_classes, BaseJobStore),\n-            'executor': (self._executor_plugins, self._executor_classes, BaseExecutor)\n+            \"trigger\": (self._trigger_plugins, self._trigger_classes, BaseTrigger),\n+            \"jobstore\": (self._jobstore_plugins, self._jobstore_classes, BaseJobStore),\n+            \"executor\": (self._executor_plugins, self._executor_classes, BaseExecutor),\n         }[type_]\n \n         try:\n@@ -902,27 +1107,29 @@ def _create_plugin_instance(self, type_, alias, constructor_kwargs):\n             if alias in plugin_container:\n                 plugin_cls = class_container[alias] = plugin_container[alias].load()\n                 if not issubclass(plugin_cls, base_class):\n-                    raise TypeError('The {0} entry point does not point to a {0} class'.\n-                                    format(type_))\n+                    raise TypeError(\n+                        f\"The {type_} entry point does not point to a {type_} class\"\n+                    )\n             else:\n-                raise LookupError('No {0} by the name \"{1}\" was found'.format(type_, alias))\n+                raise LookupError(f'No {type_} by the name \"{alias}\" was found')\n \n         return plugin_cls(**constructor_kwargs)\n \n     def _create_trigger(self, trigger, trigger_args):\n         if isinstance(trigger, BaseTrigger):\n             return trigger\n         elif trigger is None:\n-            trigger = 'date'\n-        elif not isinstance(trigger, six.string_types):\n-            raise TypeError('Expected a trigger instance or string, got %s instead' %\n-                            trigger.__class__.__name__)\n+            trigger = \"date\"\n+        elif not isinstance(trigger, str):\n+            raise TypeError(\n+                f\"Expected a trigger instance or string, got {trigger.__class__.__name__} instead\"\n+            )\n \n         # Use the scheduler's time zone if nothing else is specified\n-        trigger_args.setdefault('timezone', self.timezone)\n+        trigger_args.setdefault(\"timezone\", self.timezone)\n \n         # Instantiate the trigger class\n-        return self._create_plugin_instance('trigger', trigger, trigger_args)\n+        return self._create_plugin_instance(\"trigger\", trigger, trigger_args)\n \n     def _create_lock(self):\n         \"\"\"Creates a reentrant lock object.\"\"\"\n@@ -938,23 +1145,28 @@ def _process_jobs(self):\n \n         \"\"\"\n         if self.state == STATE_PAUSED:\n-            self._logger.debug('Scheduler is paused -- not processing jobs')\n+            self._logger.debug(\"Scheduler is paused -- not processing jobs\")\n             return None\n \n-        self._logger.debug('Looking for jobs to run')\n+        self._logger.debug(\"Looking for jobs to run\")\n         now = datetime.now(self.timezone)\n         next_wakeup_time = None\n         events = []\n \n         with self._jobstores_lock:\n-            for jobstore_alias, jobstore in six.iteritems(self._jobstores):\n+            for jobstore_alias, jobstore in self._jobstores.items():\n                 try:\n                     due_jobs = jobstore.get_due_jobs(now)\n                 except Exception as e:\n                     # Schedule a wakeup at least in jobstore_retry_interval seconds\n-                    self._logger.warning('Error getting due jobs from job store %r: %s',\n-                                         jobstore_alias, e)\n-                    retry_wakeup_time = now + timedelta(seconds=self.jobstore_retry_interval)\n+                    self._logger.warning(\n+                        \"Error getting due jobs from job store %r: %s\",\n+                        jobstore_alias,\n+                        e,\n+                    )\n+                    retry_wakeup_time = now + timedelta(\n+                        seconds=self.jobstore_retry_interval\n+                    )\n                     if not next_wakeup_time or next_wakeup_time > retry_wakeup_time:\n                         next_wakeup_time = retry_wakeup_time\n \n@@ -967,33 +1179,51 @@ def _process_jobs(self):\n                     except BaseException:\n                         self._logger.error(\n                             'Executor lookup (\"%s\") failed for job \"%s\" -- removing it from the '\n-                            'job store', job.executor, job)\n+                            \"job store\",\n+                            job.executor,\n+                            job,\n+                        )\n                         self.remove_job(job.id, jobstore_alias)\n                         continue\n \n                     run_times = job._get_run_times(now)\n-                    run_times = run_times[-1:] if run_times and job.coalesce else run_times\n+                    run_times = (\n+                        run_times[-1:] if run_times and job.coalesce else run_times\n+                    )\n                     if run_times:\n                         try:\n                             executor.submit_job(job, run_times)\n                         except MaxInstancesReachedError:\n                             self._logger.warning(\n                                 'Execution of job \"%s\" skipped: maximum number of running '\n-                                'instances reached (%d)', job, job.max_instances)\n-                            event = JobSubmissionEvent(EVENT_JOB_MAX_INSTANCES, job.id,\n-                                                       jobstore_alias, run_times)\n+                                \"instances reached (%d)\",\n+                                job,\n+                                job.max_instances,\n+                            )\n+                            event = JobSubmissionEvent(\n+                                EVENT_JOB_MAX_INSTANCES,\n+                                job.id,\n+                                jobstore_alias,\n+                                run_times,\n+                            )\n                             events.append(event)\n                         except BaseException:\n-                            self._logger.exception('Error submitting job \"%s\" to executor \"%s\"',\n-                                                   job, job.executor)\n+                            self._logger.exception(\n+                                'Error submitting job \"%s\" to executor \"%s\"',\n+                                job,\n+                                job.executor,\n+                            )\n                         else:\n-                            event = JobSubmissionEvent(EVENT_JOB_SUBMITTED, job.id, jobstore_alias,\n-                                                       run_times)\n+                            event = JobSubmissionEvent(\n+                                EVENT_JOB_SUBMITTED, job.id, jobstore_alias, run_times\n+                            )\n                             events.append(event)\n \n                         # Update the job if it has a next execution time.\n                         # Otherwise remove it from the job store.\n-                        job_next_run = job.trigger.get_next_fire_time(run_times[-1], now)\n+                        job_next_run = job.trigger.get_next_fire_time(\n+                            run_times[-1], now\n+                        )\n                         if job_next_run:\n                             job._modify(next_run_time=job_next_run)\n                             jobstore.update_job(job)\n@@ -1003,8 +1233,10 @@ def _process_jobs(self):\n                 # Set a new next wakeup time if there isn't one yet or\n                 # the jobstore has an even earlier one\n                 jobstore_next_run_time = jobstore.get_next_run_time()\n-                if jobstore_next_run_time and (next_wakeup_time is None or\n-                                               jobstore_next_run_time < next_wakeup_time):\n+                if jobstore_next_run_time and (\n+                    next_wakeup_time is None\n+                    or jobstore_next_run_time < next_wakeup_time\n+                ):\n                     next_wakeup_time = jobstore_next_run_time.astimezone(self.timezone)\n \n         # Dispatch collected events\n@@ -1014,13 +1246,19 @@ def _process_jobs(self):\n         # Determine the delay until this method should be called again\n         if self.state == STATE_PAUSED:\n             wait_seconds = None\n-            self._logger.debug('Scheduler is paused; waiting until resume() is called')\n+            self._logger.debug(\"Scheduler is paused; waiting until resume() is called\")\n         elif next_wakeup_time is None:\n             wait_seconds = None\n-            self._logger.debug('No jobs; waiting until a job is added')\n+            self._logger.debug(\"No jobs; waiting until a job is added\")\n         else:\n-            wait_seconds = min(max(timedelta_seconds(next_wakeup_time - now), 0), TIMEOUT_MAX)\n-            self._logger.debug('Next wakeup is due at %s (in %f seconds)', next_wakeup_time,\n-                               wait_seconds)\n+            now = datetime.now(self.timezone)\n+            wait_seconds = min(\n+                max((next_wakeup_time - now).total_seconds(), 0), TIMEOUT_MAX\n+            )\n+            self._logger.debug(\n+                \"Next wakeup is due at %s (in %f seconds)\",\n+                next_wakeup_time,\n+                wait_seconds,\n+            )\n \n         return wait_seconds"}, {"sha": "0330885d98210f80780295ce6737780f2c61c30c", "filename": "lib/apscheduler/schedulers/blocking.py", "status": "modified", "additions": 5, "deletions": 7, "changes": 12, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fblocking.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fblocking.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fschedulers%2Fblocking.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,27 +1,25 @@\n-from __future__ import absolute_import\n+from threading import TIMEOUT_MAX, Event\n \n-from threading import Event\n-\n-from apscheduler.schedulers.base import BaseScheduler, STATE_STOPPED\n-from apscheduler.util import TIMEOUT_MAX\n+from apscheduler.schedulers.base import STATE_STOPPED, BaseScheduler\n \n \n class BlockingScheduler(BaseScheduler):\n     \"\"\"\n     A scheduler that runs in the foreground\n     (:meth:`~apscheduler.schedulers.base.BaseScheduler.start` will block).\n     \"\"\"\n+\n     _event = None\n \n     def start(self, *args, **kwargs):\n         if self._event is None or self._event.is_set():\n             self._event = Event()\n \n-        super(BlockingScheduler, self).start(*args, **kwargs)\n+        super().start(*args, **kwargs)\n         self._main_loop()\n \n     def shutdown(self, wait=True):\n-        super(BlockingScheduler, self).shutdown(wait)\n+        super().shutdown(wait)\n         self._event.set()\n \n     def _main_loop(self):"}, {"sha": "e32ad643b40c3032e57739b16470208d21d7241c", "filename": "lib/apscheduler/schedulers/gevent.py", "status": "modified", "additions": 6, "deletions": 7, "changes": 13, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fgevent.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fgevent.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fschedulers%2Fgevent.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,14 +1,12 @@\n-from __future__ import absolute_import\n-\n-from apscheduler.schedulers.blocking import BlockingScheduler\n from apscheduler.schedulers.base import BaseScheduler\n+from apscheduler.schedulers.blocking import BlockingScheduler\n \n try:\n+    import gevent\n     from gevent.event import Event\n     from gevent.lock import RLock\n-    import gevent\n-except ImportError:  # pragma: nocover\n-    raise ImportError('GeventScheduler requires gevent installed')\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"GeventScheduler requires gevent installed\") from exc\n \n \n class GeventScheduler(BlockingScheduler):\n@@ -23,7 +21,7 @@ def start(self, *args, **kwargs):\n         return self._greenlet\n \n     def shutdown(self, *args, **kwargs):\n-        super(GeventScheduler, self).shutdown(*args, **kwargs)\n+        super().shutdown(*args, **kwargs)\n         self._greenlet.join()\n         del self._greenlet\n \n@@ -32,4 +30,5 @@ def _create_lock(self):\n \n     def _create_default_executor(self):\n         from apscheduler.executors.gevent import GeventExecutor\n+\n         return GeventExecutor()"}, {"sha": "457b5395ed7c5363fa9d82969cdd9f20b495b24b", "filename": "lib/apscheduler/schedulers/qt.py", "status": "modified", "additions": 12, "deletions": 18, "changes": 30, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fqt.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Fqt.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fschedulers%2Fqt.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,24 +1,18 @@\n-from __future__ import absolute_import\n+from importlib import import_module\n+from itertools import product\n \n from apscheduler.schedulers.base import BaseScheduler\n \n-try:\n-    from PyQt5.QtCore import QObject, QTimer\n-except (ImportError, RuntimeError):  # pragma: nocover\n+for version, pkgname in product(range(6, 1, -1), (\"PySide\", \"PyQt\")):\n     try:\n-        from PyQt4.QtCore import QObject, QTimer\n+        qtcore = import_module(pkgname + str(version) + \".QtCore\")\n     except ImportError:\n-        try:\n-            from PySide6.QtCore import QObject, QTimer  # noqa\n-        except ImportError:\n-            try:\n-                from PySide2.QtCore import QObject, QTimer  # noqa\n-            except ImportError:\n-                try:\n-                    from PySide.QtCore import QObject, QTimer  # noqa\n-                except ImportError:\n-                    raise ImportError('QtScheduler requires either PyQt5, PyQt4, PySide6, PySide2 '\n-                                      'or PySide installed')\n+        pass\n+    else:\n+        QTimer = qtcore.QTimer\n+        break\n+else:\n+    raise ImportError(\"QtScheduler requires either PySide/PyQt (v6 to v2) installed\")\n \n \n class QtScheduler(BaseScheduler):\n@@ -27,7 +21,7 @@ class QtScheduler(BaseScheduler):\n     _timer = None\n \n     def shutdown(self, *args, **kwargs):\n-        super(QtScheduler, self).shutdown(*args, **kwargs)\n+        super().shutdown(*args, **kwargs)\n         self._stop_timer()\n \n     def _start_timer(self, wait_seconds):\n@@ -46,5 +40,5 @@ def wakeup(self):\n         self._start_timer(0)\n \n     def _process_jobs(self):\n-        wait_seconds = super(QtScheduler, self)._process_jobs()\n+        wait_seconds = super()._process_jobs()\n         self._start_timer(wait_seconds)"}, {"sha": "a25764d2458b57b4fc19b64990398ff87705e77a", "filename": "lib/apscheduler/schedulers/tornado.py", "status": "modified", "additions": 21, "deletions": 9, "changes": 30, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Ftornado.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Ftornado.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fschedulers%2Ftornado.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,21 +1,24 @@\n-from __future__ import absolute_import\n-\n from datetime import timedelta\n from functools import wraps\n \n+from apscheduler.schedulers import SchedulerNotRunningError\n from apscheduler.schedulers.base import BaseScheduler\n from apscheduler.util import maybe_ref\n \n try:\n     from tornado.ioloop import IOLoop\n-except ImportError:  # pragma: nocover\n-    raise ImportError('TornadoScheduler requires tornado installed')\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"TornadoScheduler requires tornado installed\") from exc\n \n \n def run_in_ioloop(func):\n     @wraps(func)\n     def wrapper(self, *args, **kwargs):\n+        if self._ioloop is None:\n+            raise SchedulerNotRunningError\n+\n         self._ioloop.add_callback(func, self, *args, **kwargs)\n+\n     return wrapper\n \n \n@@ -34,18 +37,26 @@ class TornadoScheduler(BaseScheduler):\n     _timeout = None\n \n     @run_in_ioloop\n-    def shutdown(self, wait=True):\n-        super(TornadoScheduler, self).shutdown(wait)\n+    def _shutdown(self, wait=True):\n+        super().shutdown(wait)\n         self._stop_timer()\n \n+    def shutdown(self, wait=True):\n+        if not self.running:\n+            raise SchedulerNotRunningError\n+\n+        self._shutdown(wait)\n+\n     def _configure(self, config):\n-        self._ioloop = maybe_ref(config.pop('io_loop', None)) or IOLoop.current()\n-        super(TornadoScheduler, self)._configure(config)\n+        self._ioloop = maybe_ref(config.pop(\"io_loop\", None)) or IOLoop.current()\n+        super()._configure(config)\n \n     def _start_timer(self, wait_seconds):\n         self._stop_timer()\n         if wait_seconds is not None:\n-            self._timeout = self._ioloop.add_timeout(timedelta(seconds=wait_seconds), self.wakeup)\n+            self._timeout = self._ioloop.add_timeout(\n+                timedelta(seconds=wait_seconds), self.wakeup\n+            )\n \n     def _stop_timer(self):\n         if self._timeout:\n@@ -54,6 +65,7 @@ def _stop_timer(self):\n \n     def _create_default_executor(self):\n         from apscheduler.executors.tornado import TornadoExecutor\n+\n         return TornadoExecutor()\n \n     @run_in_ioloop"}, {"sha": "8a9183144cfe4ee339524ce64105eebe0046a46d", "filename": "lib/apscheduler/schedulers/twisted.py", "status": "modified", "additions": 15, "deletions": 8, "changes": 23, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Ftwisted.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Fschedulers%2Ftwisted.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Fschedulers%2Ftwisted.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,20 +1,20 @@\n-from __future__ import absolute_import\n-\n from functools import wraps\n \n+from apscheduler.schedulers import SchedulerNotRunningError\n from apscheduler.schedulers.base import BaseScheduler\n from apscheduler.util import maybe_ref\n \n try:\n     from twisted.internet import reactor as default_reactor\n-except ImportError:  # pragma: nocover\n-    raise ImportError('TwistedScheduler requires Twisted installed')\n+except ImportError as exc:  # pragma: nocover\n+    raise ImportError(\"TwistedScheduler requires Twisted installed\") from exc\n \n \n def run_in_reactor(func):\n     @wraps(func)\n     def wrapper(self, *args, **kwargs):\n         self._reactor.callFromThread(func, self, *args, **kwargs)\n+\n     return wrapper\n \n \n@@ -33,14 +33,20 @@ class TwistedScheduler(BaseScheduler):\n     _delayedcall = None\n \n     def _configure(self, config):\n-        self._reactor = maybe_ref(config.pop('reactor', default_reactor))\n-        super(TwistedScheduler, self)._configure(config)\n+        self._reactor = maybe_ref(config.pop(\"reactor\", default_reactor))\n+        super()._configure(config)\n \n     @run_in_reactor\n-    def shutdown(self, wait=True):\n-        super(TwistedScheduler, self).shutdown(wait)\n+    def _shutdown(self, wait=True):\n+        super().shutdown(wait)\n         self._stop_timer()\n \n+    def shutdown(self, wait=True):\n+        if not self.running:\n+            raise SchedulerNotRunningError\n+\n+        self._shutdown(wait)\n+\n     def _start_timer(self, wait_seconds):\n         self._stop_timer()\n         if wait_seconds is not None:\n@@ -59,4 +65,5 @@ def wakeup(self):\n \n     def _create_default_executor(self):\n         from apscheduler.executors.twisted import TwistedExecutor\n+\n         return TwistedExecutor()"}, {"sha": "917af8ca67926ee08578451bb3cbcd9845620ecc", "filename": "lib/apscheduler/triggers/base.py", "status": "modified", "additions": 2, "deletions": 4, "changes": 6, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fbase.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fbase.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Ftriggers%2Fbase.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,11 +1,9 @@\n+import random\n from abc import ABCMeta, abstractmethod\n from datetime import timedelta\n-import random\n-\n-import six\n \n \n-class BaseTrigger(six.with_metaclass(ABCMeta)):\n+class BaseTrigger(metaclass=ABCMeta):\n     \"\"\"Abstract base class that defines the interface that every trigger must implement.\"\"\"\n \n     __slots__ = ()"}, {"sha": "114e9ea284368137bd6372454371d27f3dd9a1bb", "filename": "lib/apscheduler/triggers/calendarinterval.py", "status": "added", "additions": 186, "deletions": 0, "changes": 186, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcalendarinterval.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcalendarinterval.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Ftriggers%2Fcalendarinterval.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,186 @@\n+from __future__ import annotations\n+\n+from datetime import date, datetime, time, timedelta, tzinfo\n+from typing import Any\n+\n+from tzlocal import get_localzone\n+\n+from apscheduler.triggers.base import BaseTrigger\n+from apscheduler.util import (\n+    asdate,\n+    astimezone,\n+    timezone_repr,\n+)\n+\n+\n+class CalendarIntervalTrigger(BaseTrigger):\n+    \"\"\"\n+    Runs the task on specified calendar-based intervals always at the same exact time of\n+    day.\n+\n+    When calculating the next date, the ``years`` and ``months`` parameters are first\n+    added to the previous date while keeping the day of the month constant. This is\n+    repeated until the resulting date is valid. After that, the ``weeks`` and ``days``\n+    parameters are added to that date. Finally, the date is combined with the given time\n+    (hour, minute, second) to form the final datetime.\n+\n+    This means that if the ``days`` or ``weeks`` parameters are not used, the task will\n+    always be executed on the same day of the month at the same wall clock time,\n+    assuming the date and time are valid.\n+\n+    If the resulting datetime is invalid due to a daylight saving forward shift, the\n+    date is discarded and the process moves on to the next date. If instead the datetime\n+    is ambiguous due to a backward DST shift, the earlier of the two resulting datetimes\n+    is used.\n+\n+    If no previous run time is specified when requesting a new run time (like when\n+    starting for the first time or resuming after being paused), ``start_date`` is used\n+    as a reference and the next valid datetime equal to or later than the current time\n+    will be returned. Otherwise, the next valid datetime starting from the previous run\n+    time is returned, even if it's in the past.\n+\n+    .. warning:: Be wary of setting a start date near the end of the month (29. \u2013 31.)\n+        if you have ``months`` specified in your interval, as this will skip the months\n+        when those days do not exist. Likewise, setting the start date on the leap day\n+        (February 29th) and having ``years`` defined may cause some years to be skipped.\n+\n+        Users are also discouraged from  using a time inside the target timezone's DST\n+        switching period (typically around 2 am) since a date could either be skipped or\n+        repeated due to the specified wall clock time either occurring twice or not at\n+        all.\n+\n+    :param years: number of years to wait\n+    :param months: number of months to wait\n+    :param weeks: number of weeks to wait\n+    :param days: number of days to wait\n+    :param hour: hour to run the task at\n+    :param minute: minute to run the task at\n+    :param second: second to run the task at\n+    :param start_date: first date to trigger on (defaults to current date if omitted)\n+    :param end_date: latest possible date to trigger on\n+    :param timezone: time zone to use for calculating the next fire time (defaults\n+        to scheduler timezone if created via the scheduler, otherwise the local time\n+        zone)\n+    :param jitter: delay the job execution by ``jitter`` seconds at most\n+    \"\"\"\n+\n+    __slots__ = (\n+        \"_time\",\n+        \"days\",\n+        \"end_date\",\n+        \"jitter\",\n+        \"months\",\n+        \"start_date\",\n+        \"timezone\",\n+        \"weeks\",\n+        \"years\",\n+    )\n+\n+    def __init__(\n+        self,\n+        *,\n+        years: int = 0,\n+        months: int = 0,\n+        weeks: int = 0,\n+        days: int = 0,\n+        hour: int = 0,\n+        minute: int = 0,\n+        second: int = 0,\n+        start_date: date | str | None = None,\n+        end_date: date | str | None = None,\n+        timezone: str | tzinfo | None = None,\n+        jitter: int | None = None,\n+    ):\n+        if timezone:\n+            self.timezone = astimezone(timezone)\n+        else:\n+            self.timezone = astimezone(get_localzone())\n+\n+        self.years = years\n+        self.months = months\n+        self.weeks = weeks\n+        self.days = days\n+        self.start_date = asdate(start_date) or date.today()\n+        self.end_date = asdate(end_date)\n+        self.jitter = jitter\n+        self._time = time(hour, minute, second, tzinfo=self.timezone)\n+\n+        if self.years == self.months == self.weeks == self.days == 0:\n+            raise ValueError(\"interval must be at least 1 day long\")\n+\n+        if self.end_date and self.start_date > self.end_date:\n+            raise ValueError(\"end_date cannot be earlier than start_date\")\n+\n+    def get_next_fire_time(\n+        self, previous_fire_time: datetime | None, now: datetime\n+    ) -> datetime | None:\n+        while True:\n+            if previous_fire_time:\n+                year, month = previous_fire_time.year, previous_fire_time.month\n+                while True:\n+                    month += self.months\n+                    year += self.years + (month - 1) // 12\n+                    month = (month - 1) % 12 + 1\n+                    try:\n+                        next_date = date(year, month, previous_fire_time.day)\n+                    except ValueError:\n+                        pass  # Nonexistent date\n+                    else:\n+                        next_date += timedelta(self.days + self.weeks * 7)\n+                        break\n+            else:\n+                next_date = self.start_date\n+\n+            # Don't return any date past end_date\n+            if self.end_date and next_date > self.end_date:\n+                return None\n+\n+            # Combine the date with the designated time and normalize the result\n+            timestamp = datetime.combine(next_date, self._time).timestamp()\n+            next_time = datetime.fromtimestamp(timestamp, self.timezone)\n+\n+            # Check if the time is off due to normalization and a forward DST shift\n+            if next_time.timetz() != self._time:\n+                previous_fire_time = next_time.date()\n+            else:\n+                return self._apply_jitter(next_time, self.jitter, now)\n+\n+    def __getstate__(self) -> dict[str, Any]:\n+        return {\n+            \"version\": 1,\n+            \"interval\": [self.years, self.months, self.weeks, self.days],\n+            \"time\": [self._time.hour, self._time.minute, self._time.second],\n+            \"start_date\": self.start_date,\n+            \"end_date\": self.end_date,\n+            \"timezone\": self.timezone,\n+            \"jitter\": self.jitter,\n+        }\n+\n+    def __setstate__(self, state: dict[str, Any]) -> None:\n+        if state.get(\"version\", 1) > 1:\n+            raise ValueError(\n+                f\"Got serialized data for version {state['version']} of \"\n+                f\"{self.__class__.__name__}, but only versions up to 1 can be handled\"\n+            )\n+\n+        self.years, self.months, self.weeks, self.days = state[\"interval\"]\n+        self.start_date = state[\"start_date\"]\n+        self.end_date = state[\"end_date\"]\n+        self.timezone = state[\"timezone\"]\n+        self.jitter = state[\"jitter\"]\n+        self._time = time(*state[\"time\"], tzinfo=self.timezone)\n+\n+    def __repr__(self) -> str:\n+        fields = []\n+        for field in \"years\", \"months\", \"weeks\", \"days\":\n+            value = getattr(self, field)\n+            if value > 0:\n+                fields.append(f\"{field}={value}\")\n+\n+        fields.append(f\"time={self._time.isoformat()!r}\")\n+        fields.append(f\"start_date='{self.start_date}'\")\n+        if self.end_date:\n+            fields.append(f\"end_date='{self.end_date}'\")\n+\n+        fields.append(f\"timezone={timezone_repr(self.timezone)!r}\")\n+        return f\"{self.__class__.__name__}({', '.join(fields)})\""}, {"sha": "7d526e9bdfb602473871056ca98f1607cd73da30", "filename": "lib/apscheduler/triggers/combining.py", "status": "modified", "additions": 37, "deletions": 18, "changes": 55, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcombining.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcombining.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Ftriggers%2Fcombining.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -3,37 +3,43 @@\n \n \n class BaseCombiningTrigger(BaseTrigger):\n-    __slots__ = ('triggers', 'jitter')\n+    __slots__ = (\"jitter\", \"triggers\")\n \n     def __init__(self, triggers, jitter=None):\n         self.triggers = triggers\n         self.jitter = jitter\n \n     def __getstate__(self):\n         return {\n-            'version': 1,\n-            'triggers': [(obj_to_ref(trigger.__class__), trigger.__getstate__())\n-                         for trigger in self.triggers],\n-            'jitter': self.jitter\n+            \"version\": 1,\n+            \"triggers\": [\n+                (obj_to_ref(trigger.__class__), trigger.__getstate__())\n+                for trigger in self.triggers\n+            ],\n+            \"jitter\": self.jitter,\n         }\n \n     def __setstate__(self, state):\n-        if state.get('version', 1) > 1:\n+        if state.get(\"version\", 1) > 1:\n             raise ValueError(\n-                'Got serialized data for version %s of %s, but only versions up to 1 can be '\n-                'handled' % (state['version'], self.__class__.__name__))\n+                f\"Got serialized data for version {state['version']} of \"\n+                f\"{self.__class__.__name__}, but only versions up to 1 can be handled\"\n+            )\n \n-        self.jitter = state['jitter']\n+        self.jitter = state[\"jitter\"]\n         self.triggers = []\n-        for clsref, state in state['triggers']:\n+        for clsref, state in state[\"triggers\"]:\n             cls = ref_to_obj(clsref)\n             trigger = cls.__new__(cls)\n             trigger.__setstate__(state)\n             self.triggers.append(trigger)\n \n     def __repr__(self):\n-        return '<{}({}{})>'.format(self.__class__.__name__, self.triggers,\n-                                   ', jitter={}'.format(self.jitter) if self.jitter else '')\n+        return \"<{}({}{})>\".format(\n+            self.__class__.__name__,\n+            self.triggers,\n+            f\", jitter={self.jitter}\" if self.jitter else \"\",\n+        )\n \n \n class AndTrigger(BaseCombiningTrigger):\n@@ -44,6 +50,15 @@ class AndTrigger(BaseCombiningTrigger):\n \n     Trigger alias: ``and``\n \n+    .. warning:: This trigger should only be used to combine triggers that fire on\n+        specific times of day, such as\n+        :class:`~apscheduler.triggers.cron.CronTrigger` and\n+        class:`~apscheduler.triggers.calendarinterval.CalendarIntervalTrigger`.\n+        Attempting to use it with\n+        :class:`~apscheduler.triggers.interval.IntervalTrigger` will likely result in\n+        the scheduler hanging as it tries to find a fire time that matches exactly\n+        between fire times produced by all the given triggers.\n+\n     :param list triggers: triggers to combine\n     :param int|None jitter: delay the job execution by ``jitter`` seconds at most\n     \"\"\"\n@@ -52,8 +67,10 @@ class AndTrigger(BaseCombiningTrigger):\n \n     def get_next_fire_time(self, previous_fire_time, now):\n         while True:\n-            fire_times = [trigger.get_next_fire_time(previous_fire_time, now)\n-                          for trigger in self.triggers]\n+            fire_times = [\n+                trigger.get_next_fire_time(previous_fire_time, now)\n+                for trigger in self.triggers\n+            ]\n             if None in fire_times:\n                 return None\n             elif min(fire_times) == max(fire_times):\n@@ -62,7 +79,7 @@ def get_next_fire_time(self, previous_fire_time, now):\n                 now = max(fire_times)\n \n     def __str__(self):\n-        return 'and[{}]'.format(', '.join(str(trigger) for trigger in self.triggers))\n+        return \"and[{}]\".format(\", \".join(str(trigger) for trigger in self.triggers))\n \n \n class OrTrigger(BaseCombiningTrigger):\n@@ -83,13 +100,15 @@ class OrTrigger(BaseCombiningTrigger):\n     __slots__ = ()\n \n     def get_next_fire_time(self, previous_fire_time, now):\n-        fire_times = [trigger.get_next_fire_time(previous_fire_time, now)\n-                      for trigger in self.triggers]\n+        fire_times = [\n+            trigger.get_next_fire_time(previous_fire_time, now)\n+            for trigger in self.triggers\n+        ]\n         fire_times = [fire_time for fire_time in fire_times if fire_time is not None]\n         if fire_times:\n             return self._apply_jitter(min(fire_times), self.jitter, now)\n         else:\n             return None\n \n     def __str__(self):\n-        return 'or[{}]'.format(', '.join(str(trigger) for trigger in self.triggers))\n+        return \"or[{}]\".format(\", \".join(str(trigger) for trigger in self.triggers))"}, {"sha": "19219b8da041a7a77bd28b29c6d889a4aa013242", "filename": "lib/apscheduler/triggers/cron/__init__.py", "status": "modified", "additions": 119, "deletions": 57, "changes": 176, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcron%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcron%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Ftriggers%2Fcron%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,13 +1,25 @@\n-from datetime import datetime, timedelta\n+from datetime import datetime, timedelta, timezone\n \n from tzlocal import get_localzone\n-import six\n \n from apscheduler.triggers.base import BaseTrigger\n from apscheduler.triggers.cron.fields import (\n-    BaseField, MonthField, WeekField, DayOfMonthField, DayOfWeekField, DEFAULT_VALUES)\n+    DEFAULT_VALUES,\n+    BaseField,\n+    DayOfMonthField,\n+    DayOfWeekField,\n+    MonthField,\n+    WeekField,\n+)\n from apscheduler.util import (\n-    datetime_ceil, convert_to_datetime, datetime_repr, astimezone, localize, normalize)\n+    astimezone,\n+    convert_to_datetime,\n+    datetime_ceil,\n+    datetime_repr,\n+    datetime_utc_add,\n+)\n+\n+UTC = timezone.utc\n \n \n class CronTrigger(BaseTrigger):\n@@ -32,39 +44,63 @@ class CronTrigger(BaseTrigger):\n     .. note:: The first weekday is always **monday**.\n     \"\"\"\n \n-    FIELD_NAMES = ('year', 'month', 'day', 'week', 'day_of_week', 'hour', 'minute', 'second')\n+    FIELD_NAMES = (\n+        \"year\",\n+        \"month\",\n+        \"day\",\n+        \"week\",\n+        \"day_of_week\",\n+        \"hour\",\n+        \"minute\",\n+        \"second\",\n+    )\n     FIELDS_MAP = {\n-        'year': BaseField,\n-        'month': MonthField,\n-        'week': WeekField,\n-        'day': DayOfMonthField,\n-        'day_of_week': DayOfWeekField,\n-        'hour': BaseField,\n-        'minute': BaseField,\n-        'second': BaseField\n+        \"year\": BaseField,\n+        \"month\": MonthField,\n+        \"week\": WeekField,\n+        \"day\": DayOfMonthField,\n+        \"day_of_week\": DayOfWeekField,\n+        \"hour\": BaseField,\n+        \"minute\": BaseField,\n+        \"second\": BaseField,\n     }\n \n-    __slots__ = 'timezone', 'start_date', 'end_date', 'fields', 'jitter'\n-\n-    def __init__(self, year=None, month=None, day=None, week=None, day_of_week=None, hour=None,\n-                 minute=None, second=None, start_date=None, end_date=None, timezone=None,\n-                 jitter=None):\n+    __slots__ = \"end_date\", \"fields\", \"jitter\", \"start_date\", \"timezone\"\n+\n+    def __init__(\n+        self,\n+        year=None,\n+        month=None,\n+        day=None,\n+        week=None,\n+        day_of_week=None,\n+        hour=None,\n+        minute=None,\n+        second=None,\n+        start_date=None,\n+        end_date=None,\n+        timezone=None,\n+        jitter=None,\n+    ):\n         if timezone:\n             self.timezone = astimezone(timezone)\n         elif isinstance(start_date, datetime) and start_date.tzinfo:\n-            self.timezone = start_date.tzinfo\n+            self.timezone = astimezone(start_date.tzinfo)\n         elif isinstance(end_date, datetime) and end_date.tzinfo:\n-            self.timezone = end_date.tzinfo\n+            self.timezone = astimezone(end_date.tzinfo)\n         else:\n             self.timezone = get_localzone()\n \n-        self.start_date = convert_to_datetime(start_date, self.timezone, 'start_date')\n-        self.end_date = convert_to_datetime(end_date, self.timezone, 'end_date')\n+        self.start_date = convert_to_datetime(start_date, self.timezone, \"start_date\")\n+        self.end_date = convert_to_datetime(end_date, self.timezone, \"end_date\")\n \n         self.jitter = jitter\n \n-        values = dict((key, value) for (key, value) in six.iteritems(locals())\n-                      if key in self.FIELD_NAMES and value is not None)\n+        values = dict(\n+            (key, value)\n+            for (key, value) in locals().items()\n+            if key in self.FIELD_NAMES and value is not None\n+        )\n         self.fields = []\n         assign_defaults = False\n         for field_name in self.FIELD_NAMES:\n@@ -76,7 +112,7 @@ def __init__(self, year=None, month=None, day=None, week=None, day_of_week=None,\n                 exprs = DEFAULT_VALUES[field_name]\n                 is_default = True\n             else:\n-                exprs = '*'\n+                exprs = \"*\"\n                 is_default = True\n \n             field_class = self.FIELDS_MAP[field_name]\n@@ -98,10 +134,16 @@ def from_crontab(cls, expr, timezone=None):\n         \"\"\"\n         values = expr.split()\n         if len(values) != 5:\n-            raise ValueError('Wrong number of fields; got {}, expected 5'.format(len(values)))\n+            raise ValueError(f\"Wrong number of fields; got {len(values)}, expected 5\")\n \n-        return cls(minute=values[0], hour=values[1], day=values[2], month=values[3],\n-                   day_of_week=values[4], timezone=timezone)\n+        return cls(\n+            minute=values[0],\n+            hour=values[1],\n+            day=values[2],\n+            month=values[3],\n+            day_of_week=values[4],\n+            timezone=timezone,\n+        )\n \n     def _increment_field_value(self, dateval, fieldnum):\n         \"\"\"\n@@ -144,7 +186,8 @@ def _increment_field_value(self, dateval, fieldnum):\n                     i += 1\n \n         difference = datetime(**values) - dateval.replace(tzinfo=None)\n-        return normalize(dateval + difference), fieldnum\n+        dateval = datetime_utc_add(dateval, difference)\n+        return dateval, fieldnum\n \n     def _set_field_value(self, dateval, fieldnum, new_value):\n         values = {}\n@@ -157,15 +200,26 @@ def _set_field_value(self, dateval, fieldnum, new_value):\n                 else:\n                     values[field.name] = new_value\n \n-        return localize(datetime(**values), self.timezone)\n+        return datetime(**values, tzinfo=self.timezone, fold=dateval.fold)\n \n     def get_next_fire_time(self, previous_fire_time, now):\n         if previous_fire_time:\n-            start_date = min(now, previous_fire_time + timedelta(microseconds=1))\n+            start_date = min(\n+                now.astimezone(UTC),\n+                datetime_utc_add(\n+                    previous_fire_time, timedelta(microseconds=1)\n+                ).astimezone(UTC),\n+            ).astimezone(self.timezone)\n             if start_date == previous_fire_time:\n-                start_date += timedelta(microseconds=1)\n+                start_date = datetime_utc_add(start_date, timedelta(microseconds=1))\n         else:\n-            start_date = max(now, self.start_date) if self.start_date else now\n+            start_date = (\n+                max(now.astimezone(UTC), self.start_date.astimezone(UTC)).astimezone(\n+                    self.timezone\n+                )\n+                if self.start_date\n+                else now\n+            )\n \n         fieldnum = 0\n         next_date = datetime_ceil(start_date).astimezone(self.timezone)\n@@ -176,14 +230,18 @@ def get_next_fire_time(self, previous_fire_time, now):\n \n             if next_value is None:\n                 # No valid value was found\n-                next_date, fieldnum = self._increment_field_value(next_date, fieldnum - 1)\n+                next_date, fieldnum = self._increment_field_value(\n+                    next_date, fieldnum - 1\n+                )\n             elif next_value > curr_value:\n                 # A valid, but higher than the starting value, was found\n                 if field.REAL:\n                     next_date = self._set_field_value(next_date, fieldnum, next_value)\n                     fieldnum += 1\n                 else:\n-                    next_date, fieldnum = self._increment_field_value(next_date, fieldnum)\n+                    next_date, fieldnum = self._increment_field_value(\n+                        next_date, fieldnum\n+                    )\n             else:\n                 # A valid value was found, no changes necessary\n                 fieldnum += 1\n@@ -198,42 +256,46 @@ def get_next_fire_time(self, previous_fire_time, now):\n \n     def __getstate__(self):\n         return {\n-            'version': 2,\n-            'timezone': self.timezone,\n-            'start_date': self.start_date,\n-            'end_date': self.end_date,\n-            'fields': self.fields,\n-            'jitter': self.jitter,\n+            \"version\": 2,\n+            \"timezone\": self.timezone,\n+            \"start_date\": self.start_date,\n+            \"end_date\": self.end_date,\n+            \"fields\": self.fields,\n+            \"jitter\": self.jitter,\n         }\n \n     def __setstate__(self, state):\n         # This is for compatibility with APScheduler 3.0.x\n         if isinstance(state, tuple):\n             state = state[1]\n \n-        if state.get('version', 1) > 2:\n+        if state.get(\"version\", 1) > 2:\n             raise ValueError(\n-                'Got serialized data for version %s of %s, but only versions up to 2 can be '\n-                'handled' % (state['version'], self.__class__.__name__))\n+                f\"Got serialized data for version {state['version']} of \"\n+                f\"{self.__class__.__name__}, but only versions up to 2 can be handled\"\n+            )\n \n-        self.timezone = state['timezone']\n-        self.start_date = state['start_date']\n-        self.end_date = state['end_date']\n-        self.fields = state['fields']\n-        self.jitter = state.get('jitter')\n+        self.timezone = astimezone(state[\"timezone\"])\n+        self.start_date = state[\"start_date\"]\n+        self.end_date = state[\"end_date\"]\n+        self.fields = state[\"fields\"]\n+        self.jitter = state.get(\"jitter\")\n \n     def __str__(self):\n-        options = [\"%s='%s'\" % (f.name, f) for f in self.fields if not f.is_default]\n-        return 'cron[%s]' % (', '.join(options))\n+        options = [f\"{f.name}='{f}'\" for f in self.fields if not f.is_default]\n+        return \"cron[{}]\".format(\", \".join(options))\n \n     def __repr__(self):\n-        options = [\"%s='%s'\" % (f.name, f) for f in self.fields if not f.is_default]\n+        options = [f\"{f.name}='{f}'\" for f in self.fields if not f.is_default]\n         if self.start_date:\n-            options.append(\"start_date=%r\" % datetime_repr(self.start_date))\n+            options.append(f\"start_date={datetime_repr(self.start_date)!r}\")\n         if self.end_date:\n-            options.append(\"end_date=%r\" % datetime_repr(self.end_date))\n+            options.append(f\"end_date={datetime_repr(self.end_date)!r}\")\n         if self.jitter:\n-            options.append('jitter=%s' % self.jitter)\n+            options.append(f\"jitter={self.jitter}\")\n \n-        return \"<%s (%s, timezone='%s')>\" % (\n-            self.__class__.__name__, ', '.join(options), self.timezone)\n+        return \"<{} ({}, timezone='{}')>\".format(\n+            self.__class__.__name__,\n+            \", \".join(options),\n+            self.timezone,\n+        )"}, {"sha": "74044da1f53da629260dc16ce6b2cc5f548bdf49", "filename": "lib/apscheduler/triggers/cron/expressions.py", "status": "modified", "additions": 101, "deletions": 67, "changes": 168, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcron%2Fexpressions.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcron%2Fexpressions.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Ftriggers%2Fcron%2Fexpressions.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,33 +1,52 @@\n \"\"\"This module contains the expressions applicable for CronTrigger's fields.\"\"\"\n \n-from calendar import monthrange\n+__all__ = (\n+    \"AllExpression\",\n+    \"LastDayOfMonthExpression\",\n+    \"RangeExpression\",\n+    \"WeekdayPositionExpression\",\n+    \"WeekdayRangeExpression\",\n+)\n+\n import re\n+from calendar import monthrange\n \n from apscheduler.util import asint\n \n-__all__ = ('AllExpression', 'RangeExpression', 'WeekdayRangeExpression',\n-           'WeekdayPositionExpression', 'LastDayOfMonthExpression')\n-\n-\n-WEEKDAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']\n-MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']\n-\n-\n-class AllExpression(object):\n-    value_re = re.compile(r'\\*(?:/(?P<step>\\d+))?$')\n+WEEKDAYS = [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"]\n+MONTHS = [\n+    \"jan\",\n+    \"feb\",\n+    \"mar\",\n+    \"apr\",\n+    \"may\",\n+    \"jun\",\n+    \"jul\",\n+    \"aug\",\n+    \"sep\",\n+    \"oct\",\n+    \"nov\",\n+    \"dec\",\n+]\n+\n+\n+class AllExpression:\n+    value_re = re.compile(r\"\\*(?:/(?P<step>\\d+))?$\")\n \n     def __init__(self, step=None):\n         self.step = asint(step)\n         if self.step == 0:\n-            raise ValueError('Increment must be higher than 0')\n+            raise ValueError(\"Increment must be higher than 0\")\n \n     def validate_range(self, field_name):\n-        from apscheduler.triggers.cron.fields import MIN_VALUES, MAX_VALUES\n+        from apscheduler.triggers.cron.fields import MAX_VALUES, MIN_VALUES\n \n         value_range = MAX_VALUES[field_name] - MIN_VALUES[field_name]\n         if self.step and self.step > value_range:\n-            raise ValueError('the step value ({}) is higher than the total range of the '\n-                             'expression ({})'.format(self.step, value_range))\n+            raise ValueError(\n+                f\"the step value ({self.step}) is higher than the total range of the \"\n+                f\"expression ({value_range})\"\n+            )\n \n     def get_next_value(self, date, field):\n         start = field.get_value(date)\n@@ -49,42 +68,47 @@ def __eq__(self, other):\n \n     def __str__(self):\n         if self.step:\n-            return '*/%d' % self.step\n-        return '*'\n+            return f\"*/{self.step}\"\n+        return \"*\"\n \n     def __repr__(self):\n-        return \"%s(%s)\" % (self.__class__.__name__, self.step)\n+        return f\"{self.__class__.__name__}({self.step})\"\n \n \n class RangeExpression(AllExpression):\n-    value_re = re.compile(\n-        r'(?P<first>\\d+)(?:-(?P<last>\\d+))?(?:/(?P<step>\\d+))?$')\n+    value_re = re.compile(r\"(?P<first>\\d+)(?:-(?P<last>\\d+))?(?:/(?P<step>\\d+))?$\")\n \n     def __init__(self, first, last=None, step=None):\n-        super(RangeExpression, self).__init__(step)\n+        super().__init__(step)\n         first = asint(first)\n         last = asint(last)\n         if last is None and step is None:\n             last = first\n         if last is not None and first > last:\n-            raise ValueError('The minimum value in a range must not be higher than the maximum')\n+            raise ValueError(\n+                \"The minimum value in a range must not be higher than the maximum\"\n+            )\n         self.first = first\n         self.last = last\n \n     def validate_range(self, field_name):\n-        from apscheduler.triggers.cron.fields import MIN_VALUES, MAX_VALUES\n+        from apscheduler.triggers.cron.fields import MAX_VALUES, MIN_VALUES\n \n-        super(RangeExpression, self).validate_range(field_name)\n+        super().validate_range(field_name)\n         if self.first < MIN_VALUES[field_name]:\n-            raise ValueError('the first value ({}) is lower than the minimum value ({})'\n-                             .format(self.first, MIN_VALUES[field_name]))\n+            raise ValueError(\n+                f\"the first value ({self.first}) is lower than the minimum value ({MIN_VALUES[field_name]})\"\n+            )\n         if self.last is not None and self.last > MAX_VALUES[field_name]:\n-            raise ValueError('the last value ({}) is higher than the maximum value ({})'\n-                             .format(self.last, MAX_VALUES[field_name]))\n+            raise ValueError(\n+                f\"the last value ({self.last}) is higher than the maximum value ({MAX_VALUES[field_name]})\"\n+            )\n         value_range = (self.last or MAX_VALUES[field_name]) - self.first\n         if self.step and self.step > value_range:\n-            raise ValueError('the step value ({}) is higher than the total range of the '\n-                             'expression ({})'.format(self.step, value_range))\n+            raise ValueError(\n+                f\"the step value ({self.step}) is higher than the total range of the \"\n+                f\"expression ({value_range})\"\n+            )\n \n     def get_next_value(self, date, field):\n         startval = field.get_value(date)\n@@ -104,106 +128,114 @@ def get_next_value(self, date, field):\n         return nextval if nextval <= maxval else None\n \n     def __eq__(self, other):\n-        return (isinstance(other, self.__class__) and self.first == other.first and\n-                self.last == other.last)\n+        return (\n+            isinstance(other, self.__class__)\n+            and self.first == other.first\n+            and self.last == other.last\n+        )\n \n     def __str__(self):\n         if self.last != self.first and self.last is not None:\n-            range = '%d-%d' % (self.first, self.last)\n+            range = f\"{self.first}-{self.last}\"\n         else:\n             range = str(self.first)\n \n         if self.step:\n-            return '%s/%d' % (range, self.step)\n+            return f\"{range}/{self.step}\"\n+\n         return range\n \n     def __repr__(self):\n         args = [str(self.first)]\n-        if self.last != self.first and self.last is not None or self.step:\n+        if (self.last != self.first and self.last is not None) or self.step:\n             args.append(str(self.last))\n+\n         if self.step:\n             args.append(str(self.step))\n-        return \"%s(%s)\" % (self.__class__.__name__, ', '.join(args))\n+\n+        return \"{}({})\".format(self.__class__.__name__, \", \".join(args))\n \n \n class MonthRangeExpression(RangeExpression):\n-    value_re = re.compile(r'(?P<first>[a-z]+)(?:-(?P<last>[a-z]+))?', re.IGNORECASE)\n+    value_re = re.compile(r\"(?P<first>[a-z]+)(?:-(?P<last>[a-z]+))?\", re.IGNORECASE)\n \n     def __init__(self, first, last=None):\n         try:\n             first_num = MONTHS.index(first.lower()) + 1\n         except ValueError:\n-            raise ValueError('Invalid month name \"%s\"' % first)\n+            raise ValueError(f'Invalid month name \"{first}\"')\n \n         if last:\n             try:\n                 last_num = MONTHS.index(last.lower()) + 1\n             except ValueError:\n-                raise ValueError('Invalid month name \"%s\"' % last)\n+                raise ValueError(f'Invalid month name \"{last}\"')\n         else:\n             last_num = None\n \n-        super(MonthRangeExpression, self).__init__(first_num, last_num)\n+        super().__init__(first_num, last_num)\n \n     def __str__(self):\n         if self.last != self.first and self.last is not None:\n-            return '%s-%s' % (MONTHS[self.first - 1], MONTHS[self.last - 1])\n+            return f\"{MONTHS[self.first - 1]}-{MONTHS[self.last - 1]}\"\n         return MONTHS[self.first - 1]\n \n     def __repr__(self):\n-        args = [\"'%s'\" % MONTHS[self.first]]\n+        args = [f\"'{MONTHS[self.first]}'\"]\n         if self.last != self.first and self.last is not None:\n-            args.append(\"'%s'\" % MONTHS[self.last - 1])\n-        return \"%s(%s)\" % (self.__class__.__name__, ', '.join(args))\n+            args.append(f\"'{MONTHS[self.last - 1]}'\")\n+        return \"{}({})\".format(self.__class__.__name__, \", \".join(args))\n \n \n class WeekdayRangeExpression(RangeExpression):\n-    value_re = re.compile(r'(?P<first>[a-z]+)(?:-(?P<last>[a-z]+))?', re.IGNORECASE)\n+    value_re = re.compile(r\"(?P<first>[a-z]+)(?:-(?P<last>[a-z]+))?\", re.IGNORECASE)\n \n     def __init__(self, first, last=None):\n         try:\n             first_num = WEEKDAYS.index(first.lower())\n         except ValueError:\n-            raise ValueError('Invalid weekday name \"%s\"' % first)\n+            raise ValueError(f'Invalid weekday name \"{first}\"')\n \n         if last:\n             try:\n                 last_num = WEEKDAYS.index(last.lower())\n             except ValueError:\n-                raise ValueError('Invalid weekday name \"%s\"' % last)\n+                raise ValueError(f'Invalid weekday name \"{last}\"')\n         else:\n             last_num = None\n \n-        super(WeekdayRangeExpression, self).__init__(first_num, last_num)\n+        super().__init__(first_num, last_num)\n \n     def __str__(self):\n         if self.last != self.first and self.last is not None:\n-            return '%s-%s' % (WEEKDAYS[self.first], WEEKDAYS[self.last])\n+            return f\"{WEEKDAYS[self.first]}-{WEEKDAYS[self.last]}\"\n         return WEEKDAYS[self.first]\n \n     def __repr__(self):\n-        args = [\"'%s'\" % WEEKDAYS[self.first]]\n+        args = [f\"'{WEEKDAYS[self.first]}'\"]\n         if self.last != self.first and self.last is not None:\n-            args.append(\"'%s'\" % WEEKDAYS[self.last])\n-        return \"%s(%s)\" % (self.__class__.__name__, ', '.join(args))\n+            args.append(f\"'{WEEKDAYS[self.last]}'\")\n+        return \"{}({})\".format(self.__class__.__name__, \", \".join(args))\n \n \n class WeekdayPositionExpression(AllExpression):\n-    options = ['1st', '2nd', '3rd', '4th', '5th', 'last']\n-    value_re = re.compile(r'(?P<option_name>%s) +(?P<weekday_name>(?:\\d+|\\w+))' %\n-                          '|'.join(options), re.IGNORECASE)\n+    options = [\"1st\", \"2nd\", \"3rd\", \"4th\", \"5th\", \"last\"]\n+    value_re = re.compile(\n+        r\"(?P<option_name>{}) +(?P<weekday_name>(?:\\d+|\\w+))\".format(\"|\".join(options)),\n+        re.IGNORECASE,\n+    )\n \n     def __init__(self, option_name, weekday_name):\n-        super(WeekdayPositionExpression, self).__init__(None)\n+        super().__init__(None)\n         try:\n             self.option_num = self.options.index(option_name.lower())\n         except ValueError:\n-            raise ValueError('Invalid weekday position \"%s\"' % option_name)\n+            raise ValueError(f'Invalid weekday position \"{option_name}\"')\n \n         try:\n             self.weekday = WEEKDAYS.index(weekday_name.lower())\n         except ValueError:\n-            raise ValueError('Invalid weekday name \"%s\"' % weekday_name)\n+            raise ValueError(f'Invalid weekday name \"{weekday_name}\"')\n \n     def get_next_value(self, date, field):\n         # Figure out the weekday of the month's first day and the number of days in that month\n@@ -224,28 +256,30 @@ def get_next_value(self, date, field):\n             return target_day\n \n     def __eq__(self, other):\n-        return (super(WeekdayPositionExpression, self).__eq__(other) and\n-                self.option_num == other.option_num and self.weekday == other.weekday)\n+        return (\n+            super().__eq__(other)\n+            and self.option_num == other.option_num\n+            and self.weekday == other.weekday\n+        )\n \n     def __str__(self):\n-        return '%s %s' % (self.options[self.option_num], WEEKDAYS[self.weekday])\n+        return f\"{self.options[self.option_num]} {WEEKDAYS[self.weekday]}\"\n \n     def __repr__(self):\n-        return \"%s('%s', '%s')\" % (self.__class__.__name__, self.options[self.option_num],\n-                                   WEEKDAYS[self.weekday])\n+        return f\"{self.__class__.__name__}('{self.options[self.option_num]}', '{WEEKDAYS[self.weekday]}')\"\n \n \n class LastDayOfMonthExpression(AllExpression):\n-    value_re = re.compile(r'last', re.IGNORECASE)\n+    value_re = re.compile(r\"last\", re.IGNORECASE)\n \n     def __init__(self):\n-        super(LastDayOfMonthExpression, self).__init__(None)\n+        super().__init__(None)\n \n     def get_next_value(self, date, field):\n         return monthrange(date.year, date.month)[1]\n \n     def __str__(self):\n-        return 'last'\n+        return \"last\"\n \n     def __repr__(self):\n-        return \"%s()\" % self.__class__.__name__\n+        return f\"{self.__class__.__name__}()\""}, {"sha": "2133fdb76763a0ef7471b94dfece589360eeaaea", "filename": "lib/apscheduler/triggers/cron/fields.py", "status": "modified", "additions": 66, "deletions": 28, "changes": 94, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcron%2Ffields.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fcron%2Ffields.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Ftriggers%2Fcron%2Ffields.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,29 +1,61 @@\n \"\"\"Fields represent CronTrigger options which map to :class:`~datetime.datetime` fields.\"\"\"\n \n-from calendar import monthrange\n-import re\n+__all__ = (\n+    \"DEFAULT_VALUES\",\n+    \"MAX_VALUES\",\n+    \"MIN_VALUES\",\n+    \"BaseField\",\n+    \"DayOfMonthField\",\n+    \"DayOfWeekField\",\n+    \"WeekField\",\n+)\n \n-import six\n+import re\n+from calendar import monthrange\n \n from apscheduler.triggers.cron.expressions import (\n-    AllExpression, RangeExpression, WeekdayPositionExpression, LastDayOfMonthExpression,\n-    WeekdayRangeExpression, MonthRangeExpression)\n-\n-\n-__all__ = ('MIN_VALUES', 'MAX_VALUES', 'DEFAULT_VALUES', 'BaseField', 'WeekField',\n-           'DayOfMonthField', 'DayOfWeekField')\n-\n-\n-MIN_VALUES = {'year': 1970, 'month': 1, 'day': 1, 'week': 1, 'day_of_week': 0, 'hour': 0,\n-              'minute': 0, 'second': 0}\n-MAX_VALUES = {'year': 9999, 'month': 12, 'day': 31, 'week': 53, 'day_of_week': 6, 'hour': 23,\n-              'minute': 59, 'second': 59}\n-DEFAULT_VALUES = {'year': '*', 'month': 1, 'day': 1, 'week': '*', 'day_of_week': '*', 'hour': 0,\n-                  'minute': 0, 'second': 0}\n-SEPARATOR = re.compile(' *, *')\n-\n-\n-class BaseField(object):\n+    AllExpression,\n+    LastDayOfMonthExpression,\n+    MonthRangeExpression,\n+    RangeExpression,\n+    WeekdayPositionExpression,\n+    WeekdayRangeExpression,\n+)\n+\n+MIN_VALUES = {\n+    \"year\": 1970,\n+    \"month\": 1,\n+    \"day\": 1,\n+    \"week\": 1,\n+    \"day_of_week\": 0,\n+    \"hour\": 0,\n+    \"minute\": 0,\n+    \"second\": 0,\n+}\n+MAX_VALUES = {\n+    \"year\": 9999,\n+    \"month\": 12,\n+    \"day\": 31,\n+    \"week\": 53,\n+    \"day_of_week\": 6,\n+    \"hour\": 23,\n+    \"minute\": 59,\n+    \"second\": 59,\n+}\n+DEFAULT_VALUES = {\n+    \"year\": \"*\",\n+    \"month\": 1,\n+    \"day\": 1,\n+    \"week\": \"*\",\n+    \"day_of_week\": \"*\",\n+    \"hour\": 0,\n+    \"minute\": 0,\n+    \"second\": 0,\n+}\n+SEPARATOR = re.compile(\" *, *\")\n+\n+\n+class BaseField:\n     REAL = True\n     COMPILERS = [AllExpression, RangeExpression]\n \n@@ -66,23 +98,26 @@ def compile_expression(self, expr):\n                 try:\n                     compiled_expr.validate_range(self.name)\n                 except ValueError as e:\n-                    exc = ValueError('Error validating expression {!r}: {}'.format(expr, e))\n-                    six.raise_from(exc, None)\n+                    raise ValueError(\n+                        f\"Error validating expression {expr!r}: {e}\"\n+                    ) from None\n \n                 self.expressions.append(compiled_expr)\n                 return\n \n-        raise ValueError('Unrecognized expression \"%s\" for field \"%s\"' % (expr, self.name))\n+        raise ValueError(f'Unrecognized expression \"{expr}\" for field \"{self.name}\"')\n \n     def __eq__(self, other):\n-        return isinstance(self, self.__class__) and self.expressions == other.expressions\n+        return (\n+            isinstance(self, self.__class__) and self.expressions == other.expressions\n+        )\n \n     def __str__(self):\n         expr_strings = (str(e) for e in self.expressions)\n-        return ','.join(expr_strings)\n+        return \",\".join(expr_strings)\n \n     def __repr__(self):\n-        return \"%s('%s', '%s')\" % (self.__class__.__name__, self.name, self)\n+        return f\"{self.__class__.__name__}('{self.name}', '{self}')\"\n \n \n class WeekField(BaseField):\n@@ -93,7 +128,10 @@ def get_value(self, dateval):\n \n \n class DayOfMonthField(BaseField):\n-    COMPILERS = BaseField.COMPILERS + [WeekdayPositionExpression, LastDayOfMonthExpression]\n+    COMPILERS = BaseField.COMPILERS + [\n+        WeekdayPositionExpression,\n+        LastDayOfMonthExpression,\n+    ]\n \n     def get_max(self, dateval):\n         return monthrange(dateval.year, dateval.month)[1]"}, {"sha": "a9302da5dbe9141fa5e680f2761dce5114ded627", "filename": "lib/apscheduler/triggers/date.py", "status": "modified", "additions": 13, "deletions": 13, "changes": 26, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fdate.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Fdate.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Ftriggers%2Fdate.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -3,7 +3,7 @@\n from tzlocal import get_localzone\n \n from apscheduler.triggers.base import BaseTrigger\n-from apscheduler.util import convert_to_datetime, datetime_repr, astimezone\n+from apscheduler.util import astimezone, convert_to_datetime, datetime_repr\n \n \n class DateTrigger(BaseTrigger):\n@@ -14,38 +14,38 @@ class DateTrigger(BaseTrigger):\n     :param datetime.tzinfo|str timezone: time zone for ``run_date`` if it doesn't have one already\n     \"\"\"\n \n-    __slots__ = 'run_date'\n+    __slots__ = \"run_date\"\n \n     def __init__(self, run_date=None, timezone=None):\n         timezone = astimezone(timezone) or get_localzone()\n         if run_date is not None:\n-            self.run_date = convert_to_datetime(run_date, timezone, 'run_date')\n+            self.run_date = convert_to_datetime(run_date, timezone, \"run_date\")\n         else:\n             self.run_date = datetime.now(timezone)\n \n     def get_next_fire_time(self, previous_fire_time, now):\n         return self.run_date if previous_fire_time is None else None\n \n     def __getstate__(self):\n-        return {\n-            'version': 1,\n-            'run_date': self.run_date\n-        }\n+        return {\"version\": 1, \"run_date\": self.run_date}\n \n     def __setstate__(self, state):\n         # This is for compatibility with APScheduler 3.0.x\n         if isinstance(state, tuple):\n             state = state[1]\n \n-        if state.get('version', 1) > 1:\n+        if state.get(\"version\", 1) > 1:\n             raise ValueError(\n-                'Got serialized data for version %s of %s, but only version 1 can be handled' %\n-                (state['version'], self.__class__.__name__))\n+                f\"Got serialized data for version {state['version']} of \"\n+                f\"{self.__class__.__name__}, but only version 1 can be handled\"\n+            )\n \n-        self.run_date = state['run_date']\n+        self.run_date = state[\"run_date\"]\n \n     def __str__(self):\n-        return 'date[%s]' % datetime_repr(self.run_date)\n+        return f\"date[{datetime_repr(self.run_date)}]\"\n \n     def __repr__(self):\n-        return \"<%s (run_date='%s')>\" % (self.__class__.__name__, datetime_repr(self.run_date))\n+        return (\n+            f\"<{self.__class__.__name__} (run_date='{datetime_repr(self.run_date)}')>\"\n+        )"}, {"sha": "0e4b24acf19efd3e8bc51d9bb28615a1ab08a692", "filename": "lib/apscheduler/triggers/interval.py", "status": "modified", "additions": 73, "deletions": 43, "changes": 116, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Finterval.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Ftriggers%2Finterval.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Ftriggers%2Finterval.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,12 +1,15 @@\n-from datetime import timedelta, datetime\n+import random\n+from datetime import datetime, timedelta\n from math import ceil\n \n from tzlocal import get_localzone\n \n from apscheduler.triggers.base import BaseTrigger\n from apscheduler.util import (\n-    convert_to_datetime, normalize, timedelta_seconds, datetime_repr,\n-    astimezone)\n+    astimezone,\n+    convert_to_datetime,\n+    datetime_repr,\n+)\n \n \n class IntervalTrigger(BaseTrigger):\n@@ -25,84 +28,111 @@ class IntervalTrigger(BaseTrigger):\n     :param int|None jitter: delay the job execution by ``jitter`` seconds at most\n     \"\"\"\n \n-    __slots__ = 'timezone', 'start_date', 'end_date', 'interval', 'interval_length', 'jitter'\n-\n-    def __init__(self, weeks=0, days=0, hours=0, minutes=0, seconds=0, start_date=None,\n-                 end_date=None, timezone=None, jitter=None):\n-        self.interval = timedelta(weeks=weeks, days=days, hours=hours, minutes=minutes,\n-                                  seconds=seconds)\n-        self.interval_length = timedelta_seconds(self.interval)\n+    __slots__ = (\n+        \"end_date\",\n+        \"interval\",\n+        \"interval_length\",\n+        \"jitter\",\n+        \"start_date\",\n+        \"timezone\",\n+    )\n+\n+    def __init__(\n+        self,\n+        weeks=0,\n+        days=0,\n+        hours=0,\n+        minutes=0,\n+        seconds=0,\n+        start_date=None,\n+        end_date=None,\n+        timezone=None,\n+        jitter=None,\n+    ):\n+        self.interval = timedelta(\n+            weeks=weeks, days=days, hours=hours, minutes=minutes, seconds=seconds\n+        )\n+        self.interval_length = self.interval.total_seconds()\n         if self.interval_length == 0:\n             self.interval = timedelta(seconds=1)\n             self.interval_length = 1\n \n         if timezone:\n             self.timezone = astimezone(timezone)\n         elif isinstance(start_date, datetime) and start_date.tzinfo:\n-            self.timezone = start_date.tzinfo\n+            self.timezone = astimezone(start_date.tzinfo)\n         elif isinstance(end_date, datetime) and end_date.tzinfo:\n-            self.timezone = end_date.tzinfo\n+            self.timezone = astimezone(end_date.tzinfo)\n         else:\n             self.timezone = get_localzone()\n \n         start_date = start_date or (datetime.now(self.timezone) + self.interval)\n-        self.start_date = convert_to_datetime(start_date, self.timezone, 'start_date')\n-        self.end_date = convert_to_datetime(end_date, self.timezone, 'end_date')\n+        self.start_date = convert_to_datetime(start_date, self.timezone, \"start_date\")\n+        self.end_date = convert_to_datetime(end_date, self.timezone, \"end_date\")\n \n         self.jitter = jitter\n \n     def get_next_fire_time(self, previous_fire_time, now):\n         if previous_fire_time:\n-            next_fire_time = previous_fire_time + self.interval\n+            next_fire_time = previous_fire_time.timestamp() + self.interval_length\n         elif self.start_date > now:\n-            next_fire_time = self.start_date\n+            next_fire_time = self.start_date.timestamp()\n         else:\n-            timediff_seconds = timedelta_seconds(now - self.start_date)\n-            next_interval_num = int(ceil(timediff_seconds / self.interval_length))\n-            next_fire_time = self.start_date + self.interval * next_interval_num\n+            timediff = now.timestamp() - self.start_date.timestamp()\n+            next_interval_num = ceil(timediff / self.interval_length)\n+            next_fire_time = (\n+                self.start_date.timestamp() + self.interval_length * next_interval_num\n+            )\n \n         if self.jitter is not None:\n-            next_fire_time = self._apply_jitter(next_fire_time, self.jitter, now)\n+            next_fire_time += random.uniform(0, self.jitter)\n \n-        if not self.end_date or next_fire_time <= self.end_date:\n-            return normalize(next_fire_time)\n+        if not self.end_date or next_fire_time <= self.end_date.timestamp():\n+            return datetime.fromtimestamp(next_fire_time, tz=self.timezone)\n \n     def __getstate__(self):\n         return {\n-            'version': 2,\n-            'timezone': self.timezone,\n-            'start_date': self.start_date,\n-            'end_date': self.end_date,\n-            'interval': self.interval,\n-            'jitter': self.jitter,\n+            \"version\": 2,\n+            \"timezone\": astimezone(self.timezone),\n+            \"start_date\": self.start_date,\n+            \"end_date\": self.end_date,\n+            \"interval\": self.interval,\n+            \"jitter\": self.jitter,\n         }\n \n     def __setstate__(self, state):\n         # This is for compatibility with APScheduler 3.0.x\n         if isinstance(state, tuple):\n             state = state[1]\n \n-        if state.get('version', 1) > 2:\n+        if state.get(\"version\", 1) > 2:\n             raise ValueError(\n-                'Got serialized data for version %s of %s, but only versions up to 2 can be '\n-                'handled' % (state['version'], self.__class__.__name__))\n+                f\"Got serialized data for version {state['version']} of \"\n+                f\"{self.__class__.__name__}, but only versions up to 2 can be handled\"\n+            )\n \n-        self.timezone = state['timezone']\n-        self.start_date = state['start_date']\n-        self.end_date = state['end_date']\n-        self.interval = state['interval']\n-        self.interval_length = timedelta_seconds(self.interval)\n-        self.jitter = state.get('jitter')\n+        self.timezone = state[\"timezone\"]\n+        self.start_date = state[\"start_date\"]\n+        self.end_date = state[\"end_date\"]\n+        self.interval = state[\"interval\"]\n+        self.interval_length = self.interval.total_seconds()\n+        self.jitter = state.get(\"jitter\")\n \n     def __str__(self):\n-        return 'interval[%s]' % str(self.interval)\n+        return f\"interval[{self.interval!s}]\"\n \n     def __repr__(self):\n-        options = ['interval=%r' % self.interval, 'start_date=%r' % datetime_repr(self.start_date)]\n+        options = [\n+            f\"interval={self.interval!r}\",\n+            f\"start_date={datetime_repr(self.start_date)!r}\",\n+        ]\n         if self.end_date:\n-            options.append(\"end_date=%r\" % datetime_repr(self.end_date))\n+            options.append(f\"end_date={datetime_repr(self.end_date)!r}\")\n         if self.jitter:\n-            options.append('jitter=%s' % self.jitter)\n+            options.append(f\"jitter={self.jitter}\")\n \n-        return \"<%s (%s, timezone='%s')>\" % (\n-            self.__class__.__name__, ', '.join(options), self.timezone)\n+        return \"<{} ({}, timezone='{}')>\".format(\n+            self.__class__.__name__,\n+            \", \".join(options),\n+            self.timezone,\n+        )"}, {"sha": "caf56b87fc1cc71acc4118ec9a52fa612e1c6444", "filename": "lib/apscheduler/util.py", "status": "modified", "additions": 191, "deletions": 136, "changes": 327, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Futil.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fapscheduler%2Futil.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fapscheduler%2Futil.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,46 +1,57 @@\n \"\"\"This module contains several handy functions primarily meant for internal use.\"\"\"\n \n-from __future__ import division\n+__all__ = (\n+    \"asbool\",\n+    \"asint\",\n+    \"astimezone\",\n+    \"check_callable_args\",\n+    \"convert_to_datetime\",\n+    \"datetime_ceil\",\n+    \"datetime_to_utc_timestamp\",\n+    \"get_callable_name\",\n+    \"localize\",\n+    \"maybe_ref\",\n+    \"normalize\",\n+    \"obj_to_ref\",\n+    \"ref_to_obj\",\n+    \"undefined\",\n+    \"utc_timestamp_to_datetime\",\n+)\n \n-from asyncio import iscoroutinefunction\n-from datetime import date, datetime, time, timedelta, tzinfo\n-from calendar import timegm\n-from functools import partial\n-from inspect import isclass, ismethod\n import re\n import sys\n+from calendar import timegm\n+from datetime import date, datetime, time, timedelta, timezone, tzinfo\n+from functools import partial\n+from inspect import isbuiltin, isclass, isfunction, ismethod, signature\n \n-from pytz import timezone, utc, FixedOffset\n-import six\n-\n-try:\n-    from inspect import signature\n-except ImportError:  # pragma: nocover\n-    from funcsigs import signature\n+if sys.version_info < (3, 14):\n+    from asyncio import iscoroutinefunction\n+else:\n+    from inspect import iscoroutinefunction\n \n-try:\n-    from threading import TIMEOUT_MAX\n-except ImportError:\n-    TIMEOUT_MAX = 4294967  # Maximum value accepted by Event.wait() on Windows\n+if sys.version_info < (3, 9):\n+    from backports.zoneinfo import ZoneInfo\n+else:\n+    from zoneinfo import ZoneInfo\n \n-__all__ = ('asint', 'asbool', 'astimezone', 'convert_to_datetime', 'datetime_to_utc_timestamp',\n-           'utc_timestamp_to_datetime', 'timedelta_seconds', 'datetime_ceil', 'get_callable_name',\n-           'obj_to_ref', 'ref_to_obj', 'maybe_ref', 'repr_escape', 'check_callable_args',\n-           'normalize', 'localize', 'TIMEOUT_MAX')\n+UTC = timezone.utc\n \n \n-class _Undefined(object):\n+class _Undefined:\n     def __nonzero__(self):\n         return False\n \n     def __bool__(self):\n         return False\n \n     def __repr__(self):\n-        return '<undefined>'\n+        return \"<undefined>\"\n \n \n-undefined = _Undefined()  #: a unique object that only signifies that no value is defined\n+undefined = (\n+    _Undefined()\n+)  #: a unique object that only signifies that no value is defined\n \n \n def asint(text):\n@@ -64,11 +75,14 @@ def asbool(obj):\n     \"\"\"\n     if isinstance(obj, str):\n         obj = obj.strip().lower()\n-        if obj in ('true', 'yes', 'on', 'y', 't', '1'):\n+        if obj in (\"true\", \"yes\", \"on\", \"y\", \"t\", \"1\"):\n             return True\n-        if obj in ('false', 'no', 'off', 'n', 'f', '0'):\n+\n+        if obj in (\"false\", \"no\", \"off\", \"n\", \"f\", \"0\"):\n             return False\n-        raise ValueError('Unable to interpret value \"%s\" as boolean' % obj)\n+\n+        raise ValueError(f'Unable to interpret value \"{obj}\" as boolean')\n+\n     return bool(obj)\n \n \n@@ -79,25 +93,49 @@ def astimezone(obj):\n     :rtype: tzinfo\n \n     \"\"\"\n-    if isinstance(obj, six.string_types):\n-        return timezone(obj)\n+    if isinstance(obj, str):\n+        if obj == \"UTC\":\n+            return timezone.utc\n+\n+        return ZoneInfo(obj)\n+\n     if isinstance(obj, tzinfo):\n-        if obj.tzname(None) == 'local':\n+        if obj.tzname(None) == \"local\":\n             raise ValueError(\n-                'Unable to determine the name of the local timezone -- you must explicitly '\n-                'specify the name of the local timezone. Please refrain from using timezones like '\n-                'EST to prevent problems with daylight saving time. Instead, use a locale based '\n-                'timezone name (such as Europe/Helsinki).')\n+                \"Unable to determine the name of the local timezone -- you must \"\n+                \"explicitly specify the name of the local timezone. Please refrain \"\n+                \"from using timezones like EST to prevent problems with daylight \"\n+                \"saving time. Instead, use a locale based timezone name (such as \"\n+                \"Europe/Helsinki).\"\n+            )\n+        elif isinstance(obj, ZoneInfo):\n+            return obj\n+        elif hasattr(obj, \"zone\"):\n+            # pytz timezones\n+            if obj.zone:\n+                return ZoneInfo(obj.zone)\n+\n+            return timezone(obj._offset)\n+\n         return obj\n+\n     if obj is not None:\n-        raise TypeError('Expected tzinfo, got %s instead' % obj.__class__.__name__)\n+        raise TypeError(f\"Expected tzinfo, got {obj.__class__.__name__} instead\")\n+\n+\n+def asdate(obj):\n+    if isinstance(obj, str):\n+        return date.fromisoformat(obj)\n+\n+    return obj\n \n \n _DATE_REGEX = re.compile(\n-    r'(?P<year>\\d{4})-(?P<month>\\d{1,2})-(?P<day>\\d{1,2})'\n-    r'(?:[ T](?P<hour>\\d{1,2}):(?P<minute>\\d{1,2}):(?P<second>\\d{1,2})'\n-    r'(?:\\.(?P<microsecond>\\d{1,6}))?'\n-    r'(?P<timezone>Z|[+-]\\d\\d:\\d\\d)?)?$')\n+    r\"(?P<year>\\d{4})-(?P<month>\\d{1,2})-(?P<day>\\d{1,2})\"\n+    r\"(?:[ T](?P<hour>\\d{1,2}):(?P<minute>\\d{1,2}):(?P<second>\\d{1,2})\"\n+    r\"(?:\\.(?P<microsecond>\\d{1,6}))?\"\n+    r\"(?P<timezone>Z|[+-]\\d\\d:\\d\\d)?)?$\"\n+)\n \n \n def convert_to_datetime(input, tz, arg_name):\n@@ -108,12 +146,13 @@ def convert_to_datetime(input, tz, arg_name):\n     If a native datetime object is passed, it is given the specified timezone.\n     If the input is a string, it is parsed as a datetime with the given timezone.\n \n-    Date strings are accepted in three different forms: date only (Y-m-d), date with time\n-    (Y-m-d H:M:S) or with date+time with microseconds (Y-m-d H:M:S.micro). Additionally you can\n-    override the time zone by giving a specific offset in the format specified by ISO 8601:\n-    Z (UTC), +HH:MM or -HH:MM.\n+    Date strings are accepted in three different forms: date only (Y-m-d), date with\n+    time (Y-m-d H:M:S) or with date+time with microseconds (Y-m-d H:M:S.micro).\n+    Additionally you can override the time zone by giving a specific offset in the\n+    format specified by ISO 8601: Z (UTC), +HH:MM or -HH:MM.\n \n-    :param str|datetime input: the datetime or string to convert to a timezone aware datetime\n+    :param str|datetime input: the datetime or string to convert to a timezone aware\n+        datetime\n     :param datetime.tzinfo tz: timezone to interpret ``input`` in\n     :param str arg_name: the name of the argument (used in an error message)\n     :rtype: datetime\n@@ -125,32 +164,33 @@ def convert_to_datetime(input, tz, arg_name):\n         datetime_ = input\n     elif isinstance(input, date):\n         datetime_ = datetime.combine(input, time())\n-    elif isinstance(input, six.string_types):\n+    elif isinstance(input, str):\n         m = _DATE_REGEX.match(input)\n         if not m:\n-            raise ValueError('Invalid date string')\n+            raise ValueError(\"Invalid date string\")\n \n         values = m.groupdict()\n-        tzname = values.pop('timezone')\n-        if tzname == 'Z':\n-            tz = utc\n+        tzname = values.pop(\"timezone\")\n+        if tzname == \"Z\":\n+            tz = timezone.utc\n         elif tzname:\n-            hours, minutes = (int(x) for x in tzname[1:].split(':'))\n-            sign = 1 if tzname[0] == '+' else -1\n-            tz = FixedOffset(sign * (hours * 60 + minutes))\n+            hours, minutes = (int(x) for x in tzname[1:].split(\":\"))\n+            sign = 1 if tzname[0] == \"+\" else -1\n+            tz = timezone(sign * timedelta(hours=hours, minutes=minutes))\n \n         values = {k: int(v or 0) for k, v in values.items()}\n         datetime_ = datetime(**values)\n     else:\n-        raise TypeError('Unsupported type for %s: %s' % (arg_name, input.__class__.__name__))\n+        raise TypeError(f\"Unsupported type for {arg_name}: {input.__class__.__name__}\")\n \n     if datetime_.tzinfo is not None:\n         return datetime_\n     if tz is None:\n         raise ValueError(\n-            'The \"tz\" argument must be specified if %s has no timezone information' % arg_name)\n-    if isinstance(tz, six.string_types):\n-        tz = timezone(tz)\n+            f'The \"tz\" argument must be specified if {arg_name} has no timezone information'\n+        )\n+    if isinstance(tz, str):\n+        tz = astimezone(tz)\n \n     return localize(datetime_, tz)\n \n@@ -176,7 +216,7 @@ def utc_timestamp_to_datetime(timestamp):\n \n     \"\"\"\n     if timestamp is not None:\n-        return datetime.fromtimestamp(timestamp, utc)\n+        return datetime.fromtimestamp(timestamp, timezone.utc)\n \n \n def timedelta_seconds(delta):\n@@ -187,8 +227,7 @@ def timedelta_seconds(delta):\n     :rtype: float\n \n     \"\"\"\n-    return delta.days * 24 * 60 * 60 + delta.seconds + \\\n-        delta.microseconds / 1000000.0\n+    return delta.days * 24 * 60 * 60 + delta.seconds + delta.microseconds / 1000000.0\n \n \n def datetime_ceil(dateval):\n@@ -199,12 +238,41 @@ def datetime_ceil(dateval):\n \n     \"\"\"\n     if dateval.microsecond > 0:\n-        return dateval + timedelta(seconds=1, microseconds=-dateval.microsecond)\n+        return datetime_utc_add(\n+            dateval, timedelta(seconds=1, microseconds=-dateval.microsecond)\n+        )\n+\n     return dateval\n \n \n+def datetime_utc_add(dateval: datetime, tdelta: timedelta) -> datetime:\n+    \"\"\"\n+    Adds an timedelta to a datetime in UTC for correct datetime arithmetic across\n+    Daylight Saving Time changes\n+\n+    :param dateval: The date to add to\n+    :type dateval: datetime\n+    :param operand: The timedelta to add to the datetime\n+    :type operand: timedelta\n+    :return: The sum of the datetime and the timedelta\n+    :rtype: datetime\n+    \"\"\"\n+    original_tz = dateval.tzinfo\n+    if original_tz is None:\n+        return dateval + tdelta\n+\n+    return (dateval.astimezone(UTC) + tdelta).astimezone(original_tz)\n+\n+\n def datetime_repr(dateval):\n-    return dateval.strftime('%Y-%m-%d %H:%M:%S %Z') if dateval else 'None'\n+    return dateval.strftime(\"%Y-%m-%d %H:%M:%S %Z\") if dateval else \"None\"\n+\n+\n+def timezone_repr(timezone: tzinfo) -> str:\n+    if isinstance(timezone, ZoneInfo):\n+        return timezone.key\n+\n+    return repr(timezone)\n \n \n def get_callable_name(func):\n@@ -214,30 +282,19 @@ def get_callable_name(func):\n     :rtype: str\n \n     \"\"\"\n-    # the easy case (on Python 3.3+)\n-    if hasattr(func, '__qualname__'):\n+    if ismethod(func):\n+        self = func.__self__\n+        cls = self if isclass(self) else type(self)\n+        return f\"{cls.__qualname__}.{func.__name__}\"\n+    elif isclass(func) or isfunction(func) or isbuiltin(func):\n         return func.__qualname__\n-\n-    # class methods, bound and unbound methods\n-    f_self = getattr(func, '__self__', None) or getattr(func, 'im_self', None)\n-    if f_self and hasattr(func, '__name__'):\n-        f_class = f_self if isclass(f_self) else f_self.__class__\n-    else:\n-        f_class = getattr(func, 'im_class', None)\n-\n-    if f_class and hasattr(func, '__name__'):\n-        return '%s.%s' % (f_class.__name__, func.__name__)\n-\n-    # class or class instance\n-    if hasattr(func, '__call__'):\n-        # class\n-        if hasattr(func, '__name__'):\n-            return func.__name__\n-\n+    elif hasattr(func, \"__call__\") and callable(func.__call__):\n         # instance of a class with a __call__ method\n-        return func.__class__.__name__\n+        return type(func).__qualname__\n \n-    raise TypeError('Unable to determine a name for %r -- maybe it is not a callable?' % func)\n+    raise TypeError(\n+        f\"Unable to determine a name for {func!r} -- maybe it is not a callable?\"\n+    )\n \n \n def obj_to_ref(obj):\n@@ -251,26 +308,20 @@ def obj_to_ref(obj):\n \n     \"\"\"\n     if isinstance(obj, partial):\n-        raise ValueError('Cannot create a reference to a partial()')\n+        raise ValueError(\"Cannot create a reference to a partial()\")\n \n     name = get_callable_name(obj)\n-    if '<lambda>' in name:\n-        raise ValueError('Cannot create a reference to a lambda')\n-    if '<locals>' in name:\n-        raise ValueError('Cannot create a reference to a nested function')\n+    if \"<lambda>\" in name:\n+        raise ValueError(\"Cannot create a reference to a lambda\")\n+    if \"<locals>\" in name:\n+        raise ValueError(\"Cannot create a reference to a nested function\")\n \n     if ismethod(obj):\n-        if hasattr(obj, 'im_self') and obj.im_self:\n-            # bound method\n-            module = obj.im_self.__module__\n-        elif hasattr(obj, 'im_class') and obj.im_class:\n-            # unbound method\n-            module = obj.im_class.__module__\n-        else:\n-            module = obj.__module__\n+        module = obj.__self__.__module__\n     else:\n         module = obj.__module__\n-    return '%s:%s' % (module, name)\n+\n+    return f\"{module}:{name}\"\n \n \n def ref_to_obj(ref):\n@@ -280,23 +331,25 @@ def ref_to_obj(ref):\n     :type ref: str\n \n     \"\"\"\n-    if not isinstance(ref, six.string_types):\n-        raise TypeError('References must be strings')\n-    if ':' not in ref:\n-        raise ValueError('Invalid reference')\n+    if not isinstance(ref, str):\n+        raise TypeError(\"References must be strings\")\n+    if \":\" not in ref:\n+        raise ValueError(\"Invalid reference\")\n \n-    modulename, rest = ref.split(':', 1)\n+    modulename, rest = ref.split(\":\", 1)\n     try:\n         obj = __import__(modulename, fromlist=[rest])\n-    except ImportError:\n-        raise LookupError('Error resolving reference %s: could not import module' % ref)\n+    except ImportError as exc:\n+        raise LookupError(\n+            f\"Error resolving reference {ref}: could not import module\"\n+        ) from exc\n \n     try:\n-        for name in rest.split('.'):\n+        for name in rest.split(\".\"):\n             obj = getattr(obj, name)\n         return obj\n     except Exception:\n-        raise LookupError('Error resolving reference %s: error looking up object' % ref)\n+        raise LookupError(f\"Error resolving reference {ref}: error looking up object\")\n \n \n def maybe_ref(ref):\n@@ -310,16 +363,6 @@ def maybe_ref(ref):\n     return ref_to_obj(ref)\n \n \n-if six.PY2:\n-    def repr_escape(string):\n-        if isinstance(string, six.text_type):\n-            return string.encode('ascii', 'backslashreplace')\n-        return string\n-else:\n-    def repr_escape(string):\n-        return string\n-\n-\n def check_callable_args(func, args, kwargs):\n     \"\"\"\n     Ensures that the given callable can be called with the given arguments.\n@@ -332,22 +375,21 @@ def check_callable_args(func, args, kwargs):\n     positional_only_kwargs = []  # positional-only parameters that have a match in kwargs\n     unsatisfied_args = []  # parameters in signature that don't have a match in args or kwargs\n     unsatisfied_kwargs = []  # keyword-only arguments that don't have a match in kwargs\n-    unmatched_args = list(args)  # args that didn't match any of the parameters in the signature\n+    unmatched_args = list(\n+        args\n+    )  # args that didn't match any of the parameters in the signature\n     # kwargs that didn't match any of the parameters in the signature\n     unmatched_kwargs = list(kwargs)\n     # indicates if the signature defines *args and **kwargs respectively\n     has_varargs = has_var_kwargs = False\n \n     try:\n-        if sys.version_info >= (3, 5):\n-            sig = signature(func, follow_wrapped=False)\n-        else:\n-            sig = signature(func)\n+        sig = signature(func, follow_wrapped=False)\n     except ValueError:\n         # signature() doesn't work against every kind of callable\n         return\n \n-    for param in six.itervalues(sig.parameters):\n+    for param in sig.parameters.values():\n         if param.kind == param.POSITIONAL_OR_KEYWORD:\n             if param.name in unmatched_kwargs and unmatched_args:\n                 pos_kwargs_conflicts.append(param.name)\n@@ -377,37 +419,50 @@ def check_callable_args(func, args, kwargs):\n \n     # Make sure there are no conflicts between args and kwargs\n     if pos_kwargs_conflicts:\n-        raise ValueError('The following arguments are supplied in both args and kwargs: %s' %\n-                         ', '.join(pos_kwargs_conflicts))\n+        raise ValueError(\n+            \"The following arguments are supplied in both args and kwargs: {}\".format(\n+                \", \".join(pos_kwargs_conflicts)\n+            )\n+        )\n \n     # Check if keyword arguments are being fed to positional-only parameters\n     if positional_only_kwargs:\n-        raise ValueError('The following arguments cannot be given as keyword arguments: %s' %\n-                         ', '.join(positional_only_kwargs))\n+        raise ValueError(\n+            \"The following arguments cannot be given as keyword arguments: {}\".format(\n+                \", \".join(positional_only_kwargs)\n+            )\n+        )\n \n-    # Check that the number of positional arguments minus the number of matched kwargs matches the\n-    # argspec\n+    # Check that the number of positional arguments minus the number of matched kwargs\n+    # matches the argspec\n     if unsatisfied_args:\n-        raise ValueError('The following arguments have not been supplied: %s' %\n-                         ', '.join(unsatisfied_args))\n+        raise ValueError(\n+            \"The following arguments have not been supplied: {}\".format(\n+                \", \".join(unsatisfied_args)\n+            )\n+        )\n \n     # Check that all keyword-only arguments have been supplied\n     if unsatisfied_kwargs:\n         raise ValueError(\n-            'The following keyword-only arguments have not been supplied in kwargs: %s' %\n-            ', '.join(unsatisfied_kwargs))\n+            \"The following keyword-only arguments have not been supplied in kwargs: \"\n+            \"{}\".format(\", \".join(unsatisfied_kwargs))\n+        )\n \n     # Check that the callable can accept the given number of positional arguments\n     if not has_varargs and unmatched_args:\n         raise ValueError(\n-            'The list of positional arguments is longer than the target callable can handle '\n-            '(allowed: %d, given in args: %d)' % (len(args) - len(unmatched_args), len(args)))\n+            f\"The list of positional arguments is longer than the target callable can \"\n+            f\"handle (allowed: {len(args) - len(unmatched_args)}, given in args: \"\n+            f\"{len(args)})\"\n+        )\n \n     # Check that the callable can accept the given keyword arguments\n     if not has_var_kwargs and unmatched_kwargs:\n         raise ValueError(\n-            'The target callable does not accept the following keyword arguments: %s' %\n-            ', '.join(unmatched_kwargs))\n+            \"The target callable does not accept the following keyword arguments: \"\n+            \"{}\".format(\", \".join(unmatched_kwargs))\n+        )\n \n \n def iscoroutinefunction_partial(f):\n@@ -424,7 +479,7 @@ def normalize(dt):\n \n \n def localize(dt, tzinfo):\n-    if hasattr(tzinfo, 'localize'):\n+    if hasattr(tzinfo, \"localize\"):\n         return tzinfo.localize(dt)\n \n     return normalize(dt.replace(tzinfo=tzinfo))"}, {"sha": "9232b379c6b322056e815a2c42969b85548b8e4d", "filename": "lib/arrow/__init__.py", "status": "modified", "additions": 2, "deletions": 0, "changes": 2, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Farrow%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -11,6 +11,7 @@\n     FORMAT_RFC1123,\n     FORMAT_RFC2822,\n     FORMAT_RFC3339,\n+    FORMAT_RFC3339_STRICT,\n     FORMAT_RSS,\n     FORMAT_W3C,\n )\n@@ -33,6 +34,7 @@\n     \"FORMAT_RFC1123\",\n     \"FORMAT_RFC2822\",\n     \"FORMAT_RFC3339\",\n+    \"FORMAT_RFC3339_STRICT\",\n     \"FORMAT_RSS\",\n     \"FORMAT_W3C\",\n     \"ParserError\","}, {"sha": "3e8d9f94621c6b29efab723e119a73a0dbe15089", "filename": "lib/arrow/_version.py", "status": "modified", "additions": 1, "deletions": 1, "changes": 2, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2F_version.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2F_version.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Farrow%2F_version.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1 +1 @@\n-__version__ = \"1.3.0\"\n+__version__ = \"1.4.0\""}, {"sha": "6fd2640da5fd0e718e73aeba49ac52930c1204b7", "filename": "lib/arrow/api.py", "status": "modified", "additions": 5, "deletions": 10, "changes": 15, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Fapi.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Fapi.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Farrow%2Fapi.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -26,8 +26,7 @@ def get(\n     locale: str = DEFAULT_LOCALE,\n     tzinfo: Optional[TZ_EXPR] = None,\n     normalize_whitespace: bool = False,\n-) -> Arrow:\n-    ...  # pragma: no cover\n+) -> Arrow: ...  # pragma: no cover\n \n \n @overload\n@@ -36,8 +35,7 @@ def get(\n     locale: str = DEFAULT_LOCALE,\n     tzinfo: Optional[TZ_EXPR] = None,\n     normalize_whitespace: bool = False,\n-) -> Arrow:\n-    ...  # pragma: no cover\n+) -> Arrow: ...  # pragma: no cover\n \n \n @overload\n@@ -57,8 +55,7 @@ def get(\n     locale: str = DEFAULT_LOCALE,\n     tzinfo: Optional[TZ_EXPR] = None,\n     normalize_whitespace: bool = False,\n-) -> Arrow:\n-    ...  # pragma: no cover\n+) -> Arrow: ...  # pragma: no cover\n \n \n @overload\n@@ -69,8 +66,7 @@ def get(\n     locale: str = DEFAULT_LOCALE,\n     tzinfo: Optional[TZ_EXPR] = None,\n     normalize_whitespace: bool = False,\n-) -> Arrow:\n-    ...  # pragma: no cover\n+) -> Arrow: ...  # pragma: no cover\n \n \n @overload\n@@ -81,8 +77,7 @@ def get(\n     locale: str = DEFAULT_LOCALE,\n     tzinfo: Optional[TZ_EXPR] = None,\n     normalize_whitespace: bool = False,\n-) -> Arrow:\n-    ...  # pragma: no cover\n+) -> Arrow: ...  # pragma: no cover\n \n \n def get(*args: Any, **kwargs: Any) -> Arrow:"}, {"sha": "d40927ed202e5869c5d97e64c1a0de9caf93c1a1", "filename": "lib/arrow/arrow.py", "status": "modified", "additions": 81, "deletions": 47, "changes": 128, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Farrow.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Farrow.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Farrow%2Farrow.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -4,23 +4,24 @@\n \n \"\"\"\n \n-\n import calendar\n import re\n import sys\n from datetime import date\n from datetime import datetime as dt_datetime\n from datetime import time as dt_time\n-from datetime import timedelta\n+from datetime import timedelta, timezone\n from datetime import tzinfo as dt_tzinfo\n from math import trunc\n from time import struct_time\n from typing import (\n     Any,\n     ClassVar,\n+    Final,\n     Generator,\n     Iterable,\n     List,\n+    Literal,\n     Mapping,\n     Optional,\n     Tuple,\n@@ -36,12 +37,6 @@\n from arrow.constants import DEFAULT_LOCALE, DEHUMANIZE_LOCALES\n from arrow.locales import TimeFrameLiteral\n \n-if sys.version_info < (3, 8):  # pragma: no cover\n-    from typing_extensions import Final, Literal\n-else:\n-    from typing import Final, Literal  # pragma: no cover\n-\n-\n TZ_EXPR = Union[dt_tzinfo, str]\n \n _T_FRAMES = Literal[\n@@ -128,6 +123,7 @@ class Arrow:\n     ]\n     _ATTRS_PLURAL: Final[List[str]] = [f\"{a}s\" for a in _ATTRS]\n     _MONTHS_PER_QUARTER: Final[int] = 3\n+    _MONTHS_PER_YEAR: Final[int] = 12\n     _SECS_PER_MINUTE: Final[int] = 60\n     _SECS_PER_HOUR: Final[int] = 60 * 60\n     _SECS_PER_DAY: Final[int] = 60 * 60 * 24\n@@ -162,7 +158,7 @@ def __init__(\n         **kwargs: Any,\n     ) -> None:\n         if tzinfo is None:\n-            tzinfo = dateutil_tz.tzutc()\n+            tzinfo = timezone.utc\n         # detect that tzinfo is a pytz object (issue #626)\n         elif (\n             isinstance(tzinfo, dt_tzinfo)\n@@ -197,7 +193,7 @@ def now(cls, tzinfo: Optional[dt_tzinfo] = None) -> \"Arrow\":\n         \"\"\"\n \n         if tzinfo is None:\n-            tzinfo = dateutil_tz.tzlocal()\n+            tzinfo = dt_datetime.now().astimezone().tzinfo\n \n         dt = dt_datetime.now(tzinfo)\n \n@@ -225,7 +221,7 @@ def utcnow(cls) -> \"Arrow\":\n \n         \"\"\"\n \n-        dt = dt_datetime.now(dateutil_tz.tzutc())\n+        dt = dt_datetime.now(timezone.utc)\n \n         return cls(\n             dt.year,\n@@ -254,7 +250,7 @@ def fromtimestamp(\n         \"\"\"\n \n         if tzinfo is None:\n-            tzinfo = dateutil_tz.tzlocal()\n+            tzinfo = dt_datetime.now().astimezone().tzinfo\n         elif isinstance(tzinfo, str):\n             tzinfo = parser.TzinfoParser.parse(tzinfo)\n \n@@ -288,7 +284,7 @@ def utcfromtimestamp(cls, timestamp: Union[int, float, str]) -> \"Arrow\":\n             raise ValueError(f\"The provided timestamp {timestamp!r} is invalid.\")\n \n         timestamp = util.normalize_timestamp(float(timestamp))\n-        dt = dt_datetime.utcfromtimestamp(timestamp)\n+        dt = dt_datetime.fromtimestamp(timestamp, timezone.utc)\n \n         return cls(\n             dt.year,\n@@ -298,7 +294,7 @@ def utcfromtimestamp(cls, timestamp: Union[int, float, str]) -> \"Arrow\":\n             dt.minute,\n             dt.second,\n             dt.microsecond,\n-            dateutil_tz.tzutc(),\n+            timezone.utc,\n             fold=getattr(dt, \"fold\", 0),\n         )\n \n@@ -322,7 +318,7 @@ def fromdatetime(cls, dt: dt_datetime, tzinfo: Optional[TZ_EXPR] = None) -> \"Arr\n \n         if tzinfo is None:\n             if dt.tzinfo is None:\n-                tzinfo = dateutil_tz.tzutc()\n+                tzinfo = timezone.utc\n             else:\n                 tzinfo = dt.tzinfo\n \n@@ -349,7 +345,7 @@ def fromdate(cls, date: date, tzinfo: Optional[TZ_EXPR] = None) -> \"Arrow\":\n         \"\"\"\n \n         if tzinfo is None:\n-            tzinfo = dateutil_tz.tzutc()\n+            tzinfo = timezone.utc\n \n         return cls(date.year, date.month, date.day, tzinfo=tzinfo)\n \n@@ -496,7 +492,7 @@ def range(\n \n             values = [getattr(current, f) for f in cls._ATTRS]\n             current = cls(*values, tzinfo=tzinfo).shift(  # type: ignore[misc]\n-                **{frame_relative: relative_steps}\n+                check_imaginary=True, **{frame_relative: relative_steps}\n             )\n \n             if frame in [\"month\", \"quarter\", \"year\"] and current.day < original_day:\n@@ -554,14 +550,14 @@ def span(\n             (<Arrow [2021-02-20T00:00:00+00:00]>, <Arrow [2021-02-26T23:59:59.999999+00:00]>)\n \n         \"\"\"\n-        if not 1 <= week_start <= 7:\n-            raise ValueError(\"week_start argument must be between 1 and 7.\")\n \n         util.validate_bounds(bounds)\n \n         frame_absolute, frame_relative, relative_steps = self._get_frames(frame)\n \n         if frame_absolute == \"week\":\n+            if not 1 <= week_start <= 7:\n+                raise ValueError(\"week_start argument must be between 1 and 7.\")\n             attr = \"day\"\n         elif frame_absolute == \"quarter\":\n             attr = \"month\"\n@@ -587,7 +583,9 @@ def span(\n             elif frame_absolute == \"quarter\":\n                 floor = floor.shift(months=-((self.month - 1) % 3))\n \n-        ceil = floor.shift(**{frame_relative: count * relative_steps})\n+        ceil = floor.shift(\n+            check_imaginary=True, **{frame_relative: count * relative_steps}\n+        )\n \n         if bounds[0] == \"(\":\n             floor = floor.shift(microseconds=+1)\n@@ -597,39 +595,49 @@ def span(\n \n         return floor, ceil\n \n-    def floor(self, frame: _T_FRAMES) -> \"Arrow\":\n+    def floor(self, frame: _T_FRAMES, **kwargs: Any) -> \"Arrow\":\n         \"\"\"Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, representing the \"floor\"\n         of the timespan of the :class:`Arrow <arrow.arrow.Arrow>` object in a given timeframe.\n         Equivalent to the first element in the 2-tuple returned by\n         :func:`span <arrow.arrow.Arrow.span>`.\n \n         :param frame: the timeframe.  Can be any ``datetime`` property (day, hour, minute...).\n+        :param week_start: (optional) only used in combination with the week timeframe. Follows isoweekday() where\n+            Monday is 1 and Sunday is 7.\n \n         Usage::\n \n             >>> arrow.utcnow().floor('hour')\n             <Arrow [2013-05-09T03:00:00+00:00]>\n \n+            >>> arrow.utcnow().floor('week', week_start=7)\n+            <Arrow [2021-02-21T00:00:00+00:00]>\n+\n         \"\"\"\n \n-        return self.span(frame)[0]\n+        return self.span(frame, **kwargs)[0]\n \n-    def ceil(self, frame: _T_FRAMES) -> \"Arrow\":\n+    def ceil(self, frame: _T_FRAMES, **kwargs: Any) -> \"Arrow\":\n         \"\"\"Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, representing the \"ceiling\"\n         of the timespan of the :class:`Arrow <arrow.arrow.Arrow>` object in a given timeframe.\n         Equivalent to the second element in the 2-tuple returned by\n         :func:`span <arrow.arrow.Arrow.span>`.\n \n         :param frame: the timeframe.  Can be any ``datetime`` property (day, hour, minute...).\n+        :param week_start: (optional) only used in combination with the week timeframe. Follows isoweekday() where\n+            Monday is 1 and Sunday is 7.\n \n         Usage::\n \n             >>> arrow.utcnow().ceil('hour')\n             <Arrow [2013-05-09T03:59:59.999999+00:00]>\n \n+            >>> arrow.utcnow().ceil('week', week_start=7)\n+            <Arrow [2021-02-27T23:59:59.999999+00:00]>\n+\n         \"\"\"\n \n-        return self.span(frame)[1]\n+        return self.span(frame, **kwargs)[1]\n \n     @classmethod\n     def span_range(\n@@ -802,15 +810,15 @@ def __hash__(self) -> int:\n \n     # attributes and properties\n \n-    def __getattr__(self, name: str) -> int:\n+    def __getattr__(self, name: str) -> Any:\n         if name == \"week\":\n             return self.isocalendar()[1]\n \n         if name == \"quarter\":\n             return int((self.month - 1) / self._MONTHS_PER_QUARTER) + 1\n \n         if not name.startswith(\"_\"):\n-            value: Optional[int] = getattr(self._datetime, name, None)\n+            value: Optional[Any] = getattr(self._datetime, name, None)\n \n             if value is not None:\n                 return value\n@@ -985,10 +993,15 @@ def replace(self, **kwargs: Any) -> \"Arrow\":\n \n         return self.fromdatetime(current)\n \n-    def shift(self, **kwargs: Any) -> \"Arrow\":\n+    def shift(self, check_imaginary: bool = True, **kwargs: Any) -> \"Arrow\":\n         \"\"\"Returns a new :class:`Arrow <arrow.arrow.Arrow>` object with attributes updated\n         according to inputs.\n \n+        Parameters:\n+        check_imaginary (bool): If True (default), will check for and resolve\n+        imaginary times (like during DST transitions). If False, skips this check.\n+\n+\n         Use pluralized property names to relatively shift their current value:\n \n         >>> import arrow\n@@ -1035,7 +1048,8 @@ def shift(self, **kwargs: Any) -> \"Arrow\":\n \n         current = self._datetime + relativedelta(**relative_kwargs)\n \n-        if not dateutil_tz.datetime_exists(current):\n+        # If check_imaginary is True, perform the check for imaginary times (DST transitions)\n+        if check_imaginary and not dateutil_tz.datetime_exists(current):\n             current = dateutil_tz.resolve_imaginary(current)\n \n         return self.fromdatetime(current)\n@@ -1092,7 +1106,8 @@ def format(\n         self, fmt: str = \"YYYY-MM-DD HH:mm:ssZZ\", locale: str = DEFAULT_LOCALE\n     ) -> str:\n         \"\"\"Returns a string representation of the :class:`Arrow <arrow.arrow.Arrow>` object,\n-        formatted according to the provided format string.\n+        formatted according to the provided format string. For a list of formatting values,\n+        see :ref:`supported-tokens`\n \n         :param fmt: the format string.\n         :param locale: the locale to format.\n@@ -1147,7 +1162,7 @@ def humanize(\n         locale = locales.get_locale(locale)\n \n         if other is None:\n-            utc = dt_datetime.utcnow().replace(tzinfo=dateutil_tz.tzutc())\n+            utc = dt_datetime.now(timezone.utc).replace(tzinfo=timezone.utc)\n             dt = utc.astimezone(self._datetime.tzinfo)\n \n         elif isinstance(other, Arrow):\n@@ -1185,6 +1200,7 @@ def humanize(\n \n                 elif diff < self._SECS_PER_MINUTE * 2:\n                     return locale.describe(\"minute\", sign, only_distance=only_distance)\n+\n                 elif diff < self._SECS_PER_HOUR:\n                     minutes = sign * max(delta_second // self._SECS_PER_MINUTE, 2)\n                     return locale.describe(\n@@ -1193,36 +1209,54 @@ def humanize(\n \n                 elif diff < self._SECS_PER_HOUR * 2:\n                     return locale.describe(\"hour\", sign, only_distance=only_distance)\n+\n                 elif diff < self._SECS_PER_DAY:\n                     hours = sign * max(delta_second // self._SECS_PER_HOUR, 2)\n                     return locale.describe(\"hours\", hours, only_distance=only_distance)\n-                elif diff < self._SECS_PER_DAY * 2:\n+\n+                calendar_diff = (\n+                    relativedelta(dt, self._datetime)\n+                    if self._datetime < dt\n+                    else relativedelta(self._datetime, dt)\n+                )\n+                calendar_months = (\n+                    calendar_diff.years * self._MONTHS_PER_YEAR + calendar_diff.months\n+                )\n+\n+                # For months, if more than 2 weeks, count as a full month\n+                if calendar_diff.days > 14:\n+                    calendar_months += 1\n+\n+                calendar_months = min(calendar_months, self._MONTHS_PER_YEAR)\n+\n+                if diff < self._SECS_PER_DAY * 2:\n                     return locale.describe(\"day\", sign, only_distance=only_distance)\n+\n                 elif diff < self._SECS_PER_WEEK:\n                     days = sign * max(delta_second // self._SECS_PER_DAY, 2)\n                     return locale.describe(\"days\", days, only_distance=only_distance)\n \n+                elif calendar_months >= 1 and diff < self._SECS_PER_YEAR:\n+                    if calendar_months == 1:\n+                        return locale.describe(\n+                            \"month\", sign, only_distance=only_distance\n+                        )\n+                    else:\n+                        months = sign * calendar_months\n+                        return locale.describe(\n+                            \"months\", months, only_distance=only_distance\n+                        )\n+\n                 elif diff < self._SECS_PER_WEEK * 2:\n                     return locale.describe(\"week\", sign, only_distance=only_distance)\n+\n                 elif diff < self._SECS_PER_MONTH:\n                     weeks = sign * max(delta_second // self._SECS_PER_WEEK, 2)\n                     return locale.describe(\"weeks\", weeks, only_distance=only_distance)\n \n-                elif diff < self._SECS_PER_MONTH * 2:\n-                    return locale.describe(\"month\", sign, only_distance=only_distance)\n-                elif diff < self._SECS_PER_YEAR:\n-                    # TODO revisit for humanization during leap years\n-                    self_months = self._datetime.year * 12 + self._datetime.month\n-                    other_months = dt.year * 12 + dt.month\n-\n-                    months = sign * max(abs(other_months - self_months), 2)\n-\n-                    return locale.describe(\n-                        \"months\", months, only_distance=only_distance\n-                    )\n-\n                 elif diff < self._SECS_PER_YEAR * 2:\n                     return locale.describe(\"year\", sign, only_distance=only_distance)\n+\n                 else:\n                     years = sign * max(delta_second // self._SECS_PER_YEAR, 2)\n                     return locale.describe(\"years\", years, only_distance=only_distance)\n@@ -1444,7 +1478,7 @@ def dehumanize(self, input_string: str, locale: str = \"en_us\") -> \"Arrow\":\n \n         time_changes = {k: sign_val * v for k, v in time_object_info.items()}\n \n-        return current_time.shift(**time_changes)\n+        return current_time.shift(check_imaginary=True, **time_changes)\n \n     # query functions\n \n@@ -1788,7 +1822,7 @@ def __le__(self, other: Any) -> bool:\n     def _get_tzinfo(tz_expr: Optional[TZ_EXPR]) -> dt_tzinfo:\n         \"\"\"Get normalized tzinfo object from various inputs.\"\"\"\n         if tz_expr is None:\n-            return dateutil_tz.tzutc()\n+            return timezone.utc\n         if isinstance(tz_expr, dt_tzinfo):\n             return tz_expr\n         else:\n@@ -1862,7 +1896,7 @@ def _get_iteration_params(cls, end: Any, limit: Optional[int]) -> Tuple[Any, int\n     @staticmethod\n     def _is_last_day_of_month(date: \"Arrow\") -> bool:\n         \"\"\"Returns a boolean indicating whether the datetime is the last day of the month.\"\"\"\n-        return date.day == calendar.monthrange(date.year, date.month)[1]\n+        return cast(int, date.day) == calendar.monthrange(date.year, date.month)[1]\n \n \n Arrow.min = Arrow.fromdatetime(dt_datetime.min)"}, {"sha": "532e95969543728e575a1100b2491d97966fc323", "filename": "lib/arrow/constants.py", "status": "modified", "additions": 1, "deletions": 5, "changes": 6, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Fconstants.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Fconstants.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Farrow%2Fconstants.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -2,11 +2,7 @@\n \n import sys\n from datetime import datetime\n-\n-if sys.version_info < (3, 8):  # pragma: no cover\n-    from typing_extensions import Final\n-else:\n-    from typing import Final  # pragma: no cover\n+from typing import Final\n \n # datetime.max.timestamp() errors on Windows, so we must hardcode\n # the highest possible datetime value that can output a timestamp."}, {"sha": "0913bfe1b49b95f1fa22be1dbcd6337791d142fe", "filename": "lib/arrow/factory.py", "status": "modified", "additions": 7, "deletions": 14, "changes": 21, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Ffactory.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Ffactory.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Farrow%2Ffactory.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -5,16 +5,13 @@\n \n \"\"\"\n \n-\n import calendar\n-from datetime import date, datetime\n+from datetime import date, datetime, timezone\n from datetime import tzinfo as dt_tzinfo\n from decimal import Decimal\n from time import struct_time\n from typing import Any, List, Optional, Tuple, Type, Union, overload\n \n-from dateutil import tz as dateutil_tz\n-\n from arrow import parser\n from arrow.arrow import TZ_EXPR, Arrow\n from arrow.constants import DEFAULT_LOCALE\n@@ -41,8 +38,7 @@ def get(\n         locale: str = DEFAULT_LOCALE,\n         tzinfo: Optional[TZ_EXPR] = None,\n         normalize_whitespace: bool = False,\n-    ) -> Arrow:\n-        ...  # pragma: no cover\n+    ) -> Arrow: ...  # pragma: no cover\n \n     @overload\n     def get(\n@@ -62,8 +58,7 @@ def get(\n         locale: str = DEFAULT_LOCALE,\n         tzinfo: Optional[TZ_EXPR] = None,\n         normalize_whitespace: bool = False,\n-    ) -> Arrow:\n-        ...  # pragma: no cover\n+    ) -> Arrow: ...  # pragma: no cover\n \n     @overload\n     def get(\n@@ -74,8 +69,7 @@ def get(\n         locale: str = DEFAULT_LOCALE,\n         tzinfo: Optional[TZ_EXPR] = None,\n         normalize_whitespace: bool = False,\n-    ) -> Arrow:\n-        ...  # pragma: no cover\n+    ) -> Arrow: ...  # pragma: no cover\n \n     @overload\n     def get(\n@@ -86,8 +80,7 @@ def get(\n         locale: str = DEFAULT_LOCALE,\n         tzinfo: Optional[TZ_EXPR] = None,\n         normalize_whitespace: bool = False,\n-    ) -> Arrow:\n-        ...  # pragma: no cover\n+    ) -> Arrow: ...  # pragma: no cover\n \n     def get(self, *args: Any, **kwargs: Any) -> Arrow:\n         \"\"\"Returns an :class:`Arrow <arrow.arrow.Arrow>` object based on flexible inputs.\n@@ -230,7 +223,7 @@ def get(self, *args: Any, **kwargs: Any) -> Arrow:\n             elif not isinstance(arg, str) and is_timestamp(arg):\n                 if tz is None:\n                     # set to UTC by default\n-                    tz = dateutil_tz.tzutc()\n+                    tz = timezone.utc\n                 return self.type.fromtimestamp(arg, tzinfo=tz)\n \n             # (Arrow) -> from the object's datetime @ tzinfo\n@@ -338,7 +331,7 @@ def now(self, tz: Optional[TZ_EXPR] = None) -> Arrow:\n         \"\"\"\n \n         if tz is None:\n-            tz = dateutil_tz.tzlocal()\n+            tz = datetime.now().astimezone().tzinfo\n         elif not isinstance(tz, dt_tzinfo):\n             tz = parser.TzinfoParser.parse(tz)\n "}, {"sha": "6c6a718c41d6cf5f98e2b83c6165bd30fe41c182", "filename": "lib/arrow/formatter.py", "status": "modified", "additions": 4, "deletions": 12, "changes": 16, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Fformatter.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Fformatter.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Farrow%2Fformatter.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,21 +1,12 @@\n \"\"\"Provides the :class:`Arrow <arrow.formatter.DateTimeFormatter>` class, an improved formatter for datetimes.\"\"\"\n \n import re\n-import sys\n-from datetime import datetime, timedelta\n-from typing import Optional, Pattern, cast\n-\n-from dateutil import tz as dateutil_tz\n+from datetime import datetime, timedelta, timezone\n+from typing import Final, Optional, Pattern, cast\n \n from arrow import locales\n from arrow.constants import DEFAULT_LOCALE\n \n-if sys.version_info < (3, 8):  # pragma: no cover\n-    from typing_extensions import Final\n-else:\n-    from typing import Final  # pragma: no cover\n-\n-\n FORMAT_ATOM: Final[str] = \"YYYY-MM-DD HH:mm:ssZZ\"\n FORMAT_COOKIE: Final[str] = \"dddd, DD-MMM-YYYY HH:mm:ss ZZZ\"\n FORMAT_RFC822: Final[str] = \"ddd, DD MMM YY HH:mm:ss Z\"\n@@ -24,6 +15,7 @@\n FORMAT_RFC1123: Final[str] = \"ddd, DD MMM YYYY HH:mm:ss Z\"\n FORMAT_RFC2822: Final[str] = \"ddd, DD MMM YYYY HH:mm:ss Z\"\n FORMAT_RFC3339: Final[str] = \"YYYY-MM-DD HH:mm:ssZZ\"\n+FORMAT_RFC3339_STRICT: Final[str] = \"YYYY-MM-DDTHH:mm:ssZZ\"\n FORMAT_RSS: Final[str] = \"ddd, DD MMM YYYY HH:mm:ss Z\"\n FORMAT_W3C: Final[str] = \"YYYY-MM-DD HH:mm:ssZZ\"\n \n@@ -128,7 +120,7 @@ def _format_token(self, dt: datetime, token: Optional[str]) -> Optional[str]:\n \n         if token in [\"ZZ\", \"Z\"]:\n             separator = \":\" if token == \"ZZ\" else \"\"\n-            tz = dateutil_tz.tzutc() if dt.tzinfo is None else dt.tzinfo\n+            tz = timezone.utc if dt.tzinfo is None else dt.tzinfo\n             # `dt` must be aware object. Otherwise, this line will raise AttributeError\n             # https://github.com/arrow-py/arrow/pull/883#discussion_r529866834\n             # datetime awareness: https://docs.python.org/3/library/datetime.html#aware-and-naive-objects"}, {"sha": "5d31aa24b399cf46aabc93201fa0d56b37192d8f", "filename": "lib/arrow/locales.py", "status": "modified", "additions": 218, "deletions": 51, "changes": 269, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Flocales.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Flocales.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Farrow%2Flocales.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,12 +1,12 @@\n \"\"\"Provides internationalization for arrow in over 60 languages and dialects.\"\"\"\n \n-import sys\n from math import trunc\n from typing import (\n     Any,\n     ClassVar,\n     Dict,\n     List,\n+    Literal,\n     Mapping,\n     Optional,\n     Sequence,\n@@ -16,11 +16,6 @@\n     cast,\n )\n \n-if sys.version_info < (3, 8):  # pragma: no cover\n-    from typing_extensions import Literal\n-else:\n-    from typing import Literal  # pragma: no cover\n-\n TimeFrameLiteral = Literal[\n     \"now\",\n     \"second\",\n@@ -659,20 +654,20 @@ class FrenchCanadianLocale(FrenchBaseLocale, Locale):\n class GreekLocale(Locale):\n     names = [\"el\", \"el-gr\"]\n \n-    past = \"{0} \u03c0\u03c1\u03b9\u03bd\"\n+    past = \"\u03c0\u03c1\u03b9\u03bd \u03b1\u03c0\u03cc {0}\"\n     future = \"\u03c3\u03b5 {0}\"\n     and_word = \"\u03ba\u03b1\u03b9\"\n \n     timeframes = {\n         \"now\": \"\u03c4\u03ce\u03c1\u03b1\",\n-        \"second\": \"\u03ad\u03bd\u03b1 \u03b4\u03b5\u03cd\u03c4\u03b5\u03c1\u03bf\",\n+        \"second\": \"\u03ad\u03bd\u03b1 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03bf\",\n         \"seconds\": \"{0} \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1\",\n         \"minute\": \"\u03ad\u03bd\u03b1 \u03bb\u03b5\u03c0\u03c4\u03cc\",\n         \"minutes\": \"{0} \u03bb\u03b5\u03c0\u03c4\u03ac\",\n         \"hour\": \"\u03bc\u03af\u03b1 \u03ce\u03c1\u03b1\",\n         \"hours\": \"{0} \u03ce\u03c1\u03b5\u03c2\",\n-        \"day\": \"\u03bc\u03af\u03b1 \u03bc\u03ad\u03c1\u03b1\",\n-        \"days\": \"{0} \u03bc\u03ad\u03c1\u03b5\u03c2\",\n+        \"day\": \"\u03bc\u03af\u03b1 \u03b7\u03bc\u03ad\u03c1\u03b1\",\n+        \"days\": \"{0} \u03b7\u03bc\u03ad\u03c1\u03b5\u03c2\",\n         \"week\": \"\u03bc\u03af\u03b1 \u03b5\u03b2\u03b4\u03bf\u03bc\u03ac\u03b4\u03b1\",\n         \"weeks\": \"{0} \u03b5\u03b2\u03b4\u03bf\u03bc\u03ac\u03b4\u03b5\u03c2\",\n         \"month\": \"\u03ad\u03bd\u03b1 \u03bc\u03ae\u03bd\u03b1\",\n@@ -702,7 +697,7 @@ class GreekLocale(Locale):\n         \"\u03a6\u03b5\u03b2\",\n         \"\u039c\u03b1\u03c1\",\n         \"\u0391\u03c0\u03c1\",\n-        \"\u039c\u03b1\u03ca\",\n+        \"\u039c\u03b1\u0390\",\n         \"\u0399\u03bf\u03bd\",\n         \"\u0399\u03bf\u03bb\",\n         \"\u0391\u03c5\u03b3\",\n@@ -781,7 +776,16 @@ class JapaneseLocale(Locale):\n         \"12\",\n     ]\n \n-    day_names = [\"\", \"\u6708\u66dc\u65e5\", \"\u706b\u66dc\u65e5\", \"\u6c34\u66dc\u65e5\", \"\u6728\u66dc\u65e5\", \"\u91d1\u66dc\u65e5\", \"\u571f\u66dc\u65e5\", \"\u65e5\u66dc\u65e5\"]\n+    day_names = [\n+        \"\",\n+        \"\u6708\u66dc\u65e5\",\n+        \"\u706b\u66dc\u65e5\",\n+        \"\u6c34\u66dc\u65e5\",\n+        \"\u6728\u66dc\u65e5\",\n+        \"\u91d1\u66dc\u65e5\",\n+        \"\u571f\u66dc\u65e5\",\n+        \"\u65e5\u66dc\u65e5\",\n+    ]\n     day_abbreviations = [\"\", \"\u6708\", \"\u706b\", \"\u6c34\", \"\u6728\", \"\u91d1\", \"\u571f\", \"\u65e5\"]\n \n \n@@ -997,7 +1001,16 @@ class ChineseCNLocale(Locale):\n         \"12\",\n     ]\n \n-    day_names = [\"\", \"\u661f\u671f\u4e00\", \"\u661f\u671f\u4e8c\", \"\u661f\u671f\u4e09\", \"\u661f\u671f\u56db\", \"\u661f\u671f\u4e94\", \"\u661f\u671f\u516d\", \"\u661f\u671f\u65e5\"]\n+    day_names = [\n+        \"\",\n+        \"\u661f\u671f\u4e00\",\n+        \"\u661f\u671f\u4e8c\",\n+        \"\u661f\u671f\u4e09\",\n+        \"\u661f\u671f\u56db\",\n+        \"\u661f\u671f\u4e94\",\n+        \"\u661f\u671f\u516d\",\n+        \"\u661f\u671f\u65e5\",\n+    ]\n     day_abbreviations = [\"\", \"\u4e00\", \"\u4e8c\", \"\u4e09\", \"\u56db\", \"\u4e94\", \"\u516d\", \"\u65e5\"]\n \n \n@@ -1116,7 +1129,16 @@ class HongKongLocale(Locale):\n         \"12\",\n     ]\n \n-    day_names = [\"\", \"\u661f\u671f\u4e00\", \"\u661f\u671f\u4e8c\", \"\u661f\u671f\u4e09\", \"\u661f\u671f\u56db\", \"\u661f\u671f\u4e94\", \"\u661f\u671f\u516d\", \"\u661f\u671f\u65e5\"]\n+    day_names = [\n+        \"\",\n+        \"\u661f\u671f\u4e00\",\n+        \"\u661f\u671f\u4e8c\",\n+        \"\u661f\u671f\u4e09\",\n+        \"\u661f\u671f\u56db\",\n+        \"\u661f\u671f\u4e94\",\n+        \"\u661f\u671f\u516d\",\n+        \"\u661f\u671f\u65e5\",\n+    ]\n     day_abbreviations = [\"\", \"\u4e00\", \"\u4e8c\", \"\u4e09\", \"\u56db\", \"\u4e94\", \"\u516d\", \"\u65e5\"]\n \n \n@@ -1145,7 +1167,6 @@ class KoreanLocale(Locale):\n     }\n \n     special_dayframes = {\n-        -3: \"\uadf8\ub044\uc81c\",\n         -2: \"\uadf8\uc81c\",\n         -1: \"\uc5b4\uc81c\",\n         1: \"\ub0b4\uc77c\",\n@@ -1154,7 +1175,7 @@ class KoreanLocale(Locale):\n         4: \"\uadf8\uae00\ud53c\",\n     }\n \n-    special_yearframes = {-2: \"\uc81c\uc791\ub144\", -1: \"\uc791\ub144\", 1: \"\ub0b4\ub144\", 2: \"\ub0b4\ud6c4\ub144\"}\n+    special_yearframes = {-2: \"\uc7ac\uc791\ub144\", -1: \"\uc791\ub144\", 1: \"\ub0b4\ub144\", 2: \"\ub0b4\ud6c4\ub144\"}\n \n     month_names = [\n         \"\",\n@@ -1187,11 +1208,32 @@ class KoreanLocale(Locale):\n         \"12\",\n     ]\n \n-    day_names = [\"\", \"\uc6d4\uc694\uc77c\", \"\ud654\uc694\uc77c\", \"\uc218\uc694\uc77c\", \"\ubaa9\uc694\uc77c\", \"\uae08\uc694\uc77c\", \"\ud1a0\uc694\uc77c\", \"\uc77c\uc694\uc77c\"]\n+    day_names = [\n+        \"\",\n+        \"\uc6d4\uc694\uc77c\",\n+        \"\ud654\uc694\uc77c\",\n+        \"\uc218\uc694\uc77c\",\n+        \"\ubaa9\uc694\uc77c\",\n+        \"\uae08\uc694\uc77c\",\n+        \"\ud1a0\uc694\uc77c\",\n+        \"\uc77c\uc694\uc77c\",\n+    ]\n     day_abbreviations = [\"\", \"\uc6d4\", \"\ud654\", \"\uc218\", \"\ubaa9\", \"\uae08\", \"\ud1a0\", \"\uc77c\"]\n \n     def _ordinal_number(self, n: int) -> str:\n-        ordinals = [\"0\", \"\uccab\", \"\ub450\", \"\uc138\", \"\ub124\", \"\ub2e4\uc12f\", \"\uc5ec\uc12f\", \"\uc77c\uacf1\", \"\uc5ec\ub35f\", \"\uc544\ud649\", \"\uc5f4\"]\n+        ordinals = [\n+            \"0\",\n+            \"\uccab\",\n+            \"\ub450\",\n+            \"\uc138\",\n+            \"\ub124\",\n+            \"\ub2e4\uc12f\",\n+            \"\uc5ec\uc12f\",\n+            \"\uc77c\uacf1\",\n+            \"\uc5ec\ub35f\",\n+            \"\uc544\ud649\",\n+            \"\uc5f4\",\n+        ]\n         if n < len(ordinals):\n             return f\"{ordinals[n]}\ubc88\uc9f8\"\n         return f\"{n}\ubc88\uc9f8\"\n@@ -1882,6 +1924,105 @@ class MacedonianLocale(SlavicBaseLocale):\n     ]\n \n \n+class MacedonianLatinLocale(SlavicBaseLocale):\n+    names = [\"mk-latn\", \"mk-mk-latn\"]\n+\n+    past = \"pred {0}\"\n+    future = \"za {0}\"\n+\n+    timeframes: ClassVar[Mapping[TimeFrameLiteral, Union[str, Mapping[str, str]]]] = {\n+        \"now\": \"sega\",\n+        \"second\": \"edna sekunda\",\n+        \"seconds\": {\n+            \"singular\": \"{0} sekunda\",\n+            \"dual\": \"{0} sekundi\",\n+            \"plural\": \"{0} sekundi\",\n+        },\n+        \"minute\": \"edna minuta\",\n+        \"minutes\": {\n+            \"singular\": \"{0} minuta\",\n+            \"dual\": \"{0} minuti\",\n+            \"plural\": \"{0} minuti\",\n+        },\n+        \"hour\": \"eden saat\",\n+        \"hours\": {\"singular\": \"{0} saat\", \"dual\": \"{0} saati\", \"plural\": \"{0} saati\"},\n+        \"day\": \"eden den\",\n+        \"days\": {\"singular\": \"{0} den\", \"dual\": \"{0} dena\", \"plural\": \"{0} dena\"},\n+        \"week\": \"edna nedela\",\n+        \"weeks\": {\n+            \"singular\": \"{0} nedela\",\n+            \"dual\": \"{0} nedeli\",\n+            \"plural\": \"{0} nedeli\",\n+        },\n+        \"month\": \"eden mesec\",\n+        \"months\": {\n+            \"singular\": \"{0} mesec\",\n+            \"dual\": \"{0} meseci\",\n+            \"plural\": \"{0} meseci\",\n+        },\n+        \"year\": \"edna godina\",\n+        \"years\": {\n+            \"singular\": \"{0} godina\",\n+            \"dual\": \"{0} godini\",\n+            \"plural\": \"{0} godini\",\n+        },\n+    }\n+\n+    meridians = {\"am\": \"dp\", \"pm\": \"pp\", \"AM\": \"pretpladne\", \"PM\": \"popladne\"}\n+\n+    month_names = [\n+        \"\",\n+        \"Januari\",\n+        \"Fevruari\",\n+        \"Mart\",\n+        \"April\",\n+        \"Maj\",\n+        \"Juni\",\n+        \"Juli\",\n+        \"Avgust\",\n+        \"Septemvri\",\n+        \"Oktomvri\",\n+        \"Noemvri\",\n+        \"Dekemvri\",\n+    ]\n+    month_abbreviations = [\n+        \"\",\n+        \"Jan\",\n+        \"Fev\",\n+        \"Mar\",\n+        \"Apr\",\n+        \"Maj\",\n+        \"Jun\",\n+        \"Jul\",\n+        \"Avg\",\n+        \"Sep\",\n+        \"Okt\",\n+        \"Noe\",\n+        \"Dek\",\n+    ]\n+\n+    day_names = [\n+        \"\",\n+        \"Ponedelnik\",\n+        \"Vtornik\",\n+        \"Sreda\",\n+        \"Chetvrtok\",\n+        \"Petok\",\n+        \"Sabota\",\n+        \"Nedela\",\n+    ]\n+    day_abbreviations = [\n+        \"\",\n+        \"Pon\",\n+        \"Vt\",\n+        \"Sre\",\n+        \"Chet\",\n+        \"Pet\",\n+        \"Sab\",\n+        \"Ned\",\n+    ]\n+\n+\n class GermanBaseLocale(Locale):\n     past = \"vor {0}\"\n     future = \"in {0}\"\n@@ -3324,6 +3465,10 @@ class FarsiLocale(Locale):\n         \"hours\": \"{0} \u0633\u0627\u0639\u062a\",\n         \"day\": \"\u06cc\u06a9 \u0631\u0648\u0632\",\n         \"days\": \"{0} \u0631\u0648\u0632\",\n+        \"week\": \"\u06cc\u06a9 \u0647\u0641\u062a\u0647\",\n+        \"weeks\": \"{0} \u0647\u0641\u062a\u0647\",\n+        \"quarter\": \"\u06cc\u06a9 \u0641\u0635\u0644\",\n+        \"quarters\": \"{0} \u0641\u0635\u0644\",\n         \"month\": \"\u06cc\u06a9 \u0645\u0627\u0647\",\n         \"months\": \"{0} \u0645\u0627\u0647\",\n         \"year\": \"\u06cc\u06a9 \u0633\u0627\u0644\",\n@@ -3339,33 +3484,33 @@ class FarsiLocale(Locale):\n \n     month_names = [\n         \"\",\n-        \"January\",\n-        \"February\",\n-        \"March\",\n-        \"April\",\n-        \"May\",\n-        \"June\",\n-        \"July\",\n-        \"August\",\n-        \"September\",\n-        \"October\",\n-        \"November\",\n-        \"December\",\n+        \"\u0698\u0627\u0646\u0648\u06cc\u0647\",\n+        \"\u0641\u0648\u0631\u06cc\u0647\",\n+        \"\u0645\u0627\u0631\u0633\",\n+        \"\u0622\u0648\u0631\u06cc\u0644\",\n+        \"\u0645\u0647\",\n+        \"\u0698\u0648\u0626\u0646\",\n+        \"\u0698\u0648\u0626\u06cc\u0647\",\n+        \"\u0627\u0648\u062a\",\n+        \"\u0633\u067e\u062a\u0627\u0645\u0628\u0631\",\n+        \"\u0627\u06a9\u062a\u0628\u0631\",\n+        \"\u0646\u0648\u0627\u0645\u0628\u0631\",\n+        \"\u062f\u0633\u0627\u0645\u0628\u0631\",\n     ]\n     month_abbreviations = [\n         \"\",\n-        \"Jan\",\n-        \"Feb\",\n-        \"Mar\",\n-        \"Apr\",\n-        \"May\",\n-        \"Jun\",\n-        \"Jul\",\n-        \"Aug\",\n-        \"Sep\",\n-        \"Oct\",\n-        \"Nov\",\n-        \"Dec\",\n+        \"\u0698\u0627\u0646\u0648\u06cc\u0647\",\n+        \"\u0641\u0648\u0631\u06cc\u0647\",\n+        \"\u0645\u0627\u0631\u0633\",\n+        \"\u0622\u0648\u0631\u06cc\u0644\",\n+        \"\u0645\u0647\",\n+        \"\u0698\u0648\u0626\u0646\",\n+        \"\u0698\u0648\u0626\u06cc\u0647\",\n+        \"\u0627\u0648\u062a\",\n+        \"\u0633\u067e\u062a\u0627\u0645\u0628\u0631\",\n+        \"\u0627\u06a9\u062a\u0628\u0631\",\n+        \"\u0646\u0648\u0627\u0645\u0628\u0631\",\n+        \"\u062f\u0633\u0627\u0645\u0628\u0631\",\n     ]\n \n     day_names = [\n@@ -3378,7 +3523,16 @@ class FarsiLocale(Locale):\n         \"\u0634\u0646\u0628\u0647\",\n         \"\u06cc\u06a9\u0634\u0646\u0628\u0647\",\n     ]\n-    day_abbreviations = [\"\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n+    day_abbreviations = [\n+        \"\",\n+        \"\u062f\u0648 \u0634\u0646\u0628\u0647\",\n+        \"\u0633\u0647 \u0634\u0646\u0628\u0647\",\n+        \"\u0686\u0647\u0627\u0631\u0634\u0646\u0628\u0647\",\n+        \"\u067e\u0646\u062c\u0634\u0646\u0628\u0647\",\n+        \"\u062c\u0645\u0639\u0647\",\n+        \"\u0634\u0646\u0628\u0647\",\n+        \"\u06cc\u06a9\u0634\u0646\u0628\u0647\",\n+    ]\n \n \n class HebrewLocale(Locale):\n@@ -3575,6 +3729,8 @@ class CatalanLocale(Locale):\n         \"hours\": \"{0} hores\",\n         \"day\": \"un dia\",\n         \"days\": \"{0} dies\",\n+        \"week\": \"una setmana\",\n+        \"weeks\": \"{0} setmanes\",\n         \"month\": \"un mes\",\n         \"months\": \"{0} mesos\",\n         \"year\": \"un any\",\n@@ -3859,16 +4015,18 @@ class ThaiLocale(Locale):\n     timeframes = {\n         \"now\": \"\u0e02\u0e13\u0e30\u0e19\u0e35\u0e49\",\n         \"second\": \"\u0e27\u0e34\u0e19\u0e32\u0e17\u0e35\",\n-        \"seconds\": \"{0} \u0e44\u0e21\u0e48\u0e01\u0e35\u0e48\u0e27\u0e34\u0e19\u0e32\u0e17\u0e35\",\n-        \"minute\": \"1 \u0e19\u0e32\u0e17\u0e35\",\n+        \"seconds\": \"{0} \u0e27\u0e34\u0e19\u0e32\u0e17\u0e35\",\n+        \"minute\": \"\u0e19\u0e32\u0e17\u0e35\",\n         \"minutes\": \"{0} \u0e19\u0e32\u0e17\u0e35\",\n-        \"hour\": \"1 \u0e0a\u0e31\u0e48\u0e27\u0e42\u0e21\u0e07\",\n+        \"hour\": \"\u0e0a\u0e31\u0e48\u0e27\u0e42\u0e21\u0e07\",\n         \"hours\": \"{0} \u0e0a\u0e31\u0e48\u0e27\u0e42\u0e21\u0e07\",\n-        \"day\": \"1 \u0e27\u0e31\u0e19\",\n+        \"day\": \"\u0e27\u0e31\u0e19\",\n         \"days\": \"{0} \u0e27\u0e31\u0e19\",\n-        \"month\": \"1 \u0e40\u0e14\u0e37\u0e2d\u0e19\",\n+        \"week\": \"\u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\",\n+        \"weeks\": \"{0} \u0e2a\u0e31\u0e1b\u0e14\u0e32\u0e2b\u0e4c\",\n+        \"month\": \"\u0e40\u0e14\u0e37\u0e2d\u0e19\",\n         \"months\": \"{0} \u0e40\u0e14\u0e37\u0e2d\u0e19\",\n-        \"year\": \"1 \u0e1b\u0e35\",\n+        \"year\": \"\u0e1b\u0e35\",\n         \"years\": \"{0} \u0e1b\u0e35\",\n     }\n \n@@ -3903,8 +4061,17 @@ class ThaiLocale(Locale):\n         \"\u0e18.\u0e04.\",\n     ]\n \n-    day_names = [\"\", \"\u0e08\u0e31\u0e19\u0e17\u0e23\u0e4c\", \"\u0e2d\u0e31\u0e07\u0e04\u0e32\u0e23\", \"\u0e1e\u0e38\u0e18\", \"\u0e1e\u0e24\u0e2b\u0e31\u0e2a\u0e1a\u0e14\u0e35\", \"\u0e28\u0e38\u0e01\u0e23\u0e4c\", \"\u0e40\u0e2a\u0e32\u0e23\u0e4c\", \"\u0e2d\u0e32\u0e17\u0e34\u0e15\u0e22\u0e4c\"]\n-    day_abbreviations = [\"\", \"\u0e08\", \"\u0e2d\", \"\u0e1e\", \"\u0e1e\u0e24\", \"\u0e28\", \"\u0e2a\", \"\u0e2d\u0e32\"]\n+    day_names = [\n+        \"\",\n+        \"\u0e27\u0e31\u0e19\u0e08\u0e31\u0e19\u0e17\u0e23\u0e4c\",\n+        \"\u0e27\u0e31\u0e19\u0e2d\u0e31\u0e07\u0e04\u0e32\u0e23\",\n+        \"\u0e27\u0e31\u0e19\u0e1e\u0e38\u0e18\",\n+        \"\u0e27\u0e31\u0e19\u0e1e\u0e24\u0e2b\u0e31\u0e2a\u0e1a\u0e14\u0e35\",\n+        \"\u0e27\u0e31\u0e19\u0e28\u0e38\u0e01\u0e23\u0e4c\",\n+        \"\u0e27\u0e31\u0e19\u0e40\u0e2a\u0e32\u0e23\u0e4c\",\n+        \"\u0e27\u0e31\u0e19\u0e2d\u0e32\u0e17\u0e34\u0e15\u0e22\u0e4c\",\n+    ]\n+    day_abbreviations = [\"\", \"\u0e08.\", \"\u0e2d.\", \"\u0e1e.\", \"\u0e1e\u0e24.\", \"\u0e28.\", \"\u0e2a.\", \"\u0e2d\u0e32.\"]\n \n     meridians = {\"am\": \"am\", \"pm\": \"pm\", \"AM\": \"AM\", \"PM\": \"PM\"}\n "}, {"sha": "46b833887ca3487872e0f7836794693496a90d47", "filename": "lib/arrow/parser.py", "status": "modified", "additions": 190, "deletions": 24, "changes": 214, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Fparser.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Farrow%2Fparser.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Farrow%2Fparser.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,8 +1,7 @@\n \"\"\"Provides the :class:`Arrow <arrow.parser.DateTimeParser>` class, a better way to parse datetime strings.\"\"\"\n \n import re\n-import sys\n-from datetime import datetime, timedelta\n+from datetime import datetime, timedelta, timezone\n from datetime import tzinfo as dt_tzinfo\n from functools import lru_cache\n from typing import (\n@@ -11,30 +10,38 @@\n     Dict,\n     Iterable,\n     List,\n+    Literal,\n     Match,\n     Optional,\n     Pattern,\n     SupportsFloat,\n     SupportsInt,\n     Tuple,\n+    TypedDict,\n     Union,\n     cast,\n     overload,\n )\n \n-from dateutil import tz\n+try:\n+    from zoneinfo import ZoneInfo, ZoneInfoNotFoundError\n+except ImportError:\n+    from backports.zoneinfo import ZoneInfo, ZoneInfoNotFoundError  # type: ignore[no-redef]\n \n from arrow import locales\n from arrow.constants import DEFAULT_LOCALE\n from arrow.util import next_weekday, normalize_timestamp\n \n-if sys.version_info < (3, 8):  # pragma: no cover\n-    from typing_extensions import Literal, TypedDict\n-else:\n-    from typing import Literal, TypedDict  # pragma: no cover\n-\n \n class ParserError(ValueError):\n+    \"\"\"\n+    A custom exception class for handling parsing errors in the parser.\n+\n+    Notes:\n+        This class inherits from the built-in `ValueError` class and is used to raise exceptions\n+        when an error occurs during the parsing process.\n+    \"\"\"\n+\n     pass\n \n \n@@ -44,6 +51,14 @@ class ParserError(ValueError):\n # _parse_multiformat() and the appropriate error message was not\n # transmitted to the user.\n class ParserMatchError(ParserError):\n+    \"\"\"\n+    This class is a subclass of the ParserError class and is used to raise errors that occur during the matching process.\n+\n+    Notes:\n+        This class is part of the Arrow parser and is used to provide error handling when a parsing match fails.\n+\n+    \"\"\"\n+\n     pass\n \n \n@@ -85,6 +100,29 @@ class ParserMatchError(ParserError):\n \n \n class _Parts(TypedDict, total=False):\n+    \"\"\"\n+    A dictionary that represents different parts of a datetime.\n+\n+    :class:`_Parts` is a TypedDict that represents various components of a date or time,\n+    such as year, month, day, hour, minute, second, microsecond, timestamp, expanded_timestamp, tzinfo,\n+    am_pm, day_of_week, and weekdate.\n+\n+    :ivar year: The year, if present, as an integer.\n+    :ivar month: The month, if present, as an integer.\n+    :ivar day_of_year: The day of the year, if present, as an integer.\n+    :ivar day: The day, if present, as an integer.\n+    :ivar hour: The hour, if present, as an integer.\n+    :ivar minute: The minute, if present, as an integer.\n+    :ivar second: The second, if present, as an integer.\n+    :ivar microsecond: The microsecond, if present, as an integer.\n+    :ivar timestamp: The timestamp, if present, as a float.\n+    :ivar expanded_timestamp: The expanded timestamp, if present, as an integer.\n+    :ivar tzinfo: The timezone info, if present, as a :class:`dt_tzinfo` object.\n+    :ivar am_pm: The AM/PM indicator, if present, as a string literal \"am\" or \"pm\".\n+    :ivar day_of_week: The day of the week, if present, as an integer.\n+    :ivar weekdate: The week date, if present, as a tuple of three integers or None.\n+    \"\"\"\n+\n     year: int\n     month: int\n     day_of_year: int\n@@ -102,6 +140,16 @@ class _Parts(TypedDict, total=False):\n \n \n class DateTimeParser:\n+    \"\"\"A :class:`DateTimeParser <arrow.arrow.parser>` object\n+\n+    Contains the regular expressions and functions to parse and split the input strings into tokens and eventually\n+    produce a datetime that is used by :class:`Arrow <arrow.arrow.Arrow>` internally.\n+\n+    :param locale: the locale string\n+    :param cache_size: the size of the LRU cache used for regular expressions. Defaults to 0.\n+\n+    \"\"\"\n+\n     _FORMAT_RE: ClassVar[Pattern[str]] = re.compile(\n         r\"(YYY?Y?|MM?M?M?|Do|DD?D?D?|d?d?d?d|HH?|hh?|mm?|ss?|S+|ZZ?Z?|a|A|x|X|W)\"\n     )\n@@ -159,6 +207,15 @@ class DateTimeParser:\n     _input_re_map: Dict[_FORMAT_TYPE, Pattern[str]]\n \n     def __init__(self, locale: str = DEFAULT_LOCALE, cache_size: int = 0) -> None:\n+        \"\"\"\n+        Contains the regular expressions and functions to parse and split the input strings into tokens and eventually\n+        produce a datetime that is used by :class:`Arrow <arrow.arrow.Arrow>` internally.\n+\n+        :param locale: the locale string\n+        :type locale: str\n+        :param cache_size: the size of the LRU cache used for regular expressions. Defaults to 0.\n+        :type cache_size: int\n+        \"\"\"\n         self.locale = locales.get_locale(locale)\n         self._input_re_map = self._BASE_INPUT_RE_MAP.copy()\n         self._input_re_map.update(\n@@ -195,6 +252,23 @@ def __init__(self, locale: str = DEFAULT_LOCALE, cache_size: int = 0) -> None:\n     def parse_iso(\n         self, datetime_string: str, normalize_whitespace: bool = False\n     ) -> datetime:\n+        \"\"\"\n+        Parses a datetime string using a ISO 8601-like format.\n+\n+        :param datetime_string: The datetime string to parse.\n+        :param normalize_whitespace: Whether to normalize whitespace in the datetime string (default is False).\n+        :type datetime_string: str\n+        :type normalize_whitespace: bool\n+        :returns: The parsed datetime object.\n+        :rtype: datetime\n+        :raises ParserError: If the datetime string is not in a valid ISO 8601-like format.\n+\n+        Usage::\n+        >>> import arrow.parser\n+        >>> arrow.parser.DateTimeParser().parse_iso('2021-10-12T14:30:00')\n+        datetime.datetime(2021, 10, 12, 14, 30)\n+\n+        \"\"\"\n         if normalize_whitespace:\n             datetime_string = re.sub(r\"\\s+\", \" \", datetime_string.strip())\n \n@@ -302,6 +376,27 @@ def parse(\n         fmt: Union[List[str], str],\n         normalize_whitespace: bool = False,\n     ) -> datetime:\n+        \"\"\"\n+        Parses a datetime string using a specified format.\n+\n+        :param datetime_string: The datetime string to parse.\n+        :param fmt: The format string or list of format strings to use for parsing.\n+        :param normalize_whitespace: Whether to normalize whitespace in the datetime string (default is False).\n+        :type datetime_string: str\n+        :type fmt: Union[List[str], str]\n+        :type normalize_whitespace: bool\n+        :returns: The parsed datetime object.\n+        :rtype: datetime\n+        :raises ParserMatchError: If the datetime string does not match the specified format.\n+\n+        Usage::\n+\n+        >>> import arrow.parser\n+        >>> arrow.parser.DateTimeParser().parse('2021-10-12 14:30:00', 'YYYY-MM-DD HH:mm:ss')\n+        datetime.datetime(2021, 10, 12, 14, 30)\n+\n+\n+        \"\"\"\n         if normalize_whitespace:\n             datetime_string = re.sub(r\"\\s+\", \" \", datetime_string)\n \n@@ -344,6 +439,15 @@ def parse(\n         return self._build_datetime(parts)\n \n     def _generate_pattern_re(self, fmt: str) -> Tuple[List[_FORMAT_TYPE], Pattern[str]]:\n+        \"\"\"\n+        Generates a regular expression pattern from a format string.\n+\n+        :param fmt: The format string to convert into a regular expression pattern.\n+        :type fmt: str\n+        :returns: A tuple containing a list of format tokens and the corresponding regular expression pattern.\n+        :rtype: Tuple[List[_FORMAT_TYPE], Pattern[str]]\n+        :raises ParserError: If an unrecognized token is encountered in the format string.\n+        \"\"\"\n         # fmt is a string of tokens like 'YYYY-MM-DD'\n         # we construct a new string by replacing each\n         # token by its pattern:\n@@ -450,51 +554,60 @@ def _parse_token(\n         ],\n         value: Union[str, bytes, SupportsInt, bytearray],\n         parts: _Parts,\n-    ) -> None:\n-        ...  # pragma: no cover\n+    ) -> None: ...  # pragma: no cover\n \n     @overload\n     def _parse_token(\n         self,\n         token: Literal[\"X\"],\n         value: Union[str, bytes, SupportsFloat, bytearray],\n         parts: _Parts,\n-    ) -> None:\n-        ...  # pragma: no cover\n+    ) -> None: ...  # pragma: no cover\n \n     @overload\n     def _parse_token(\n         self,\n         token: Literal[\"MMMM\", \"MMM\", \"dddd\", \"ddd\", \"S\"],\n         value: Union[str, bytes, bytearray],\n         parts: _Parts,\n-    ) -> None:\n-        ...  # pragma: no cover\n+    ) -> None: ...  # pragma: no cover\n \n     @overload\n     def _parse_token(\n         self,\n         token: Literal[\"a\", \"A\", \"ZZZ\", \"ZZ\", \"Z\"],\n         value: Union[str, bytes],\n         parts: _Parts,\n-    ) -> None:\n-        ...  # pragma: no cover\n+    ) -> None: ...  # pragma: no cover\n \n     @overload\n     def _parse_token(\n         self,\n         token: Literal[\"W\"],\n         value: Tuple[_WEEKDATE_ELEMENT, _WEEKDATE_ELEMENT, Optional[_WEEKDATE_ELEMENT]],\n         parts: _Parts,\n-    ) -> None:\n-        ...  # pragma: no cover\n+    ) -> None: ...  # pragma: no cover\n \n     def _parse_token(\n         self,\n         token: Any,\n         value: Any,\n         parts: _Parts,\n     ) -> None:\n+        \"\"\"\n+        Parse a token and its value, and update the `_Parts` dictionary with the parsed values.\n+\n+        The function supports several tokens, including \"YYYY\", \"YY\", \"MMMM\", \"MMM\", \"MM\", \"M\", \"DDDD\", \"DDD\", \"DD\", \"D\", \"Do\", \"dddd\", \"ddd\", \"HH\", \"H\", \"mm\", \"m\", \"ss\", \"s\", \"S\", \"X\", \"x\", \"ZZZ\", \"ZZ\", \"Z\", \"a\", \"A\", and \"W\". Each token is matched and the corresponding value is parsed and added to the `_Parts` dictionary.\n+\n+        :param token: The token to parse.\n+        :type token: Any\n+        :param value: The value of the token.\n+        :type value: Any\n+        :param parts: A dictionary to update with the parsed values.\n+        :type parts: _Parts\n+        :raises ParserMatchError: If the hour token value is not between 0 and 12 inclusive for tokens \"a\" or \"A\".\n+\n+        \"\"\"\n         if token == \"YYYY\":\n             parts[\"year\"] = int(value)\n \n@@ -581,6 +694,14 @@ def _parse_token(\n \n     @staticmethod\n     def _build_datetime(parts: _Parts) -> datetime:\n+        \"\"\"\n+        Build a datetime object from a dictionary of date parts.\n+\n+        :param parts: A dictionary containing the date parts extracted from a date string.\n+        :type parts: dict\n+        :return: A datetime object representing the date and time.\n+        :rtype: datetime.datetime\n+        \"\"\"\n         weekdate = parts.get(\"weekdate\")\n \n         if weekdate is not None:\n@@ -604,14 +725,14 @@ def _build_datetime(parts: _Parts) -> datetime:\n         timestamp = parts.get(\"timestamp\")\n \n         if timestamp is not None:\n-            return datetime.fromtimestamp(timestamp, tz=tz.tzutc())\n+            return datetime.fromtimestamp(timestamp, tz=timezone.utc)\n \n         expanded_timestamp = parts.get(\"expanded_timestamp\")\n \n         if expanded_timestamp is not None:\n             return datetime.fromtimestamp(\n                 normalize_timestamp(expanded_timestamp),\n-                tz=tz.tzutc(),\n+                tz=timezone.utc,\n             )\n \n         day_of_year = parts.get(\"day_of_year\")\n@@ -707,6 +828,21 @@ def _build_datetime(parts: _Parts) -> datetime:\n         )\n \n     def _parse_multiformat(self, string: str, formats: Iterable[str]) -> datetime:\n+        \"\"\"\n+        Parse a date and time string using multiple formats.\n+\n+        Tries to parse the provided string with each format in the given `formats`\n+        iterable, returning the resulting `datetime` object if a match is found. If no\n+        format matches the string, a `ParserError` is raised.\n+\n+        :param string: The date and time string to parse.\n+        :type string: str\n+        :param formats: An iterable of date and time format strings to try, in order.\n+        :type formats: Iterable[str]\n+        :returns: The parsed date and time.\n+        :rtype: datetime.datetime\n+        :raises ParserError: If no format matches the input string.\n+        \"\"\"\n         _datetime: Optional[datetime] = None\n \n         for fmt in formats:\n@@ -729,23 +865,50 @@ def _parse_multiformat(self, string: str, formats: Iterable[str]) -> datetime:\n     def _generate_choice_re(\n         choices: Iterable[str], flags: Union[int, re.RegexFlag] = 0\n     ) -> Pattern[str]:\n+        \"\"\"\n+        Generate a regular expression pattern that matches a choice from an iterable.\n+\n+        Takes an iterable of strings (`choices`) and returns a compiled regular expression\n+        pattern that matches any of the choices. The pattern is created by joining the\n+        choices with the '|' (OR) operator, which matches any of the enclosed patterns.\n+\n+        :param choices: An iterable of strings to match.\n+        :type choices: Iterable[str]\n+        :param flags: Optional regular expression flags. Default is 0.\n+        :type flags: Union[int, re.RegexFlag], optional\n+        :returns: A compiled regular expression pattern that matches any of the choices.\n+        :rtype: re.Pattern[str]\n+        \"\"\"\n         return re.compile(r\"({})\".format(\"|\".join(choices)), flags=flags)\n \n \n class TzinfoParser:\n+    \"\"\"\n+    Parser for timezone information.\n+    \"\"\"\n+\n     _TZINFO_RE: ClassVar[Pattern[str]] = re.compile(\n         r\"^(?:\\(UTC)*([\\+\\-])?(\\d{2})(?:\\:?(\\d{2}))?\"\n     )\n \n     @classmethod\n     def parse(cls, tzinfo_string: str) -> dt_tzinfo:\n+        \"\"\"\n+        Parse a timezone string and return a datetime timezone object.\n+\n+        :param tzinfo_string: The timezone string to parse.\n+        :type tzinfo_string: str\n+        :returns: The parsed datetime timezone object.\n+        :rtype: datetime.timezone\n+        :raises ParserError: If the timezone string cannot be parsed.\n+        \"\"\"\n         tzinfo: Optional[dt_tzinfo] = None\n \n         if tzinfo_string == \"local\":\n-            tzinfo = tz.tzlocal()\n+            tzinfo = datetime.now().astimezone().tzinfo\n \n         elif tzinfo_string in [\"utc\", \"UTC\", \"Z\"]:\n-            tzinfo = tz.tzutc()\n+            tzinfo = timezone.utc\n \n         else:\n             iso_match = cls._TZINFO_RE.match(tzinfo_string)\n@@ -760,10 +923,13 @@ def parse(cls, tzinfo_string: str) -> dt_tzinfo:\n                 if sign == \"-\":\n                     seconds *= -1\n \n-                tzinfo = tz.tzoffset(None, seconds)\n+                tzinfo = timezone(timedelta(seconds=seconds))\n \n             else:\n-                tzinfo = tz.gettz(tzinfo_string)\n+                try:\n+                    tzinfo = ZoneInfo(tzinfo_string)\n+                except ZoneInfoNotFoundError:\n+                    tzinfo = None\n \n         if tzinfo is None:\n             raise ParserError(f\"Could not parse timezone expression {tzinfo_string!r}.\")"}, {"sha": "73fbfca6b36927c6371839df3dfe733bebb2066f", "filename": "lib/autocommand/__init__.py", "status": "removed", "additions": 0, "deletions": 27, "changes": 27, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fautocommand%2F__init__.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,27 +0,0 @@\n-# Copyright 2014-2016 Nathan West\n-#\n-# This file is part of autocommand.\n-#\n-# autocommand is free software: you can redistribute it and/or modify\n-# it under the terms of the GNU Lesser General Public License as published by\n-# the Free Software Foundation, either version 3 of the License, or\n-# (at your option) any later version.\n-#\n-# autocommand is distributed in the hope that it will be useful,\n-# but WITHOUT ANY WARRANTY; without even the implied warranty of\n-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-# GNU Lesser General Public License for more details.\n-#\n-# You should have received a copy of the GNU Lesser General Public License\n-# along with autocommand.  If not, see <http://www.gnu.org/licenses/>.\n-\n-# flake8 flags all these imports as unused, hence the NOQAs everywhere.\n-\n-from .automain import automain  # NOQA\n-from .autoparse import autoparse, smart_open  # NOQA\n-from .autocommand import autocommand  # NOQA\n-\n-try:\n-    from .autoasync import autoasync  # NOQA\n-except ImportError:  # pragma: no cover\n-    pass"}, {"sha": "688f7e05544bb5b4914d290c9bfe73bb99f4e06a", "filename": "lib/autocommand/autoasync.py", "status": "removed", "additions": 0, "deletions": 142, "changes": 142, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Fautoasync.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Fautoasync.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fautocommand%2Fautoasync.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,142 +0,0 @@\n-# Copyright 2014-2015 Nathan West\n-#\n-# This file is part of autocommand.\n-#\n-# autocommand is free software: you can redistribute it and/or modify\n-# it under the terms of the GNU Lesser General Public License as published by\n-# the Free Software Foundation, either version 3 of the License, or\n-# (at your option) any later version.\n-#\n-# autocommand is distributed in the hope that it will be useful,\n-# but WITHOUT ANY WARRANTY; without even the implied warranty of\n-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-# GNU Lesser General Public License for more details.\n-#\n-# You should have received a copy of the GNU Lesser General Public License\n-# along with autocommand.  If not, see <http://www.gnu.org/licenses/>.\n-\n-from asyncio import get_event_loop, iscoroutine\n-from functools import wraps\n-from inspect import signature\n-\n-\n-async def _run_forever_coro(coro, args, kwargs, loop):\n-    '''\n-    This helper function launches an async main function that was tagged with\n-    forever=True. There are two possibilities:\n-\n-    - The function is a normal function, which handles initializing the event\n-      loop, which is then run forever\n-    - The function is a coroutine, which needs to be scheduled in the event\n-      loop, which is then run forever\n-      - There is also the possibility that the function is a normal function\n-        wrapping a coroutine function\n-\n-    The function is therefore called unconditionally and scheduled in the event\n-    loop if the return value is a coroutine object.\n-\n-    The reason this is a separate function is to make absolutely sure that all\n-    the objects created are garbage collected after all is said and done; we\n-    do this to ensure that any exceptions raised in the tasks are collected\n-    ASAP.\n-    '''\n-\n-    # Personal note: I consider this an antipattern, as it relies on the use of\n-    # unowned resources. The setup function dumps some stuff into the event\n-    # loop where it just whirls in the ether without a well defined owner or\n-    # lifetime. For this reason, there's a good chance I'll remove the\n-    # forever=True feature from autoasync at some point in the future.\n-    thing = coro(*args, **kwargs)\n-    if iscoroutine(thing):\n-        await thing\n-\n-\n-def autoasync(coro=None, *, loop=None, forever=False, pass_loop=False):\n-    '''\n-    Convert an asyncio coroutine into a function which, when called, is\n-    evaluted in an event loop, and the return value returned. This is intented\n-    to make it easy to write entry points into asyncio coroutines, which\n-    otherwise need to be explictly evaluted with an event loop's\n-    run_until_complete.\n-\n-    If `loop` is given, it is used as the event loop to run the coro in. If it\n-    is None (the default), the loop is retreived using asyncio.get_event_loop.\n-    This call is defered until the decorated function is called, so that\n-    callers can install custom event loops or event loop policies after\n-    @autoasync is applied.\n-\n-    If `forever` is True, the loop is run forever after the decorated coroutine\n-    is finished. Use this for servers created with asyncio.start_server and the\n-    like.\n-\n-    If `pass_loop` is True, the event loop object is passed into the coroutine\n-    as the `loop` kwarg when the wrapper function is called. In this case, the\n-    wrapper function's __signature__ is updated to remove this parameter, so\n-    that autoparse can still be used on it without generating a parameter for\n-    `loop`.\n-\n-    This coroutine can be called with ( @autoasync(...) ) or without\n-    ( @autoasync ) arguments.\n-\n-    Examples:\n-\n-    @autoasync\n-    def get_file(host, port):\n-        reader, writer = yield from asyncio.open_connection(host, port)\n-        data = reader.read()\n-        sys.stdout.write(data.decode())\n-\n-    get_file(host, port)\n-\n-    @autoasync(forever=True, pass_loop=True)\n-    def server(host, port, loop):\n-        yield_from loop.create_server(Proto, host, port)\n-\n-    server('localhost', 8899)\n-\n-    '''\n-    if coro is None:\n-        return lambda c: autoasync(\n-            c, loop=loop,\n-            forever=forever,\n-            pass_loop=pass_loop)\n-\n-    # The old and new signatures are required to correctly bind the loop\n-    # parameter in 100% of cases, even if it's a positional parameter.\n-    # NOTE: A future release will probably require the loop parameter to be\n-    # a kwonly parameter.\n-    if pass_loop:\n-        old_sig = signature(coro)\n-        new_sig = old_sig.replace(parameters=(\n-            param for name, param in old_sig.parameters.items()\n-            if name != \"loop\"))\n-\n-    @wraps(coro)\n-    def autoasync_wrapper(*args, **kwargs):\n-        # Defer the call to get_event_loop so that, if a custom policy is\n-        # installed after the autoasync decorator, it is respected at call time\n-        local_loop = get_event_loop() if loop is None else loop\n-\n-        # Inject the 'loop' argument. We have to use this signature binding to\n-        # ensure it's injected in the correct place (positional, keyword, etc)\n-        if pass_loop:\n-            bound_args = old_sig.bind_partial()\n-            bound_args.arguments.update(\n-                loop=local_loop,\n-                **new_sig.bind(*args, **kwargs).arguments)\n-            args, kwargs = bound_args.args, bound_args.kwargs\n-\n-        if forever:\n-            local_loop.create_task(_run_forever_coro(\n-                coro, args, kwargs, local_loop\n-            ))\n-            local_loop.run_forever()\n-        else:\n-            return local_loop.run_until_complete(coro(*args, **kwargs))\n-\n-    # Attach the updated signature. This allows 'pass_loop' to be used with\n-    # autoparse\n-    if pass_loop:\n-        autoasync_wrapper.__signature__ = new_sig\n-\n-    return autoasync_wrapper"}, {"sha": "097e86de0720126a1390d728f7e50a4f89cac77f", "filename": "lib/autocommand/autocommand.py", "status": "removed", "additions": 0, "deletions": 70, "changes": 70, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Fautocommand.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Fautocommand.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fautocommand%2Fautocommand.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,70 +0,0 @@\n-# Copyright 2014-2015 Nathan West\n-#\n-# This file is part of autocommand.\n-#\n-# autocommand is free software: you can redistribute it and/or modify\n-# it under the terms of the GNU Lesser General Public License as published by\n-# the Free Software Foundation, either version 3 of the License, or\n-# (at your option) any later version.\n-#\n-# autocommand is distributed in the hope that it will be useful,\n-# but WITHOUT ANY WARRANTY; without even the implied warranty of\n-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-# GNU Lesser General Public License for more details.\n-#\n-# You should have received a copy of the GNU Lesser General Public License\n-# along with autocommand.  If not, see <http://www.gnu.org/licenses/>.\n-\n-from .autoparse import autoparse\n-from .automain import automain\n-try:\n-    from .autoasync import autoasync\n-except ImportError:  # pragma: no cover\n-    pass\n-\n-\n-def autocommand(\n-        module, *,\n-        description=None,\n-        epilog=None,\n-        add_nos=False,\n-        parser=None,\n-        loop=None,\n-        forever=False,\n-        pass_loop=False):\n-\n-    if callable(module):\n-        raise TypeError('autocommand requires a module name argument')\n-\n-    def autocommand_decorator(func):\n-        # Step 1: if requested, run it all in an asyncio event loop. autoasync\n-        # patches the __signature__ of the decorated function, so that in the\n-        # event that pass_loop is True, the `loop` parameter of the original\n-        # function will *not* be interpreted as a command-line argument by\n-        # autoparse\n-        if loop is not None or forever or pass_loop:\n-            func = autoasync(\n-                func,\n-                loop=None if loop is True else loop,\n-                pass_loop=pass_loop,\n-                forever=forever)\n-\n-        # Step 2: create parser. We do this second so that the arguments are\n-        # parsed and passed *before* entering the asyncio event loop, if it\n-        # exists. This simplifies the stack trace and ensures errors are\n-        # reported earlier. It also ensures that errors raised during parsing &\n-        # passing are still raised if `forever` is True.\n-        func = autoparse(\n-            func,\n-            description=description,\n-            epilog=epilog,\n-            add_nos=add_nos,\n-            parser=parser)\n-\n-        # Step 3: call the function automatically if __name__ == '__main__' (or\n-        # if True was provided)\n-        func = automain(module)(func)\n-\n-        return func\n-\n-    return autocommand_decorator"}, {"sha": "6cc45db66a1c1dbf1bedb993683b0700d0510643", "filename": "lib/autocommand/automain.py", "status": "removed", "additions": 0, "deletions": 59, "changes": 59, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Fautomain.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Fautomain.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fautocommand%2Fautomain.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,59 +0,0 @@\n-# Copyright 2014-2015 Nathan West\n-#\n-# This file is part of autocommand.\n-#\n-# autocommand is free software: you can redistribute it and/or modify\n-# it under the terms of the GNU Lesser General Public License as published by\n-# the Free Software Foundation, either version 3 of the License, or\n-# (at your option) any later version.\n-#\n-# autocommand is distributed in the hope that it will be useful,\n-# but WITHOUT ANY WARRANTY; without even the implied warranty of\n-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-# GNU Lesser General Public License for more details.\n-#\n-# You should have received a copy of the GNU Lesser General Public License\n-# along with autocommand.  If not, see <http://www.gnu.org/licenses/>.\n-\n-import sys\n-from .errors import AutocommandError\n-\n-\n-class AutomainRequiresModuleError(AutocommandError, TypeError):\n-    pass\n-\n-\n-def automain(module, *, args=(), kwargs=None):\n-    '''\n-    This decorator automatically invokes a function if the module is being run\n-    as the \"__main__\" module. Optionally, provide args or kwargs with which to\n-    call the function. If `module` is \"__main__\", the function is called, and\n-    the program is `sys.exit`ed with the return value. You can also pass `True`\n-    to cause the function to be called unconditionally. If the function is not\n-    called, it is returned unchanged by the decorator.\n-\n-    Usage:\n-\n-    @automain(__name__)  # Pass __name__ to check __name__==\"__main__\"\n-    def main():\n-        ...\n-\n-    If __name__ is \"__main__\" here, the main function is called, and then\n-    sys.exit called with the return value.\n-    '''\n-\n-    # Check that @automain(...) was called, rather than @automain\n-    if callable(module):\n-        raise AutomainRequiresModuleError(module)\n-\n-    if module == '__main__' or module is True:\n-        if kwargs is None:\n-            kwargs = {}\n-\n-        # Use a function definition instead of a lambda for a neater traceback\n-        def automain_decorator(main):\n-            sys.exit(main(*args, **kwargs))\n-\n-        return automain_decorator\n-    else:\n-        return lambda main: main"}, {"sha": "0276a3fae10eb367af326543bcd69e62432b2f7b", "filename": "lib/autocommand/autoparse.py", "status": "removed", "additions": 0, "deletions": 333, "changes": 333, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Fautoparse.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Fautoparse.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fautocommand%2Fautoparse.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,333 +0,0 @@\n-# Copyright 2014-2015 Nathan West\n-#\n-# This file is part of autocommand.\n-#\n-# autocommand is free software: you can redistribute it and/or modify\n-# it under the terms of the GNU Lesser General Public License as published by\n-# the Free Software Foundation, either version 3 of the License, or\n-# (at your option) any later version.\n-#\n-# autocommand is distributed in the hope that it will be useful,\n-# but WITHOUT ANY WARRANTY; without even the implied warranty of\n-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-# GNU Lesser General Public License for more details.\n-#\n-# You should have received a copy of the GNU Lesser General Public License\n-# along with autocommand.  If not, see <http://www.gnu.org/licenses/>.\n-\n-import sys\n-from re import compile as compile_regex\n-from inspect import signature, getdoc, Parameter\n-from argparse import ArgumentParser\n-from contextlib import contextmanager\n-from functools import wraps\n-from io import IOBase\n-from autocommand.errors import AutocommandError\n-\n-\n-_empty = Parameter.empty\n-\n-\n-class AnnotationError(AutocommandError):\n-    '''Annotation error: annotation must be a string, type, or tuple of both'''\n-\n-\n-class PositionalArgError(AutocommandError):\n-    '''\n-    Postional Arg Error: autocommand can't handle postional-only parameters\n-    '''\n-\n-\n-class KWArgError(AutocommandError):\n-    '''kwarg Error: autocommand can't handle a **kwargs parameter'''\n-\n-\n-class DocstringError(AutocommandError):\n-    '''Docstring error'''\n-\n-\n-class TooManySplitsError(DocstringError):\n-    '''\n-    The docstring had too many ---- section splits. Currently we only support\n-    using up to a single split, to split the docstring into description and\n-    epilog parts.\n-    '''\n-\n-\n-def _get_type_description(annotation):\n-    '''\n-    Given an annotation, return the (type, description) for the parameter.\n-    If you provide an annotation that is somehow both a string and a callable,\n-    the behavior is undefined.\n-    '''\n-    if annotation is _empty:\n-        return None, None\n-    elif callable(annotation):\n-        return annotation, None\n-    elif isinstance(annotation, str):\n-        return None, annotation\n-    elif isinstance(annotation, tuple):\n-        try:\n-            arg1, arg2 = annotation\n-        except ValueError as e:\n-            raise AnnotationError(annotation) from e\n-        else:\n-            if callable(arg1) and isinstance(arg2, str):\n-                return arg1, arg2\n-            elif isinstance(arg1, str) and callable(arg2):\n-                return arg2, arg1\n-\n-    raise AnnotationError(annotation)\n-\n-\n-def _add_arguments(param, parser, used_char_args, add_nos):\n-    '''\n-    Add the argument(s) to an ArgumentParser (using add_argument) for a given\n-    parameter. used_char_args is the set of -short options currently already in\n-    use, and is updated (if necessary) by this function. If add_nos is True,\n-    this will also add an inverse switch for all boolean options. For\n-    instance, for the boolean parameter \"verbose\", this will create --verbose\n-    and --no-verbose.\n-    '''\n-\n-    # Impl note: This function is kept separate from make_parser because it's\n-    # already very long and I wanted to separate out as much as possible into\n-    # its own call scope, to prevent even the possibility of suble mutation\n-    # bugs.\n-    if param.kind is param.POSITIONAL_ONLY:\n-        raise PositionalArgError(param)\n-    elif param.kind is param.VAR_KEYWORD:\n-        raise KWArgError(param)\n-\n-    # These are the kwargs for the add_argument function.\n-    arg_spec = {}\n-    is_option = False\n-\n-    # Get the type and default from the annotation.\n-    arg_type, description = _get_type_description(param.annotation)\n-\n-    # Get the default value\n-    default = param.default\n-\n-    # If there is no explicit type, and the default is present and not None,\n-    # infer the type from the default.\n-    if arg_type is None and default not in {_empty, None}:\n-        arg_type = type(default)\n-\n-    # Add default. The presence of a default means this is an option, not an\n-    # argument.\n-    if default is not _empty:\n-        arg_spec['default'] = default\n-        is_option = True\n-\n-    # Add the type\n-    if arg_type is not None:\n-        # Special case for bool: make it just a --switch\n-        if arg_type is bool:\n-            if not default or default is _empty:\n-                arg_spec['action'] = 'store_true'\n-            else:\n-                arg_spec['action'] = 'store_false'\n-\n-            # Switches are always options\n-            is_option = True\n-\n-        # Special case for file types: make it a string type, for filename\n-        elif isinstance(default, IOBase):\n-            arg_spec['type'] = str\n-\n-        # TODO: special case for list type.\n-        #   - How to specificy type of list members?\n-        #       - param: [int]\n-        #       - param: int =[]\n-        #   - action='append' vs nargs='*'\n-\n-        else:\n-            arg_spec['type'] = arg_type\n-\n-    # nargs: if the signature includes *args, collect them as trailing CLI\n-    # arguments in a list. *args can't have a default value, so it can never be\n-    # an option.\n-    if param.kind is param.VAR_POSITIONAL:\n-        # TODO: consider depluralizing metavar/name here.\n-        arg_spec['nargs'] = '*'\n-\n-    # Add description.\n-    if description is not None:\n-        arg_spec['help'] = description\n-\n-    # Get the --flags\n-    flags = []\n-    name = param.name\n-\n-    if is_option:\n-        # Add the first letter as a -short option.\n-        for letter in name[0], name[0].swapcase():\n-            if letter not in used_char_args:\n-                used_char_args.add(letter)\n-                flags.append('-{}'.format(letter))\n-                break\n-\n-        # If the parameter is a --long option, or is a -short option that\n-        # somehow failed to get a flag, add it.\n-        if len(name) > 1 or not flags:\n-            flags.append('--{}'.format(name))\n-\n-        arg_spec['dest'] = name\n-    else:\n-        flags.append(name)\n-\n-    parser.add_argument(*flags, **arg_spec)\n-\n-    # Create the --no- version for boolean switches\n-    if add_nos and arg_type is bool:\n-        parser.add_argument(\n-            '--no-{}'.format(name),\n-            action='store_const',\n-            dest=name,\n-            const=default if default is not _empty else False)\n-\n-\n-def make_parser(func_sig, description, epilog, add_nos):\n-    '''\n-    Given the signature of a function, create an ArgumentParser\n-    '''\n-    parser = ArgumentParser(description=description, epilog=epilog)\n-\n-    used_char_args = {'h'}\n-\n-    # Arange the params so that single-character arguments are first. This\n-    # esnures they don't have to get --long versions. sorted is stable, so the\n-    # parameters will otherwise still be in relative order.\n-    params = sorted(\n-        func_sig.parameters.values(),\n-        key=lambda param: len(param.name) > 1)\n-\n-    for param in params:\n-        _add_arguments(param, parser, used_char_args, add_nos)\n-\n-    return parser\n-\n-\n-_DOCSTRING_SPLIT = compile_regex(r'\\n\\s*-{4,}\\s*\\n')\n-\n-\n-def parse_docstring(docstring):\n-    '''\n-    Given a docstring, parse it into a description and epilog part\n-    '''\n-    if docstring is None:\n-        return '', ''\n-\n-    parts = _DOCSTRING_SPLIT.split(docstring)\n-\n-    if len(parts) == 1:\n-        return docstring, ''\n-    elif len(parts) == 2:\n-        return parts[0], parts[1]\n-    else:\n-        raise TooManySplitsError()\n-\n-\n-def autoparse(\n-        func=None, *,\n-        description=None,\n-        epilog=None,\n-        add_nos=False,\n-        parser=None):\n-    '''\n-    This decorator converts a function that takes normal arguments into a\n-    function which takes a single optional argument, argv, parses it using an\n-    argparse.ArgumentParser, and calls the underlying function with the parsed\n-    arguments. If it is not given, sys.argv[1:] is used. This is so that the\n-    function can be used as a setuptools entry point, as well as a normal main\n-    function. sys.argv[1:] is not evaluated until the function is called, to\n-    allow injecting different arguments for testing.\n-\n-    It uses the argument signature of the function to create an\n-    ArgumentParser. Parameters without defaults become positional parameters,\n-    while parameters *with* defaults become --options. Use annotations to set\n-    the type of the parameter.\n-\n-    The `desctiption` and `epilog` parameters corrospond to the same respective\n-    argparse parameters. If no description is given, it defaults to the\n-    decorated functions's docstring, if present.\n-\n-    If add_nos is True, every boolean option (that is, every parameter with a\n-    default of True/False or a type of bool) will have a --no- version created\n-    as well, which inverts the option. For instance, the --verbose option will\n-    have a --no-verbose counterpart. These are not mutually exclusive-\n-    whichever one appears last in the argument list will have precedence.\n-\n-    If a parser is given, it is used instead of one generated from the function\n-    signature. In this case, no parser is created; instead, the given parser is\n-    used to parse the argv argument. The parser's results' argument names must\n-    match up with the parameter names of the decorated function.\n-\n-    The decorated function is attached to the result as the `func` attribute,\n-    and the parser is attached as the `parser` attribute.\n-    '''\n-\n-    # If @autoparse(...) is used instead of @autoparse\n-    if func is None:\n-        return lambda f: autoparse(\n-            f, description=description,\n-            epilog=epilog,\n-            add_nos=add_nos,\n-            parser=parser)\n-\n-    func_sig = signature(func)\n-\n-    docstr_description, docstr_epilog = parse_docstring(getdoc(func))\n-\n-    if parser is None:\n-        parser = make_parser(\n-            func_sig,\n-            description or docstr_description,\n-            epilog or docstr_epilog,\n-            add_nos)\n-\n-    @wraps(func)\n-    def autoparse_wrapper(argv=None):\n-        if argv is None:\n-            argv = sys.argv[1:]\n-\n-        # Get empty argument binding, to fill with parsed arguments. This\n-        # object does all the heavy lifting of turning named arguments into\n-        # into correctly bound *args and **kwargs.\n-        parsed_args = func_sig.bind_partial()\n-        parsed_args.arguments.update(vars(parser.parse_args(argv)))\n-\n-        return func(*parsed_args.args, **parsed_args.kwargs)\n-\n-    # TODO: attach an updated __signature__ to autoparse_wrapper, just in case.\n-\n-    # Attach the wrapped function and parser, and return the wrapper.\n-    autoparse_wrapper.func = func\n-    autoparse_wrapper.parser = parser\n-    return autoparse_wrapper\n-\n-\n-@contextmanager\n-def smart_open(filename_or_file, *args, **kwargs):\n-    '''\n-    This context manager allows you to open a filename, if you want to default\n-    some already-existing file object, like sys.stdout, which shouldn't be\n-    closed at the end of the context. If the filename argument is a str, bytes,\n-    or int, the file object is created via a call to open with the given *args\n-    and **kwargs, sent to the context, and closed at the end of the context,\n-    just like \"with open(filename) as f:\". If it isn't one of the openable\n-    types, the object simply sent to the context unchanged, and left unclosed\n-    at the end of the context. Example:\n-\n-        def work_with_file(name=sys.stdout):\n-            with smart_open(name) as f:\n-                # Works correctly if name is a str filename or sys.stdout\n-                print(\"Some stuff\", file=f)\n-                # If it was a filename, f is closed at the end here.\n-    '''\n-    if isinstance(filename_or_file, (str, bytes, int)):\n-        with open(filename_or_file, *args, **kwargs) as file:\n-            yield file\n-    else:\n-        yield filename_or_file"}, {"sha": "2570607399a3ae13cb92db65a9171d955d3248c6", "filename": "lib/autocommand/errors.py", "status": "removed", "additions": 0, "deletions": 23, "changes": 23, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Ferrors.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fautocommand%2Ferrors.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fautocommand%2Ferrors.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,23 +0,0 @@\n-# Copyright 2014-2016 Nathan West\n-#\n-# This file is part of autocommand.\n-#\n-# autocommand is free software: you can redistribute it and/or modify\n-# it under the terms of the GNU Lesser General Public License as published by\n-# the Free Software Foundation, either version 3 of the License, or\n-# (at your option) any later version.\n-#\n-# autocommand is distributed in the hope that it will be useful,\n-# but WITHOUT ANY WARRANTY; without even the implied warranty of\n-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-# GNU Lesser General Public License for more details.\n-#\n-# You should have received a copy of the GNU Lesser General Public License\n-# along with autocommand.  If not, see <http://www.gnu.org/licenses/>.\n-\n-\n-class AutocommandError(Exception):\n-    '''Base class for autocommand exceptions'''\n-    pass\n-\n-# Individual modules will define errors specific to that module."}, {"sha": "80f1f0b7065bf8c3340b161c1f726ae68d0c8260", "filename": "lib/backports/cached_property/__init__.py", "status": "removed", "additions": 0, "deletions": 94, "changes": 94, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbackports%2Fcached_property%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbackports%2Fcached_property%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbackports%2Fcached_property%2F__init__.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,94 +0,0 @@\n-\"\"\"Backport of python 3.8 functools.cached_property.\n-\n-cached_property() - computed once per instance, cached as attribute\n-\"\"\"\n-\n-__all__ = (\"cached_property\",)\n-\n-# Standard Library\n-from sys import version_info\n-try:\n-    # Local Implementation\n-    from ._version import version as __version__\n-except ImportError:\n-    pass\n-\n-if version_info >= (3, 8):\n-    # Standard Library\n-    from functools import cached_property  # pylint: disable=no-name-in-module\n-else:\n-    # Standard Library\n-    from threading import RLock\n-    from typing import Any\n-    from typing import Callable\n-    from typing import Optional\n-    from typing import Type\n-    from typing import TypeVar\n-\n-    _NOT_FOUND = object()\n-    _T = TypeVar(\"_T\")\n-    _S = TypeVar(\"_S\")\n-\n-    # noinspection PyPep8Naming\n-    class cached_property:  # NOSONAR  # pylint: disable=invalid-name  # noqa: N801\n-        \"\"\"Cached property implementation.\n-\n-        Transform a method of a class into a property whose value is computed once\n-        and then cached as a normal attribute for the life of the instance.\n-        Similar to property(), with the addition of caching.\n-        Useful for expensive computed properties of instances\n-        that are otherwise effectively immutable.\n-        \"\"\"\n-\n-        def __init__(self, func: Callable[[Any], _T]) -> None:\n-            \"\"\"Cached property implementation.\"\"\"\n-            self.func = func\n-            self.attrname: Optional[str] = None\n-            self.__doc__ = func.__doc__\n-            self.lock = RLock()\n-\n-        def __set_name__(self, owner: Type[Any], name: str) -> None:\n-            \"\"\"Assign attribute name and owner.\"\"\"\n-            if self.attrname is None:\n-                self.attrname = name\n-            elif name != self.attrname:\n-                raise TypeError(\n-                    \"Cannot assign the same cached_property to two different names \"\n-                    f\"({self.attrname!r} and {name!r}).\"\n-                )\n-\n-        def __get__(self, instance: Optional[_S], owner: Optional[Type[Any]] = None) -> Any:\n-            \"\"\"Property-like getter implementation.\n-\n-            :return: property instance if requested on class or value/cached value if requested on instance.\n-            :rtype: Union[cached_property[_T], _T]\n-            :raises TypeError: call without calling __set_name__ or no '__dict__' attribute\n-            \"\"\"\n-            if instance is None:\n-                return self\n-            if self.attrname is None:\n-                raise TypeError(\"Cannot use cached_property instance without calling __set_name__ on it.\")\n-            try:\n-                cache = instance.__dict__\n-            except AttributeError:  # not all objects have __dict__ (e.g. class defines slots)\n-                msg = (\n-                    f\"No '__dict__' attribute on {type(instance).__name__!r} \"\n-                    f\"instance to cache {self.attrname!r} property.\"\n-                )\n-                raise TypeError(msg) from None\n-            val = cache.get(self.attrname, _NOT_FOUND)\n-            if val is _NOT_FOUND:\n-                with self.lock:\n-                    # check if another thread filled cache while we awaited lock\n-                    val = cache.get(self.attrname, _NOT_FOUND)\n-                    if val is _NOT_FOUND:\n-                        val = self.func(instance)\n-                        try:\n-                            cache[self.attrname] = val\n-                        except TypeError:\n-                            msg = (\n-                                f\"The '__dict__' attribute on {type(instance).__name__!r} instance \"\n-                                f\"does not support item assignment for caching {self.attrname!r} property.\"\n-                            )\n-                            raise TypeError(msg) from None\n-            return val"}, {"sha": "13e619ba681eb9f4283666975e93100513489126", "filename": "lib/backports/cached_property/__init__.pyi", "status": "removed", "additions": 0, "deletions": 24, "changes": 24, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbackports%2Fcached_property%2F__init__.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbackports%2Fcached_property%2F__init__.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbackports%2Fcached_property%2F__init__.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,24 +0,0 @@\n-# Standard Library\n-from threading import RLock\n-from typing import Any\n-from typing import Callable\n-from typing import Generic\n-from typing import Optional\n-from typing import Type\n-from typing import TypeVar\n-from typing import overload\n-\n-_T = TypeVar(\"_T\")\n-_S = TypeVar(\"_S\")\n-\n-# noinspection PyPep8Naming\n-class cached_property(Generic[_T]):\n-    func: Callable[[Any], _T]\n-    attrname: Optional[str]\n-    lock: RLock\n-    def __init__(self, func: Callable[[Any], _T]) -> None: ...\n-    @overload\n-    def __get__(self, instance: None, owner: Optional[Type[Any]] = ...) -> cached_property[_T]: ...\n-    @overload\n-    def __get__(self, instance: _S, owner: Optional[Type[Any]] = ...) -> _T: ...\n-    def __set_name__(self, owner: Type[Any], name: str) -> None: ..."}, {"sha": "87c708f5a74f569b60288a7a7cc484d49436915a", "filename": "lib/backports/cached_property/_version.py", "status": "removed", "additions": 0, "deletions": 5, "changes": 5, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbackports%2Fcached_property%2F_version.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbackports%2Fcached_property%2F_version.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbackports%2Fcached_property%2F_version.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1,5 +0,0 @@\n-# coding: utf-8\n-# file generated by setuptools_scm\n-# don't change, don't track in version control\n-version = '1.0.2'\n-version_tuple = (1, 0, 2)"}, {"sha": "8b137891791fe96927ad78e64b0aad7bded08bdc", "filename": "lib/backports/cached_property/py.typed", "status": "removed", "additions": 0, "deletions": 1, "changes": 1, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbackports%2Fcached_property%2Fpy.typed", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbackports%2Fcached_property%2Fpy.typed", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbackports%2Fcached_property%2Fpy.typed?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8", "patch": "@@ -1 +0,0 @@\n-"}, {"sha": "b056e84d5b2ad8d8ca568263c7650afd0597aa2c", "filename": "lib/bleach/__init__.py", "status": "modified", "additions": 3, "deletions": 3, "changes": 6, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbleach%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -11,9 +11,9 @@\n \n \n # yyyymmdd\n-__releasedate__ = \"20241029\"\n+__releasedate__ = \"20251027\"\n # x.y.z or x.y.z.dev0 -- semver\n-__version__ = \"6.2.0\"\n+__version__ = \"6.3.0\"\n \n \n __all__ = [\"clean\", \"linkify\"]\n@@ -58,7 +58,7 @@ def clean(\n     :arg dict attributes: allowed attributes; can be a callable, list or dict;\n         defaults to ``bleach.sanitizer.ALLOWED_ATTRIBUTES``\n \n-    :arg list protocols: allowed list of protocols for links; defaults\n+    :arg set protocols: set of allowed protocols for links; defaults\n         to ``bleach.sanitizer.ALLOWED_PROTOCOLS``\n \n     :arg bool strip: whether or not to strip disallowed elements"}, {"sha": "11184e0d1f79ecf301e34d071d57e8600037b08c", "filename": "lib/bleach/_vendor/html5lib/constants.py", "status": "modified", "additions": 2, "deletions": 1, "changes": 3, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2F_vendor%2Fhtml5lib%2Fconstants.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2F_vendor%2Fhtml5lib%2Fconstants.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbleach%2F_vendor%2Fhtml5lib%2Fconstants.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -571,7 +571,8 @@\n     \"col\",\n     \"input\",\n     \"source\",\n-    \"track\"\n+    \"track\",\n+    \"wbr\",\n ])\n \n cdataElements = frozenset(['title', 'textarea'])"}, {"sha": "a37c9d8d9d675e75efae4190e719b8baf683b42d", "filename": "lib/bleach/_vendor/html5lib/filters/sanitizer.py", "status": "modified", "additions": 1, "deletions": 0, "changes": 1, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2F_vendor%2Fhtml5lib%2Ffilters%2Fsanitizer.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2F_vendor%2Fhtml5lib%2Ffilters%2Fsanitizer.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbleach%2F_vendor%2Fhtml5lib%2Ffilters%2Fsanitizer.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -128,6 +128,7 @@\n     (namespaces['html'], 'ul'),\n     (namespaces['html'], 'var'),\n     (namespaces['html'], 'video'),\n+    (namespaces['html'], 'wbr'),\n     (namespaces['mathml'], 'maction'),\n     (namespaces['mathml'], 'math'),\n     (namespaces['mathml'], 'merror'),"}, {"sha": "df8883458863784e3684355f7ea7473aefacf717", "filename": "lib/bleach/_vendor/vendor_install.sh", "status": "modified", "additions": 1, "deletions": 0, "changes": 1, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2F_vendor%2Fvendor_install.sh", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2F_vendor%2Fvendor_install.sh", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbleach%2F_vendor%2Fvendor_install.sh?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -12,6 +12,7 @@ pip install --no-binary all --no-compile --no-deps -r \"${BLEACH_VENDOR_DIR}/vend\n \n # Apply patches\n (cd \"${DEST}\" && patch -p2 < 01_html5lib_six.patch)\n+(cd \"${DEST}\" && patch -p2 < 02_html5lib_wbr.patch)\n \n # install Python 3.6.14 urllib.urlparse for #536\n curl --proto '=https' --tlsv1.2 -o \"${DEST}/parse.py\" https://raw.githubusercontent.com/python/cpython/v3.6.14/Lib/urllib/parse.py"}, {"sha": "012828353c426f8eadc02cac2a5a620d8b06bcb5", "filename": "lib/bleach/sanitizer.py", "status": "modified", "additions": 4, "deletions": 4, "changes": 8, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2Fsanitizer.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbleach%2Fsanitizer.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbleach%2Fsanitizer.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -34,7 +34,7 @@\n     \"acronym\": [\"title\"],\n }\n \n-#: List of allowed protocols\n+#: Set of allowed protocols\n ALLOWED_PROTOCOLS = frozenset((\"http\", \"https\", \"mailto\"))\n \n #: Invisible characters--0 to and including 31 except 9 (tab), 10 (lf), and 13 (cr)\n@@ -101,7 +101,7 @@ def __init__(\n         :arg dict attributes: allowed attributes; can be a callable, list or dict;\n             defaults to ``bleach.sanitizer.ALLOWED_ATTRIBUTES``\n \n-        :arg list protocols: allowed list of protocols for links; defaults\n+        :arg set protocols: set of allowed protocols for links; defaults\n             to ``bleach.sanitizer.ALLOWED_PROTOCOLS``\n \n         :arg bool strip: whether or not to strip disallowed elements\n@@ -279,7 +279,7 @@ def __init__(\n         :arg dict attributes: allowed attributes; can be a callable, list or dict;\n             defaults to ``bleach.sanitizer.ALLOWED_ATTRIBUTES``\n \n-        :arg list allowed_protocols: allowed list of protocols for links; defaults\n+        :arg set allowed_protocols: set of allowed protocols for links; defaults\n             to ``bleach.sanitizer.ALLOWED_PROTOCOLS``\n \n         :arg attr_val_is_uri: set of attributes that have URI values\n@@ -476,7 +476,7 @@ def sanitize_uri_value(self, value, allowed_protocols):\n         \"\"\"Checks a uri value to see if it's allowed\n \n         :arg value: the uri value to sanitize\n-        :arg allowed_protocols: list of allowed protocols\n+        :arg allowed_protocols: set of allowed protocols\n \n         :returns: allowed value or None\n "}, {"sha": "19b7c0ed057f19b9a32247b5a4eb8eca97ff2f04", "filename": "lib/bs4/__init__.py", "status": "modified", "additions": 593, "deletions": 259, "changes": 852, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -7,44 +7,74 @@\n provides methods and Pythonic idioms that make it easy to navigate,\n search, and modify the parse tree.\n \n-Beautiful Soup works with Python 3.6 and up. It works better if lxml\n-and/or html5lib is installed.\n+Beautiful Soup works with Python 3.7 and up. It works better if lxml\n+and/or html5lib is installed, but they are not required.\n \n For more than you ever wanted to know about Beautiful Soup, see the\n documentation: http://www.crummy.com/software/BeautifulSoup/bs4/doc/\n \"\"\"\n \n __author__ = \"Leonard Richardson (leonardr@segfault.org)\"\n-__version__ = \"4.12.3\"\n-__copyright__ = \"Copyright (c) 2004-2024 Leonard Richardson\"\n+__version__ = \"4.14.3\"\n+__copyright__ = \"Copyright (c) 2004-2025 Leonard Richardson\"\n # Use of this source code is governed by the MIT license.\n __license__ = \"MIT\"\n \n-__all__ = ['BeautifulSoup']\n+__all__ = [\n+    \"AttributeResemblesVariableWarning\",\n+    \"BeautifulSoup\",\n+    \"Comment\",\n+    \"Declaration\",\n+    \"ProcessingInstruction\",\n+    \"ResultSet\",\n+    \"CSS\",\n+    \"Script\",\n+    \"Stylesheet\",\n+    \"Tag\",\n+    \"TemplateString\",\n+    \"ElementFilter\",\n+    \"UnicodeDammit\",\n+    \"CData\",\n+    \"Doctype\",\n+\n+    # Exceptions\n+    \"FeatureNotFound\",\n+    \"ParserRejectedMarkup\",\n+    \"StopParsing\",\n+\n+    # Warnings\n+    \"AttributeResemblesVariableWarning\",\n+    \"GuessedAtParserWarning\",\n+    \"MarkupResemblesLocatorWarning\",\n+    \"UnusualUsageWarning\",\n+    \"XMLParsedAsHTMLWarning\",\n+]\n \n from collections import Counter\n-import os\n-import re\n+import io\n import sys\n-import traceback\n import warnings\n \n # The very first thing we do is give a useful error if someone is\n # running this code under Python 2.\n if sys.version_info.major < 3:\n-    raise ImportError('You are trying to use a Python 3-specific version of Beautiful Soup under Python 2. This will not work. The final version of Beautiful Soup to support Python 2 was 4.9.3.')\n+    raise ImportError(\n+        \"You are trying to use a Python 3-specific version of Beautiful Soup under Python 2. This will not work. The final version of Beautiful Soup to support Python 2 was 4.9.3.\"\n+    )\n \n from .builder import (\n     builder_registry,\n-    ParserRejectedMarkup,\n-    XMLParsedAsHTMLWarning,\n-    HTMLParserTreeBuilder\n+    TreeBuilder,\n )\n+from .builder._htmlparser import HTMLParserTreeBuilder\n from .dammit import UnicodeDammit\n+from .css import CSS\n+from ._deprecation import (\n+    _deprecated,\n+)\n from .element import (\n     CData,\n     Comment,\n-    CSS,\n     DEFAULT_OUTPUT_ENCODING,\n     Declaration,\n     Doctype,\n@@ -55,24 +85,53 @@\n     ResultSet,\n     Script,\n     Stylesheet,\n-    SoupStrainer,\n     Tag,\n     TemplateString,\n-    )\n+)\n+from .formatter import Formatter\n+from .filter import (\n+    ElementFilter,\n+    SoupStrainer,\n+)\n+from typing import (\n+    Any,\n+    cast,\n+    Counter as CounterType,\n+    Dict,\n+    Iterator,\n+    List,\n+    Sequence,\n+    Sized,\n+    Optional,\n+    Type,\n+    Union,\n+)\n \n-# Define some custom warnings.\n-class GuessedAtParserWarning(UserWarning):\n-    \"\"\"The warning issued when BeautifulSoup has to guess what parser to\n-    use -- probably because no parser was specified in the constructor.\n-    \"\"\"\n+from bs4._typing import (\n+    _Encoding,\n+    _Encodings,\n+    _IncomingMarkup,\n+    _InsertableElement,\n+    _RawAttributeValue,\n+    _RawAttributeValues,\n+    _RawMarkup,\n+)\n+\n+# Import all warnings and exceptions into the main package.\n+from bs4.exceptions import (\n+    FeatureNotFound,\n+    ParserRejectedMarkup,\n+    StopParsing,\n+)\n+from bs4._warnings import (\n+    AttributeResemblesVariableWarning,\n+    GuessedAtParserWarning,\n+    MarkupResemblesLocatorWarning,\n+    UnusualUsageWarning,\n+    XMLParsedAsHTMLWarning,\n+)\n \n-class MarkupResemblesLocatorWarning(UserWarning):\n-    \"\"\"The warning issued when BeautifulSoup is given 'markup' that\n-    actually looks like a resource locator -- a URL or a path to a file\n-    on disk.\n-    \"\"\"\n \n-   \n class BeautifulSoup(Tag):\n     \"\"\"A data structure representing a parsed HTML or XML document.\n \n@@ -104,24 +163,62 @@ class BeautifulSoup(Tag):\n     handle_endtag.\n     \"\"\"\n \n-    # Since BeautifulSoup subclasses Tag, it's possible to treat it as\n-    # a Tag with a .name. This name makes it clear the BeautifulSoup\n-    # object isn't a real markup tag.\n-    ROOT_TAG_NAME = '[document]'\n-\n-    # If the end-user gives no indication which tree builder they\n-    # want, look for one with these features.\n-    DEFAULT_BUILDER_FEATURES = ['html', 'fast']\n-\n-    # A string containing all ASCII whitespace characters, used in\n-    # endData() to detect data chunks that seem 'empty'.\n-    ASCII_SPACES = '\\x20\\x0a\\x09\\x0c\\x0d'\n-\n-    NO_PARSER_SPECIFIED_WARNING = \"No parser was explicitly specified, so I'm using the best available %(markup_type)s parser for this system (\\\"%(parser)s\\\"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.\\n\\nThe code that caused this warning is on line %(line_number)s of the file %(filename)s. To get rid of this warning, pass the additional argument 'features=\\\"%(parser)s\\\"' to the BeautifulSoup constructor.\\n\"\n-   \n-    def __init__(self, markup=\"\", features=None, builder=None,\n-                 parse_only=None, from_encoding=None, exclude_encodings=None,\n-                 element_classes=None, **kwargs):\n+    #: Since `BeautifulSoup` subclasses `Tag`, it's possible to treat it as\n+    #: a `Tag` with a `Tag.name`. Hoever, this name makes it clear the\n+    #: `BeautifulSoup` object isn't a real markup tag.\n+    ROOT_TAG_NAME: str = \"[document]\"\n+\n+    #: If the end-user gives no indication which tree builder they\n+    #: want, look for one with these features.\n+    DEFAULT_BUILDER_FEATURES: Sequence[str] = [\"html\", \"fast\"]\n+\n+    #: A string containing all ASCII whitespace characters, used in\n+    #: during parsing to detect data chunks that seem 'empty'.\n+    ASCII_SPACES: str = \"\\x20\\x0a\\x09\\x0c\\x0d\"\n+\n+    # FUTURE PYTHON:\n+    element_classes: Dict[Type[PageElement], Type[PageElement]]  #: :meta private:\n+    builder: TreeBuilder  #: :meta private:\n+    is_xml: bool\n+    known_xml: Optional[bool]\n+    parse_only: Optional[SoupStrainer]  #: :meta private:\n+\n+    # These members are only used while parsing markup.\n+    markup: Optional[_RawMarkup]  #: :meta private:\n+    current_data: List[str]  #: :meta private:\n+    currentTag: Optional[Tag]  #: :meta private:\n+    tagStack: List[Tag]  #: :meta private:\n+    open_tag_counter: CounterType[str]  #: :meta private:\n+    preserve_whitespace_tag_stack: List[Tag]  #: :meta private:\n+    string_container_stack: List[Tag]  #: :meta private:\n+    _most_recent_element: Optional[PageElement]  #: :meta private:\n+\n+    #: Beautiful Soup's best guess as to the character encoding of the\n+    #: original document.\n+    original_encoding: Optional[_Encoding]\n+\n+    #: The character encoding, if any, that was explicitly defined\n+    #: in the original document. This may or may not match\n+    #: `BeautifulSoup.original_encoding`.\n+    declared_html_encoding: Optional[_Encoding]\n+\n+    #: This is True if the markup that was parsed contains\n+    #: U+FFFD REPLACEMENT_CHARACTER characters which were not present\n+    #: in the original markup. These mark character sequences that\n+    #: could not be represented in Unicode.\n+    contains_replacement_characters: bool\n+\n+    def __init__(\n+        self,\n+        markup: _IncomingMarkup = \"\",\n+        features: Optional[Union[str, Sequence[str]]] = None,\n+        builder: Optional[Union[TreeBuilder, Type[TreeBuilder]]] = None,\n+        parse_only: Optional[SoupStrainer] = None,\n+        from_encoding: Optional[_Encoding] = None,\n+        exclude_encodings: Optional[_Encodings] = None,\n+        element_classes: Optional[Dict[Type[PageElement], Type[PageElement]]] = None,\n+        **kwargs: Any,\n+    ):\n         \"\"\"Constructor.\n \n         :param markup: A string or a file-like object representing\n@@ -165,67 +262,85 @@ def __init__(self, markup=\"\", features=None, builder=None,\n          Beautiful Soup 3. None of these arguments do anything in\n          Beautiful Soup 4; they will result in a warning and then be\n          ignored.\n-         \n+\n          Apart from this, any keyword arguments passed into the\n          BeautifulSoup constructor are propagated to the TreeBuilder\n          constructor. This makes it possible to configure a\n          TreeBuilder by passing in arguments, not just by saying which\n          one to use.\n         \"\"\"\n-        if 'convertEntities' in kwargs:\n-            del kwargs['convertEntities']\n+        if \"convertEntities\" in kwargs:\n+            del kwargs[\"convertEntities\"]\n             warnings.warn(\n                 \"BS4 does not respect the convertEntities argument to the \"\n                 \"BeautifulSoup constructor. Entities are always converted \"\n-                \"to Unicode characters.\")\n+                \"to Unicode characters.\"\n+            )\n \n-        if 'markupMassage' in kwargs:\n-            del kwargs['markupMassage']\n+        if \"markupMassage\" in kwargs:\n+            del kwargs[\"markupMassage\"]\n             warnings.warn(\n                 \"BS4 does not respect the markupMassage argument to the \"\n                 \"BeautifulSoup constructor. The tree builder is responsible \"\n-                \"for any necessary markup massage.\")\n+                \"for any necessary markup massage.\"\n+            )\n \n-        if 'smartQuotesTo' in kwargs:\n-            del kwargs['smartQuotesTo']\n+        if \"smartQuotesTo\" in kwargs:\n+            del kwargs[\"smartQuotesTo\"]\n             warnings.warn(\n                 \"BS4 does not respect the smartQuotesTo argument to the \"\n                 \"BeautifulSoup constructor. Smart quotes are always converted \"\n-                \"to Unicode characters.\")\n+                \"to Unicode characters.\"\n+            )\n \n-        if 'selfClosingTags' in kwargs:\n-            del kwargs['selfClosingTags']\n+        if \"selfClosingTags\" in kwargs:\n+            del kwargs[\"selfClosingTags\"]\n             warnings.warn(\n-                \"BS4 does not respect the selfClosingTags argument to the \"\n+                \"Beautiful Soup 4 does not respect the selfClosingTags argument to the \"\n                 \"BeautifulSoup constructor. The tree builder is responsible \"\n-                \"for understanding self-closing tags.\")\n+                \"for understanding self-closing tags.\"\n+            )\n \n-        if 'isHTML' in kwargs:\n-            del kwargs['isHTML']\n+        if \"isHTML\" in kwargs:\n+            del kwargs[\"isHTML\"]\n             warnings.warn(\n-                \"BS4 does not respect the isHTML argument to the \"\n+                \"Beautiful Soup 4 does not respect the isHTML argument to the \"\n                 \"BeautifulSoup constructor. Suggest you use \"\n                 \"features='lxml' for HTML and features='lxml-xml' for \"\n-                \"XML.\")\n+                \"XML.\"\n+            )\n \n-        def deprecated_argument(old_name, new_name):\n+        def deprecated_argument(old_name: str, new_name: str) -> Optional[Any]:\n             if old_name in kwargs:\n                 warnings.warn(\n                     'The \"%s\" argument to the BeautifulSoup constructor '\n-                    'has been renamed to \"%s.\"' % (old_name, new_name),\n-                    DeprecationWarning, stacklevel=3\n+                    'was renamed to \"%s\" in Beautiful Soup 4.0.0'\n+                    % (old_name, new_name),\n+                    DeprecationWarning,\n+                    stacklevel=3,\n                 )\n                 return kwargs.pop(old_name)\n             return None\n \n-        parse_only = parse_only or deprecated_argument(\n-            \"parseOnlyThese\", \"parse_only\")\n+        parse_only = parse_only or deprecated_argument(\"parseOnlyThese\", \"parse_only\")\n+        if parse_only is not None:\n+            # Issue a warning if we can tell in advance that\n+            # parse_only will exclude the entire tree.\n+            if parse_only.excludes_everything:\n+                warnings.warn(\n+                    f\"The given value for parse_only will exclude everything: {parse_only}\",\n+                    UserWarning,\n+                    stacklevel=3,\n+                )\n \n         from_encoding = from_encoding or deprecated_argument(\n-            \"fromEncoding\", \"from_encoding\")\n+            \"fromEncoding\", \"from_encoding\"\n+        )\n \n         if from_encoding and isinstance(markup, str):\n-            warnings.warn(\"You provided Unicode markup but also provided a value for from_encoding. Your from_encoding will be ignored.\")\n+            warnings.warn(\n+                \"You provided Unicode markup but also provided a value for from_encoding. Your from_encoding will be ignored.\"\n+            )\n             from_encoding = None\n \n         self.element_classes = element_classes or dict()\n@@ -235,7 +350,8 @@ def deprecated_argument(old_name, new_name):\n         # specify a parser' warning.\n         original_builder = builder\n         original_features = features\n-            \n+\n+        builder_class: Optional[Type[TreeBuilder]] = None\n         if isinstance(builder, type):\n             # A builder class was passed in; it needs to be instantiated.\n             builder_class = builder\n@@ -245,22 +361,32 @@ def deprecated_argument(old_name, new_name):\n                 features = [features]\n             if features is None or len(features) == 0:\n                 features = self.DEFAULT_BUILDER_FEATURES\n-            builder_class = builder_registry.lookup(*features)\n-            if builder_class is None:\n+            possible_builder_class = builder_registry.lookup(*features)\n+            if possible_builder_class is None:\n                 raise FeatureNotFound(\n                     \"Couldn't find a tree builder with the features you \"\n                     \"requested: %s. Do you need to install a parser library?\"\n-                    % \",\".join(features))\n+                    % \",\".join(features)\n+                )\n+            builder_class = possible_builder_class\n \n         # At this point either we have a TreeBuilder instance in\n         # builder, or we have a builder_class that we can instantiate\n         # with the remaining **kwargs.\n         if builder is None:\n+            assert builder_class is not None\n             builder = builder_class(**kwargs)\n-            if not original_builder and not (\n-                    original_features == builder.NAME or\n-                    original_features in builder.ALTERNATE_NAMES\n-            ) and markup:\n+            if (\n+                not original_builder\n+                and not (\n+                    original_features == builder.NAME\n+                    or (\n+                        isinstance(original_features, str)\n+                        and original_features in builder.ALTERNATE_NAMES\n+                    )\n+                )\n+                and markup\n+            ):\n                 # The user did not tell us which TreeBuilder to use,\n                 # and we had to guess. Issue a warning.\n                 if builder.is_xml:\n@@ -281,8 +407,8 @@ def deprecated_argument(old_name, new_name):\n                     line_number = caller.f_lineno\n                 else:\n                     globals = sys.__dict__\n-                    line_number= 1                    \n-                filename = globals.get('__file__')\n+                    line_number = 1\n+                filename = globals.get(\"__file__\")\n                 if filename:\n                     fnl = filename.lower()\n                     if fnl.endswith((\".pyc\", \".pyo\")):\n@@ -294,41 +420,56 @@ def deprecated_argument(old_name, new_name):\n                         filename=filename,\n                         line_number=line_number,\n                         parser=builder.NAME,\n-                        markup_type=markup_type\n+                        markup_type=markup_type,\n                     )\n                     warnings.warn(\n-                        self.NO_PARSER_SPECIFIED_WARNING % values,\n-                        GuessedAtParserWarning, stacklevel=2\n+                        GuessedAtParserWarning.MESSAGE % values,\n+                        GuessedAtParserWarning,\n+                        stacklevel=2,\n                     )\n         else:\n             if kwargs:\n-                warnings.warn(\"Keyword arguments to the BeautifulSoup constructor will be ignored. These would normally be passed into the TreeBuilder constructor, but a TreeBuilder instance was passed in as `builder`.\")\n-                    \n+                warnings.warn(\n+                    \"Keyword arguments to the BeautifulSoup constructor will be ignored. These would normally be passed into the TreeBuilder constructor, but a TreeBuilder instance was passed in as `builder`.\"\n+                )\n+\n         self.builder = builder\n         self.is_xml = builder.is_xml\n         self.known_xml = self.is_xml\n         self._namespaces = dict()\n         self.parse_only = parse_only\n \n-        if hasattr(markup, 'read'):        # It's a file-type object.\n-            markup = markup.read()\n-        elif len(markup) <= 256 and (\n-                (isinstance(markup, bytes) and not b'<' in markup)\n-                or (isinstance(markup, str) and not '<' in markup)\n+        if hasattr(markup, \"read\"):  # It's a file-type object.\n+            markup = cast(io.IOBase, markup).read()\n+        elif not isinstance(markup, (bytes, str)) and not hasattr(markup, \"__len__\"):\n+            raise TypeError(\n+                f\"Incoming markup is of an invalid type: {markup!r}. Markup must be a string, a bytestring, or an open filehandle.\"\n+            )\n+        elif isinstance(markup, Sized) and len(markup) <= 256 and (\n+            (isinstance(markup, bytes) and b\"<\" not in markup and b\"\\n\" not in markup)\n+            or (isinstance(markup, str) and \"<\" not in markup and \"\\n\" not in markup)\n         ):\n             # Issue warnings for a couple beginner problems\n             # involving passing non-markup to Beautiful Soup.\n             # Beautiful Soup will still parse the input as markup,\n             # since that is sometimes the intended behavior.\n             if not self._markup_is_url(markup):\n-                self._markup_resembles_filename(markup)                \n+                self._markup_resembles_filename(markup)\n+\n+        # At this point we know markup is a string or bytestring.  If\n+        # it was a file-type object, we've read from it.\n+        markup = cast(_RawMarkup, markup)\n \n         rejections = []\n         success = False\n-        for (self.markup, self.original_encoding, self.declared_html_encoding,\n-         self.contains_replacement_characters) in (\n-             self.builder.prepare_markup(\n-                 markup, from_encoding, exclude_encodings=exclude_encodings)):\n+        for (\n+            self.markup,\n+            self.original_encoding,\n+            self.declared_html_encoding,\n+            self.contains_replacement_characters,\n+        ) in self.builder.prepare_markup(\n+            markup, from_encoding, exclude_encodings=exclude_encodings\n+        ):\n             self.reset()\n             self.builder.initialize_soup(self)\n             try:\n@@ -342,15 +483,16 @@ def deprecated_argument(old_name, new_name):\n         if not success:\n             other_exceptions = [str(e) for e in rejections]\n             raise ParserRejectedMarkup(\n-                \"The markup you provided was rejected by the parser. Trying a different parser or a different encoding may help.\\n\\nOriginal exception(s) from parser:\\n \" + \"\\n \".join(other_exceptions)\n+                \"The markup you provided was rejected by the parser. Trying a different parser or a different encoding may help.\\n\\nOriginal exception(s) from parser:\\n \"\n+                + \"\\n \".join(other_exceptions)\n             )\n \n         # Clear out the markup and remove the builder's circular\n         # reference to this object.\n         self.markup = None\n         self.builder.soup = None\n \n-    def _clone(self):\n+    def copy_self(self) -> \"BeautifulSoup\":\n         \"\"\"Create a new BeautifulSoup object with the same TreeBuilder,\n         but not associated with any markup.\n \n@@ -362,24 +504,24 @@ def _clone(self):\n         # since we won't be parsing it again.\n         clone.original_encoding = self.original_encoding\n         return clone\n-        \n-    def __getstate__(self):\n+\n+    def __getstate__(self) -> Dict[str, Any]:\n         # Frequently a tree builder can't be pickled.\n         d = dict(self.__dict__)\n-        if 'builder' in d and d['builder'] is not None and not self.builder.picklable:\n-            d['builder'] = type(self.builder)\n+        if \"builder\" in d and d[\"builder\"] is not None and not self.builder.picklable:\n+            d[\"builder\"] = type(self.builder)\n         # Store the contents as a Unicode string.\n-        d['contents'] = []\n-        d['markup'] = self.decode()\n+        d[\"contents\"] = []\n+        d[\"markup\"] = self.decode()\n \n         # If _most_recent_element is present, it's a Tag object left\n         # over from initial parse. It might not be picklable and we\n         # don't need it.\n-        if '_most_recent_element' in d:\n-            del d['_most_recent_element']\n+        if \"_most_recent_element\" in d:\n+            del d[\"_most_recent_element\"]\n         return d\n \n-    def __setstate__(self, state):\n+    def __setstate__(self, state: Dict[str, Any]) -> None:\n         # If necessary, restore the TreeBuilder by looking it up.\n         self.__dict__ = state\n         if isinstance(self.builder, type):\n@@ -391,102 +533,145 @@ def __setstate__(self, state):\n         self.builder.soup = self\n         self.reset()\n         self._feed()\n-        return state\n \n-    \n     @classmethod\n-    def _decode_markup(cls, markup):\n-        \"\"\"Ensure `markup` is bytes so it's safe to send into warnings.warn.\n+    @_deprecated(\n+        replaced_by=\"nothing (private method, will be removed)\", version=\"4.13.0\"\n+    )\n+    def _decode_markup(cls, markup: _RawMarkup) -> str:\n+        \"\"\"Ensure `markup` is Unicode so it's safe to send into warnings.warn.\n \n-        TODO: warnings.warn had this problem back in 2010 but it might not\n-        anymore.\n+        warnings.warn had this problem back in 2010 but fortunately\n+        not anymore. This has not been used for a long time; I just\n+        noticed that fact while working on 4.13.0.\n         \"\"\"\n         if isinstance(markup, bytes):\n-            decoded = markup.decode('utf-8', 'replace')\n+            decoded = markup.decode(\"utf-8\", \"replace\")\n         else:\n             decoded = markup\n         return decoded\n \n     @classmethod\n-    def _markup_is_url(cls, markup):\n+    def _markup_is_url(cls, markup: _RawMarkup) -> bool:\n         \"\"\"Error-handling method to raise a warning if incoming markup looks\n         like a URL.\n \n-        :param markup: A string.\n-        :return: Whether or not the markup resembles a URL\n-            closely enough to justify a warning.\n+        :param markup: A string of markup.\n+        :return: Whether or not the markup resembled a URL\n+            closely enough to justify issuing a warning.\n         \"\"\"\n+        problem: bool = False\n         if isinstance(markup, bytes):\n-            space = b' '\n-            cant_start_with = (b\"http:\", b\"https:\")\n+            problem = (\n+                any(markup.startswith(prefix) for prefix in (b\"http:\", b\"https:\"))\n+                and b\" \" not in markup\n+            )\n         elif isinstance(markup, str):\n-            space = ' '\n-            cant_start_with = (\"http:\", \"https:\")\n+            problem = (\n+                any(markup.startswith(prefix) for prefix in (\"http:\", \"https:\"))\n+                and \" \" not in markup\n+            )\n         else:\n             return False\n \n-        if any(markup.startswith(prefix) for prefix in cant_start_with):\n-            if not space in markup:\n-                warnings.warn(\n-                    'The input looks more like a URL than markup. You may want to use'\n-                    ' an HTTP client like requests to get the document behind'\n-                    ' the URL, and feed that document to Beautiful Soup.',\n-                    MarkupResemblesLocatorWarning,\n-                    stacklevel=3\n-                )\n-                return True\n-        return False\n+        if not problem:\n+            return False\n+        warnings.warn(\n+            MarkupResemblesLocatorWarning.URL_MESSAGE % dict(what=\"URL\"),\n+            MarkupResemblesLocatorWarning,\n+            stacklevel=3,\n+        )\n+        return True\n \n     @classmethod\n-    def _markup_resembles_filename(cls, markup):\n-        \"\"\"Error-handling method to raise a warning if incoming markup\n+    def _markup_resembles_filename(cls, markup: _RawMarkup) -> bool:\n+        \"\"\"Error-handling method to issue a warning if incoming markup\n         resembles a filename.\n \n-        :param markup: A bytestring or string.\n-        :return: Whether or not the markup resembles a filename\n-            closely enough to justify a warning.\n+        :param markup: A string of markup.\n+        :return: Whether or not the markup resembled a filename\n+            closely enough to justify issuing a warning.\n         \"\"\"\n-        path_characters = '/\\\\'\n-        extensions = ['.html', '.htm', '.xml', '.xhtml', '.txt']\n-        if isinstance(markup, bytes):\n-            path_characters = path_characters.encode(\"utf8\")\n-            extensions = [x.encode('utf8') for x in extensions]\n+        markup_b: bytes\n+\n+        # We're only checking ASCII characters, so rather than write\n+        # the same tests twice, convert Unicode to a bytestring and\n+        # operate on the bytestring.\n+        if isinstance(markup, str):\n+            markup_b = markup.encode(\"utf8\")\n+        else:\n+            markup_b = markup\n+\n+        # Step 1: does it end with a common textual file extension?\n         filelike = False\n-        if any(x in markup for x in path_characters):\n+        lower = markup_b.lower()\n+        extensions = [b\".html\", b\".htm\", b\".xml\", b\".xhtml\", b\".txt\"]\n+        if any(lower.endswith(ext) for ext in extensions):\n             filelike = True\n-        else:\n-            lower = markup.lower()\n-            if any(lower.endswith(ext) for ext in extensions):\n-                filelike = True\n-        if filelike:\n-            warnings.warn(\n-                'The input looks more like a filename than markup. You may'\n-                ' want to open this file and pass the filehandle into'\n-                ' Beautiful Soup.',\n-                MarkupResemblesLocatorWarning, stacklevel=3\n-            )\n-            return True\n-        return False\n-    \n-    def _feed(self):\n+        if not filelike:\n+            return False\n+\n+        # Step 2: it _might_ be a file, but there are a few things\n+        # we can look for that aren't very common in filenames.\n+\n+        # Characters that have special meaning to Unix shells. (< was\n+        # excluded before this method was called.)\n+        #\n+        # Many of these are also reserved characters that cannot\n+        # appear in Windows filenames.\n+        for byte in markup_b:\n+            if byte in b\"?*#&;>$|\":\n+                return False\n+\n+        # Two consecutive forward slashes (as seen in a URL) or two\n+        # consecutive spaces (as seen in fixed-width data).\n+        #\n+        # (Paths to Windows network shares contain consecutive\n+        #  backslashes, so checking that doesn't seem as helpful.)\n+        if b\"//\" in markup_b:\n+            return False\n+        if b\"  \" in markup_b:\n+            return False\n+\n+        # A colon in any position other than position 1 (e.g. after a\n+        # Windows drive letter).\n+        if markup_b.startswith(b\":\"):\n+            return False\n+        colon_i = markup_b.rfind(b\":\")\n+        if colon_i not in (-1, 1):\n+            return False\n+\n+        # Step 3: If it survived all of those checks, it's similar\n+        # enough to a file to justify issuing a warning.\n+        warnings.warn(\n+            MarkupResemblesLocatorWarning.FILENAME_MESSAGE % dict(what=\"filename\"),\n+            MarkupResemblesLocatorWarning,\n+            stacklevel=3,\n+        )\n+        return True\n+\n+    def _feed(self) -> None:\n         \"\"\"Internal method that parses previously set markup, creating a large\n         number of Tag and NavigableString objects.\n         \"\"\"\n         # Convert the document to Unicode.\n         self.builder.reset()\n \n-        self.builder.feed(self.markup)\n+        if self.markup is not None:\n+            self.builder.feed(self.markup)\n         # Close out any unfinished strings and close all the open tags.\n         self.endData()\n-        while self.currentTag.name != self.ROOT_TAG_NAME:\n+        while (\n+            self.currentTag is not None and self.currentTag.name != self.ROOT_TAG_NAME\n+        ):\n             self.popTag()\n \n-    def reset(self):\n+    def reset(self) -> None:\n         \"\"\"Reset this object to a state as though it had never parsed any\n         markup.\n         \"\"\"\n         Tag.__init__(self, self, self.builder, self.ROOT_TAG_NAME)\n-        self.hidden = 1\n+        self.hidden = True\n         self.builder.reset()\n         self.current_data = []\n         self.currentTag = None\n@@ -497,35 +682,71 @@ def reset(self):\n         self._most_recent_element = None\n         self.pushTag(self)\n \n-    def new_tag(self, name, namespace=None, nsprefix=None, attrs={},\n-                sourceline=None, sourcepos=None, **kwattrs):\n+    def new_tag(\n+        self,\n+        name: str,\n+        namespace: Optional[str] = None,\n+        nsprefix: Optional[str] = None,\n+        attrs: Optional[_RawAttributeValues] = None,\n+        sourceline: Optional[int] = None,\n+        sourcepos: Optional[int] = None,\n+        string: Optional[str] = None,\n+        **kwattrs: _RawAttributeValue,\n+    ) -> Tag:\n         \"\"\"Create a new Tag associated with this BeautifulSoup object.\n \n         :param name: The name of the new Tag.\n         :param namespace: The URI of the new Tag's XML namespace, if any.\n         :param prefix: The prefix for the new Tag's XML namespace, if any.\n         :param attrs: A dictionary of this Tag's attribute values; can\n-            be used instead of `kwattrs` for attributes like 'class'\n+            be used instead of ``kwattrs`` for attributes like 'class'\n             that are reserved words in Python.\n         :param sourceline: The line number where this tag was\n             (purportedly) found in its source document.\n-        :param sourcepos: The character position within `sourceline` where this\n+        :param sourcepos: The character position within ``sourceline`` where this\n             tag was (purportedly) found.\n+        :param string: String content for the new Tag, if any.\n         :param kwattrs: Keyword arguments for the new Tag's attribute values.\n \n         \"\"\"\n-        kwattrs.update(attrs)\n-        return self.element_classes.get(Tag, Tag)(\n-            None, self.builder, name, namespace, nsprefix, kwattrs,\n-            sourceline=sourceline, sourcepos=sourcepos\n+        attr_container = self.builder.attribute_dict_class(**kwattrs)\n+        if attrs is not None:\n+            attr_container.update(attrs)\n+        tag_class = self.element_classes.get(Tag, Tag)\n+\n+        # Assume that this is either Tag or a subclass of Tag. If not,\n+        # the user brought type-unsafety upon themselves.\n+        tag_class = cast(Type[Tag], tag_class)\n+        tag = tag_class(\n+            None,\n+            self.builder,\n+            name,\n+            namespace,\n+            nsprefix,\n+            attr_container,\n+            sourceline=sourceline,\n+            sourcepos=sourcepos,\n         )\n \n-    def string_container(self, base_class=None):\n+        if string is not None:\n+            tag.string = string\n+        return tag\n+\n+    def string_container(\n+        self, base_class: Optional[Type[NavigableString]] = None\n+    ) -> Type[NavigableString]:\n+        \"\"\"Find the class that should be instantiated to hold a given kind of\n+        string.\n+\n+        This may be a built-in Beautiful Soup class or a custom class passed\n+        in to the BeautifulSoup constructor.\n+        \"\"\"\n         container = base_class or NavigableString\n-        \n-        # There may be a general override of NavigableString.\n-        container = self.element_classes.get(\n-            container, container\n+\n+        # The user may want us to use some other class (hopefully a\n+        # custom subclass) instead of the one we'd use normally.\n+        container = cast(\n+            Type[NavigableString], self.element_classes.get(container, container)\n         )\n \n         # On top of that, we may be inside a tag that needs a special\n@@ -535,43 +756,65 @@ def string_container(self, base_class=None):\n                 self.string_container_stack[-1].name, container\n             )\n         return container\n-        \n-    def new_string(self, s, subclass=None):\n-        \"\"\"Create a new NavigableString associated with this BeautifulSoup\n+\n+    def new_string(\n+        self, s: str, subclass: Optional[Type[NavigableString]] = None\n+    ) -> NavigableString:\n+        \"\"\"Create a new `NavigableString` associated with this `BeautifulSoup`\n         object.\n+\n+        :param s: The string content of the `NavigableString`\n+        :param subclass: The subclass of `NavigableString`, if any, to\n+               use. If a document is being processed, an appropriate\n+               subclass for the current location in the document will\n+               be determined automatically.\n         \"\"\"\n         container = self.string_container(subclass)\n         return container(s)\n \n-    def insert_before(self, *args):\n+    def insert_before(self, *args: _InsertableElement) -> List[PageElement]:\n         \"\"\"This method is part of the PageElement API, but `BeautifulSoup` doesn't implement\n         it because there is nothing before or after it in the parse tree.\n         \"\"\"\n-        raise NotImplementedError(\"BeautifulSoup objects don't support insert_before().\")\n+        raise NotImplementedError(\n+            \"BeautifulSoup objects don't support insert_before().\"\n+        )\n \n-    def insert_after(self, *args):\n+    def insert_after(self, *args: _InsertableElement) -> List[PageElement]:\n         \"\"\"This method is part of the PageElement API, but `BeautifulSoup` doesn't implement\n         it because there is nothing before or after it in the parse tree.\n         \"\"\"\n         raise NotImplementedError(\"BeautifulSoup objects don't support insert_after().\")\n \n-    def popTag(self):\n-        \"\"\"Internal method called by _popToTag when a tag is closed.\"\"\"\n+    def popTag(self) -> Optional[Tag]:\n+        \"\"\"Internal method called by _popToTag when a tag is closed.\n+\n+        :meta private:\n+        \"\"\"\n+        if not self.tagStack:\n+            # Nothing to pop. This shouldn't happen.\n+            return None\n         tag = self.tagStack.pop()\n         if tag.name in self.open_tag_counter:\n             self.open_tag_counter[tag.name] -= 1\n-        if self.preserve_whitespace_tag_stack and tag == self.preserve_whitespace_tag_stack[-1]:\n+        if (\n+            self.preserve_whitespace_tag_stack\n+            and tag == self.preserve_whitespace_tag_stack[-1]\n+        ):\n             self.preserve_whitespace_tag_stack.pop()\n         if self.string_container_stack and tag == self.string_container_stack[-1]:\n             self.string_container_stack.pop()\n-        #print(\"Pop\", tag.name)\n+        # print(\"Pop\", tag.name)\n         if self.tagStack:\n             self.currentTag = self.tagStack[-1]\n         return self.currentTag\n \n-    def pushTag(self, tag):\n-        \"\"\"Internal method called by handle_starttag when a tag is opened.\"\"\"\n-        #print(\"Push\", tag.name)\n+    def pushTag(self, tag: Tag) -> None:\n+        \"\"\"Internal method called by handle_starttag when a tag is opened.\n+\n+        :meta private:\n+        \"\"\"\n+        # print(\"Push\", tag.name)\n         if self.currentTag is not None:\n             self.currentTag.contents.append(tag)\n         self.tagStack.append(tag)\n@@ -583,12 +826,17 @@ def pushTag(self, tag):\n         if tag.name in self.builder.string_containers:\n             self.string_container_stack.append(tag)\n \n-    def endData(self, containerClass=None):\n+    def endData(self, containerClass: Optional[Type[NavigableString]] = None) -> None:\n         \"\"\"Method called by the TreeBuilder when the end of a data segment\n         occurs.\n-        \"\"\"       \n+\n+        :param containerClass: The class to use when incorporating the\n+        data segment into the parse tree.\n+\n+        :meta private:\n+        \"\"\"\n         if self.current_data:\n-            current_data = ''.join(self.current_data)\n+            current_data = \"\".join(self.current_data)\n             # If whitespace is not preserved, and this string contains\n             # nothing but ASCII spaces, replace it with a single space\n             # or newline.\n@@ -599,28 +847,41 @@ def endData(self, containerClass=None):\n                         strippable = False\n                         break\n                 if strippable:\n-                    if '\\n' in current_data:\n-                        current_data = '\\n'\n+                    if \"\\n\" in current_data:\n+                        current_data = \"\\n\"\n                     else:\n-                        current_data = ' '\n+                        current_data = \" \"\n \n             # Reset the data collector.\n             self.current_data = []\n \n             # Should we add this string to the tree at all?\n-            if self.parse_only and len(self.tagStack) <= 1 and \\\n-                   (not self.parse_only.text or \\\n-                    not self.parse_only.search(current_data)):\n+            if (\n+                self.parse_only\n+                and len(self.tagStack) <= 1\n+                and (not self.parse_only.allow_string_creation(current_data))\n+            ):\n                 return\n \n             containerClass = self.string_container(containerClass)\n             o = containerClass(current_data)\n             self.object_was_parsed(o)\n \n-    def object_was_parsed(self, o, parent=None, most_recent_element=None):\n-        \"\"\"Method called by the TreeBuilder to integrate an object into the parse tree.\"\"\"\n+    def object_was_parsed(\n+        self,\n+        o: PageElement,\n+        parent: Optional[Tag] = None,\n+        most_recent_element: Optional[PageElement] = None,\n+    ) -> None:\n+        \"\"\"Method called by the TreeBuilder to integrate an object into the\n+        parse tree.\n+\n+        :meta private:\n+        \"\"\"\n         if parent is None:\n             parent = self.currentTag\n+        assert parent is not None\n+        previous_element: Optional[PageElement]\n         if most_recent_element is not None:\n             previous_element = most_recent_element\n         else:\n@@ -645,12 +906,12 @@ def object_was_parsed(self, o, parent=None, most_recent_element=None):\n         if fix:\n             self._linkage_fixer(parent)\n \n-    def _linkage_fixer(self, el):\n+    def _linkage_fixer(self, el: Tag) -> None:\n         \"\"\"Make sure linkage of this fragment is sound.\"\"\"\n \n         first = el.contents[0]\n         child = el.contents[-1]\n-        descendant = child\n+        descendant: PageElement = child\n \n         if child is first and el.parent is not None:\n             # Parent should be linked to first child\n@@ -668,14 +929,18 @@ def _linkage_fixer(self, el):\n \n         # This index is a tag, dig deeper for a \"last descendant\"\n         if isinstance(child, Tag) and child.contents:\n-            descendant = child._last_descendant(False)\n+            # _last_decendant is typed as returning Optional[PageElement],\n+            # but the value can't be None here, because el is a Tag\n+            # which we know has contents.\n+            descendant = cast(PageElement, child._last_descendant(False))\n \n         # As the final step, link last descendant. It should be linked\n         # to the parent's next sibling (if found), else walk up the chain\n         # and find a parent with a sibling. It should have no next sibling.\n         descendant.next_element = None\n         descendant.next_sibling = None\n-        target = el\n+\n+        target: Optional[Tag] = el\n         while True:\n             if target is None:\n                 break\n@@ -685,7 +950,9 @@ def _linkage_fixer(self, el):\n                 break\n             target = target.parent\n \n-    def _popToTag(self, name, nsprefix=None, inclusivePop=True):\n+    def _popToTag(\n+        self, name: str, nsprefix: Optional[str] = None, inclusivePop: bool = True\n+    ) -> Optional[Tag]:\n         \"\"\"Pops the tag stack up to and including the most recent\n         instance of the given tag.\n \n@@ -698,11 +965,12 @@ def _popToTag(self, name, nsprefix=None, inclusivePop=True):\n           to but *not* including the most recent instqance of the\n           given tag.\n \n+        :meta private:\n         \"\"\"\n-        #print(\"Popping to %s\" % name)\n+        # print(\"Popping to %s\" % name)\n         if name == self.ROOT_TAG_NAME:\n             # The BeautifulSoup object itself can never be popped.\n-            return\n+            return None\n \n         most_recently_popped = None\n \n@@ -711,46 +979,71 @@ def _popToTag(self, name, nsprefix=None, inclusivePop=True):\n             if not self.open_tag_counter.get(name):\n                 break\n             t = self.tagStack[i]\n-            if (name == t.name and nsprefix == t.prefix):\n+            if name == t.name and nsprefix == t.prefix:\n                 if inclusivePop:\n                     most_recently_popped = self.popTag()\n                 break\n             most_recently_popped = self.popTag()\n \n         return most_recently_popped\n \n-    def handle_starttag(self, name, namespace, nsprefix, attrs, sourceline=None,\n-                        sourcepos=None, namespaces=None):\n+    def handle_starttag(\n+        self,\n+        name: str,\n+        namespace: Optional[str],\n+        nsprefix: Optional[str],\n+        attrs: _RawAttributeValues,\n+        sourceline: Optional[int] = None,\n+        sourcepos: Optional[int] = None,\n+        namespaces: Optional[Dict[str, str]] = None,\n+    ) -> Optional[Tag]:\n         \"\"\"Called by the tree builder when a new tag is encountered.\n \n         :param name: Name of the tag.\n         :param nsprefix: Namespace prefix for the tag.\n-        :param attrs: A dictionary of attribute values.\n+        :param attrs: A dictionary of attribute values. Note that\n+           attribute values are expected to be simple strings; processing\n+           of multi-valued attributes such as \"class\" comes later.\n         :param sourceline: The line number where this tag was found in its\n             source document.\n         :param sourcepos: The character position within `sourceline` where this\n             tag was found.\n-        :param namespaces: A dictionary of all namespace prefix mappings \n+        :param namespaces: A dictionary of all namespace prefix mappings\n             currently in scope in the document.\n \n         If this method returns None, the tag was rejected by an active\n-        SoupStrainer. You should proceed as if the tag had not occurred\n+        `ElementFilter`. You should proceed as if the tag had not occurred\n         in the document. For instance, if this was a self-closing tag,\n         don't call handle_endtag.\n+\n+        :meta private:\n         \"\"\"\n         # print(\"Start tag %s: %s\" % (name, attrs))\n         self.endData()\n \n-        if (self.parse_only and len(self.tagStack) <= 1\n-            and (self.parse_only.text\n-                 or not self.parse_only.search_tag(name, attrs))):\n+        if (\n+            self.parse_only\n+            and len(self.tagStack) <= 1\n+            and not self.parse_only.allow_tag_creation(nsprefix, name, attrs)\n+        ):\n             return None\n \n-        tag = self.element_classes.get(Tag, Tag)(\n-            self, self.builder, name, namespace, nsprefix, attrs,\n-            self.currentTag, self._most_recent_element,\n-            sourceline=sourceline, sourcepos=sourcepos,\n-            namespaces=namespaces\n+        tag_class = self.element_classes.get(Tag, Tag)\n+        # Assume that this is either Tag or a subclass of Tag. If not,\n+        # the user brought type-unsafety upon themselves.\n+        tag_class = cast(Type[Tag], tag_class)\n+        tag = tag_class(\n+            self,\n+            self.builder,\n+            name,\n+            namespace,\n+            nsprefix,\n+            attrs,\n+            self.currentTag,\n+            self._most_recent_element,\n+            sourceline=sourceline,\n+            sourcepos=sourcepos,\n+            namespaces=namespaces,\n         )\n         if tag is None:\n             return tag\n@@ -760,81 +1053,122 @@ def handle_starttag(self, name, namespace, nsprefix, attrs, sourceline=None,\n         self.pushTag(tag)\n         return tag\n \n-    def handle_endtag(self, name, nsprefix=None):\n+    def handle_endtag(self, name: str, nsprefix: Optional[str] = None) -> None:\n         \"\"\"Called by the tree builder when an ending tag is encountered.\n \n         :param name: Name of the tag.\n         :param nsprefix: Namespace prefix for the tag.\n+\n+        :meta private:\n         \"\"\"\n-        #print(\"End tag: \" + name)\n+        # print(\"End tag: \" + name)\n         self.endData()\n         self._popToTag(name, nsprefix)\n-        \n-    def handle_data(self, data):\n-        \"\"\"Called by the tree builder when a chunk of textual data is encountered.\"\"\"\n+\n+    def handle_data(self, data: str) -> None:\n+        \"\"\"Called by the tree builder when a chunk of textual data is\n+        encountered.\n+\n+        :meta private:\n+        \"\"\"\n         self.current_data.append(data)\n-       \n-    def decode(self, pretty_print=False,\n-               eventual_encoding=DEFAULT_OUTPUT_ENCODING,\n-               formatter=\"minimal\", iterator=None):\n-        \"\"\"Returns a string or Unicode representation of the parse tree\n-            as an HTML or XML document.\n-\n-        :param pretty_print: If this is True, indentation will be used to\n-            make the document more readable.\n+\n+    def decode(\n+        self,\n+        indent_level: Optional[int] = None,\n+        eventual_encoding: _Encoding = DEFAULT_OUTPUT_ENCODING,\n+        formatter: Union[Formatter, str] = \"minimal\",\n+        iterator: Optional[Iterator[PageElement]] = None,\n+        **kwargs: Any,\n+    ) -> str:\n+        \"\"\"Returns a string representation of the parse tree\n+            as a full HTML or XML document.\n+\n+        :param indent_level: Each line of the rendering will be\n+           indented this many levels. (The ``formatter`` decides what a\n+           'level' means, in terms of spaces or other characters\n+           output.) This is used internally in recursive calls while\n+           pretty-printing.\n         :param eventual_encoding: The encoding of the final document.\n             If this is None, the document will be a Unicode string.\n+        :param formatter: Either a `Formatter` object, or a string naming one of\n+            the standard formatters.\n+        :param iterator: The iterator to use when navigating over the\n+            parse tree. This is only used by `Tag.decode_contents` and\n+            you probably won't need to use it.\n         \"\"\"\n         if self.is_xml:\n             # Print the XML declaration\n-            encoding_part = ''\n+            encoding_part = \"\"\n+            declared_encoding: Optional[str] = eventual_encoding\n             if eventual_encoding in PYTHON_SPECIFIC_ENCODINGS:\n                 # This is a special Python encoding; it can't actually\n                 # go into an XML document because it means nothing\n                 # outside of Python.\n-                eventual_encoding = None\n-            if eventual_encoding != None:\n-                encoding_part = ' encoding=\"%s\"' % eventual_encoding\n+                declared_encoding = None\n+            if declared_encoding is not None:\n+                encoding_part = ' encoding=\"%s\"' % declared_encoding\n             prefix = '<?xml version=\"1.0\"%s?>\\n' % encoding_part\n         else:\n-            prefix = ''\n-        if not pretty_print:\n-            indent_level = None\n+            prefix = \"\"\n+\n+        # Prior to 4.13.0, the first argument to this method was a\n+        # bool called pretty_print, which gave the method a different\n+        # signature from its superclass implementation, Tag.decode.\n+        #\n+        # The signatures of the two methods now match, but just in\n+        # case someone is still passing a boolean in as the first\n+        # argument to this method (or a keyword argument with the old\n+        # name), we can handle it and put out a DeprecationWarning.\n+        warning: Optional[str] = None\n+        pretty_print: Optional[bool] = None\n+        if isinstance(indent_level, bool):\n+            if indent_level is True:\n+                indent_level = 0\n+            elif indent_level is False:\n+                indent_level = None\n+            warning = f\"As of 4.13.0, the first argument to BeautifulSoup.decode has been changed from bool to int, to match Tag.decode. Pass in a value of {indent_level} instead.\"\n         else:\n-            indent_level = 0\n+            pretty_print = kwargs.pop(\"pretty_print\", None)\n+            assert not kwargs\n+            if pretty_print is not None:\n+                if pretty_print is True:\n+                    indent_level = 0\n+                elif pretty_print is False:\n+                    indent_level = None\n+                warning = f\"As of 4.13.0, the pretty_print argument to BeautifulSoup.decode has been removed, to match Tag.decode. Pass in a value of indent_level={indent_level} instead.\"\n+\n+        if warning:\n+            warnings.warn(warning, DeprecationWarning, stacklevel=2)\n+        elif indent_level is False or pretty_print is False:\n+            indent_level = None\n         return prefix + super(BeautifulSoup, self).decode(\n-            indent_level, eventual_encoding, formatter, iterator)\n+            indent_level, eventual_encoding, formatter, iterator\n+        )\n+\n \n # Aliases to make it easier to get started quickly, e.g. 'from bs4 import _soup'\n _s = BeautifulSoup\n _soup = BeautifulSoup\n \n+\n class BeautifulStoneSoup(BeautifulSoup):\n     \"\"\"Deprecated interface to an XML parser.\"\"\"\n \n-    def __init__(self, *args, **kwargs):\n-        kwargs['features'] = 'xml'\n+    def __init__(self, *args: Any, **kwargs: Any):\n+        kwargs[\"features\"] = \"xml\"\n         warnings.warn(\n-            'The BeautifulStoneSoup class is deprecated. Instead of using '\n+            \"The BeautifulStoneSoup class was deprecated in version 4.0.0. Instead of using \"\n             'it, pass features=\"xml\" into the BeautifulSoup constructor.',\n-            DeprecationWarning, stacklevel=2\n+            DeprecationWarning,\n+            stacklevel=2,\n         )\n         super(BeautifulStoneSoup, self).__init__(*args, **kwargs)\n \n \n-class StopParsing(Exception):\n-    \"\"\"Exception raised by a TreeBuilder if it's unable to continue parsing.\"\"\"\n-    pass\n-\n-class FeatureNotFound(ValueError):\n-    \"\"\"Exception raised by the BeautifulSoup constructor if no parser with the\n-    requested features is found.\n-    \"\"\"\n-    pass\n-\n-\n-#If this file is run as a script, act as an HTML pretty-printer.\n-if __name__ == '__main__':\n+# If this file is run as a script, act as an HTML pretty-printer.\n+if __name__ == \"__main__\":\n     import sys\n+\n     soup = BeautifulSoup(sys.stdin)\n     print((soup.prettify()))"}, {"sha": "a7b5685b8ab8b40597e1f006e721ae2cc12c4b03", "filename": "lib/bs4/_deprecation.py", "status": "added", "additions": 80, "deletions": 0, "changes": 80, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2F_deprecation.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2F_deprecation.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2F_deprecation.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,80 @@\n+\"\"\"Helper functions for deprecation.\n+\n+This interface is itself unstable and may change without warning. Do\n+not use these functions yourself, even as a joke. The underscores are\n+there for a reason. No support will be given.\n+\n+In particular, most of this will go away without warning once\n+Beautiful Soup drops support for Python 3.11, since Python 3.12\n+defines a `@typing.deprecated()\n+decorator. <https://peps.python.org/pep-0702/>`_\n+\"\"\"\n+\n+import functools\n+import warnings\n+\n+from typing import (\n+    Any,\n+    Callable,\n+)\n+\n+\n+def _deprecated_alias(old_name: str, new_name: str, version: str):\n+    \"\"\"Alias one attribute name to another for backward compatibility\n+\n+    :meta private:\n+    \"\"\"\n+\n+    @property # type:ignore\n+    def alias(self) -> Any:\n+        \":meta private:\"\n+        warnings.warn(\n+            f\"Access to deprecated property {old_name}. (Replaced by {new_name}) -- Deprecated since version {version}.\",\n+            DeprecationWarning,\n+            stacklevel=2,\n+        )\n+        return getattr(self, new_name)\n+\n+    @alias.setter\n+    def alias(self, value: str) -> None:\n+        \":meta private:\"\n+        warnings.warn(\n+            f\"Write to deprecated property {old_name}. (Replaced by {new_name}) -- Deprecated since version {version}.\",\n+            DeprecationWarning,\n+            stacklevel=2,\n+        )\n+        return setattr(self, new_name, value)\n+\n+    return alias\n+\n+\n+def _deprecated_function_alias(\n+    old_name: str, new_name: str, version: str\n+) -> Callable[[Any], Any]:\n+    def alias(self, *args: Any, **kwargs: Any) -> Any:\n+        \":meta private:\"\n+        warnings.warn(\n+            f\"Call to deprecated method {old_name}. (Replaced by {new_name}) -- Deprecated since version {version}.\",\n+            DeprecationWarning,\n+            stacklevel=2,\n+        )\n+        return getattr(self, new_name)(*args, **kwargs)\n+\n+    return alias\n+\n+\n+def _deprecated(replaced_by: str, version: str) -> Callable:\n+    def deprecate(func: Callable) -> Callable:\n+        @functools.wraps(func)\n+        def with_warning(*args: Any, **kwargs: Any) -> Any:\n+            \":meta private:\"\n+            warnings.warn(\n+                f\"Call to deprecated method {func.__name__}. (Replaced by {replaced_by}) -- Deprecated since version {version}.\",\n+                DeprecationWarning,\n+                stacklevel=2,\n+            )\n+            return func(*args, **kwargs)\n+\n+        return with_warning\n+\n+    return deprecate"}, {"sha": "0ab69df14360e9b344a1e165bab77eebffdb8eb6", "filename": "lib/bs4/_typing.py", "status": "added", "additions": 205, "deletions": 0, "changes": 205, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2F_typing.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2F_typing.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2F_typing.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,205 @@\n+# Custom type aliases used throughout Beautiful Soup to improve readability.\n+\n+# Notes on improvements to the type system in newer versions of Python\n+# that can be used once Beautiful Soup drops support for older\n+# versions:\n+#\n+# * ClassVar can be put on class variables now.\n+# * In 3.10, x|y is an accepted shorthand for Union[x,y].\n+# * In 3.10, TypeAlias gains capabilities that can be used to\n+#   improve the tree matching types (I don't remember what, exactly).\n+# * In 3.9 it's possible to specialize the re.Match type,\n+#   e.g. re.Match[str]. In 3.8 there's a typing.re namespace for this,\n+#   but it's removed in 3.12, so to support the widest possible set of\n+#   versions I'm not using it.\n+\n+from typing_extensions import (\n+    runtime_checkable,\n+    Protocol,\n+    TypeAlias,\n+)\n+from typing import (\n+    Any,\n+    Callable,\n+    Dict,\n+    IO,\n+    Iterable,\n+    Mapping,\n+    Optional,\n+    Pattern,\n+    TYPE_CHECKING,\n+    Union,\n+)\n+\n+if TYPE_CHECKING:\n+    from bs4.element import (\n+        AttributeValueList,\n+        NamespacedAttribute,\n+        NavigableString,\n+        PageElement,\n+        ResultSet,\n+        Tag,\n+    )\n+\n+\n+@runtime_checkable\n+class _RegularExpressionProtocol(Protocol):\n+    \"\"\"A protocol object which can accept either Python's built-in\n+    `re.Pattern` objects, or the similar ``Regex`` objects defined by the\n+    third-party ``regex`` package.\n+    \"\"\"\n+\n+    def search(\n+        self, string: str, pos: int = ..., endpos: int = ...\n+    ) -> Optional[Any]: ...\n+\n+    @property\n+    def pattern(self) -> str: ...\n+\n+\n+# Aliases for markup in various stages of processing.\n+#\n+#: The rawest form of markup: either a string, bytestring, or an open filehandle.\n+_IncomingMarkup: TypeAlias = Union[str, bytes, IO[str], IO[bytes]]\n+\n+#: Markup that is in memory but has (potentially) yet to be converted\n+#: to Unicode.\n+_RawMarkup: TypeAlias = Union[str, bytes]\n+\n+# Aliases for character encodings\n+#\n+\n+#: A data encoding.\n+_Encoding: TypeAlias = str\n+\n+#: One or more data encodings.\n+_Encodings: TypeAlias = Iterable[_Encoding]\n+\n+# Aliases for XML namespaces\n+#\n+\n+#: The prefix for an XML namespace.\n+_NamespacePrefix: TypeAlias = str\n+\n+#: The URL of an XML namespace\n+_NamespaceURL: TypeAlias = str\n+\n+#: A mapping of prefixes to namespace URLs.\n+_NamespaceMapping: TypeAlias = Dict[_NamespacePrefix, _NamespaceURL]\n+\n+#: A mapping of namespace URLs to prefixes\n+_InvertedNamespaceMapping: TypeAlias = Dict[_NamespaceURL, _NamespacePrefix]\n+\n+# Aliases for the attribute values associated with HTML/XML tags.\n+#\n+\n+#: The value associated with an HTML or XML attribute. This is the\n+#: relatively unprocessed value Beautiful Soup expects to come from a\n+#: `TreeBuilder`.\n+_RawAttributeValue: TypeAlias = str\n+\n+#: A dictionary of names to `_RawAttributeValue` objects. This is how\n+#: Beautiful Soup expects a `TreeBuilder` to represent a tag's\n+#: attribute values.\n+_RawAttributeValues: TypeAlias = (\n+    \"Mapping[Union[str, NamespacedAttribute], _RawAttributeValue]\"\n+)\n+\n+#: An attribute value in its final form, as stored in the\n+# `Tag` class, after it has been processed and (in some cases)\n+# split into a list of strings.\n+_AttributeValue: TypeAlias = Union[str, \"AttributeValueList\"]\n+\n+#: A dictionary of names to :py:data:`_AttributeValue` objects. This is what\n+#: a tag's attributes look like after processing.\n+_AttributeValues: TypeAlias = Dict[str, _AttributeValue]\n+\n+#: The methods that deal with turning :py:data:`_RawAttributeValue` into\n+#: :py:data:`_AttributeValue` may be called several times, even after the values\n+#: are already processed (e.g. when cloning a tag), so they need to\n+#: be able to acommodate both possibilities.\n+_RawOrProcessedAttributeValues: TypeAlias = Union[_RawAttributeValues, _AttributeValues]\n+\n+#: A number of tree manipulation methods can take either a `PageElement` or a\n+#: normal Python string (which will be converted to a `NavigableString`).\n+_InsertableElement: TypeAlias = Union[\"PageElement\", str]\n+\n+# Aliases to represent the many possibilities for matching bits of a\n+# parse tree.\n+#\n+# This is very complicated because we're applying a formal type system\n+# to some very DWIM code. The types we end up with will be the types\n+# of the arguments to the SoupStrainer constructor and (more\n+# familiarly to Beautiful Soup users) the find* methods.\n+\n+#: A function that takes a PageElement and returns a yes-or-no answer.\n+_PageElementMatchFunction: TypeAlias = Callable[[\"PageElement\"], bool]\n+\n+#: A function that takes the raw parsed ingredients of a markup tag\n+#: and returns a yes-or-no answer.\n+#  Not necessary at the moment.\n+# _AllowTagCreationFunction:TypeAlias = Callable[[Optional[str], str, Optional[_RawAttributeValues]], bool]\n+\n+#: A function that takes the raw parsed ingredients of a markup string node\n+#: and returns a yes-or-no answer.\n+#  Not necessary at the moment.\n+# _AllowStringCreationFunction:TypeAlias = Callable[[Optional[str]], bool]\n+\n+#: A function that takes a `Tag` and returns a yes-or-no answer.\n+#: A `TagNameMatchRule` expects this kind of function, if you're\n+#: going to pass it a function.\n+_TagMatchFunction: TypeAlias = Callable[[\"Tag\"], bool]\n+\n+#: A function that takes a string (or None) and returns a yes-or-no\n+#: answer. An `AttributeValueMatchRule` expects this kind of function, if\n+#: you're going to pass it a function.\n+_NullableStringMatchFunction: TypeAlias = Callable[[Optional[str]], bool]\n+\n+#: A function that takes a string and returns a yes-or-no answer.  A\n+# `StringMatchRule` expects this kind of function, if you're going to\n+# pass it a function.\n+_StringMatchFunction: TypeAlias = Callable[[str], bool]\n+\n+#: Either a tag name, an attribute value or a string can be matched\n+#: against a string, bytestring, regular expression, or a boolean.\n+_BaseStrainable: TypeAlias = Union[str, bytes, Pattern[str], bool]\n+\n+#: A tag can be matched either with the `_BaseStrainable` options, or\n+#: using a function that takes the `Tag` as its sole argument.\n+_BaseStrainableElement: TypeAlias = Union[_BaseStrainable, _TagMatchFunction]\n+\n+#: A tag's attribute value can be matched either with the\n+#: `_BaseStrainable` options, or using a function that takes that\n+#: value as its sole argument.\n+_BaseStrainableAttribute: TypeAlias = Union[_BaseStrainable, _NullableStringMatchFunction]\n+\n+#: A tag can be matched using either a single criterion or a list of\n+#: criteria.\n+_StrainableElement: TypeAlias = Union[\n+    _BaseStrainableElement, Iterable[_BaseStrainableElement]\n+]\n+\n+#: An attribute value can be matched using either a single criterion\n+#: or a list of criteria.\n+_StrainableAttribute: TypeAlias = Union[\n+    _BaseStrainableAttribute, Iterable[_BaseStrainableAttribute]\n+]\n+\n+#: An string can be matched using the same techniques as\n+#: an attribute value.\n+_StrainableString: TypeAlias = _StrainableAttribute\n+\n+#: A dictionary may be used to match against multiple attribute vlaues at once.\n+_StrainableAttributes: TypeAlias = Dict[str, _StrainableAttribute]\n+\n+#: Many Beautiful soup methods return a PageElement or an ResultSet of\n+#: PageElements. A PageElement is either a Tag or a NavigableString.\n+#: These convenience aliases make it easier for IDE users to see which methods\n+#: are available on the objects they're dealing with.\n+_OneElement: TypeAlias = Union[\"PageElement\", \"Tag\", \"NavigableString\"]\n+_AtMostOneElement: TypeAlias = Optional[_OneElement]\n+_AtMostOneTag: TypeAlias = Optional[\"Tag\"]\n+_AtMostOneNavigableString: TypeAlias = Optional[\"NavigableString\"]\n+_QueryResults: TypeAlias = \"ResultSet[_OneElement]\"\n+_SomeTags: TypeAlias = \"ResultSet[Tag]\"\n+_SomeNavigableStrings: TypeAlias = \"ResultSet[NavigableString]\""}, {"sha": "43094730458541fd387595dfd61c483f477b95f0", "filename": "lib/bs4/_warnings.py", "status": "added", "additions": 98, "deletions": 0, "changes": 98, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2F_warnings.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2F_warnings.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2F_warnings.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -0,0 +1,98 @@\n+\"\"\"Define some custom warnings.\"\"\"\n+\n+\n+class GuessedAtParserWarning(UserWarning):\n+    \"\"\"The warning issued when BeautifulSoup has to guess what parser to\n+    use -- probably because no parser was specified in the constructor.\n+    \"\"\"\n+\n+    MESSAGE: str = \"\"\"No parser was explicitly specified, so I'm using the best available %(markup_type)s parser for this system (\"%(parser)s\"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.\n+\n+The code that caused this warning is on line %(line_number)s of the file %(filename)s. To get rid of this warning, pass the additional argument 'features=\"%(parser)s\"' to the BeautifulSoup constructor.\n+\"\"\"\n+\n+\n+class UnusualUsageWarning(UserWarning):\n+    \"\"\"A superclass for warnings issued when Beautiful Soup sees\n+    something that is typically the result of a mistake in the calling\n+    code, but might be intentional on the part of the user. If it is\n+    in fact intentional, you can filter the individual warning class\n+    to get rid of the warning. If you don't like Beautiful Soup\n+    second-guessing what you are doing, you can filter the\n+    UnusualUsageWarningclass itself and get rid of these entirely.\n+    \"\"\"\n+\n+\n+class MarkupResemblesLocatorWarning(UnusualUsageWarning):\n+    \"\"\"The warning issued when BeautifulSoup is given 'markup' that\n+    actually looks like a resource locator -- a URL or a path to a file\n+    on disk.\n+    \"\"\"\n+\n+    #: :meta private:\n+    GENERIC_MESSAGE: str = \"\"\"\n+\n+However, if you want to parse some data that happens to look like a %(what)s, then nothing has gone wrong: you are using Beautiful Soup correctly, and this warning is spurious and can be filtered. To make this warning go away, run this code before calling the BeautifulSoup constructor:\n+\n+    from bs4 import MarkupResemblesLocatorWarning\n+    import warnings\n+\n+    warnings.filterwarnings(\"ignore\", category=MarkupResemblesLocatorWarning)\n+    \"\"\"\n+\n+    URL_MESSAGE: str = (\n+        \"\"\"The input passed in on this line looks more like a URL than HTML or XML.\n+\n+If you meant to use Beautiful Soup to parse the web page found at a certain URL, then something has gone wrong. You should use an Python package like 'requests' to fetch the content behind the URL. Once you have the content as a string, you can feed that string into Beautiful Soup.\"\"\"\n+        + GENERIC_MESSAGE\n+    )\n+\n+    FILENAME_MESSAGE: str = (\n+        \"\"\"The input passed in on this line looks more like a filename than HTML or XML.\n+\n+If you meant to use Beautiful Soup to parse the contents of a file on disk, then something has gone wrong. You should open the file first, using code like this:\n+\n+    filehandle = open(your filename)\n+\n+You can then feed the open filehandle into Beautiful Soup instead of using the filename.\"\"\"\n+        + GENERIC_MESSAGE\n+    )\n+\n+\n+class AttributeResemblesVariableWarning(UnusualUsageWarning, SyntaxWarning):\n+    \"\"\"The warning issued when Beautiful Soup suspects a provided\n+    attribute name may actually be the misspelled name of a Beautiful\n+    Soup variable. Generally speaking, this is only used in cases like\n+    \"_class\" where it's very unlikely the user would be referencing an\n+    XML attribute with that name.\n+    \"\"\"\n+\n+    MESSAGE: str = \"\"\"%(original)r is an unusual attribute name and is a common misspelling for %(autocorrect)r.\n+\n+If you meant %(autocorrect)r, change your code to use it, and this warning will go away.\n+\n+If you really did mean to check the %(original)r attribute, this warning is spurious and can be filtered. To make it go away, run this code before creating your BeautifulSoup object:\n+\n+    from bs4 import AttributeResemblesVariableWarning\n+    import warnings\n+\n+    warnings.filterwarnings(\"ignore\", category=AttributeResemblesVariableWarning)\n+\"\"\"\n+\n+\n+class XMLParsedAsHTMLWarning(UnusualUsageWarning):\n+    \"\"\"The warning issued when an HTML parser is used to parse\n+    XML that is not (as far as we can tell) XHTML.\n+    \"\"\"\n+\n+    MESSAGE: str = \"\"\"It looks like you're using an HTML parser to parse an XML document.\n+\n+Assuming this really is an XML document, what you're doing might work, but you should know that using an XML parser will be more reliable. To parse this document as XML, make sure you have the Python package 'lxml' installed, and pass the keyword argument `features=\"xml\"` into the BeautifulSoup constructor.\n+\n+If you want or need to use an HTML parser on this document, you can make this warning go away by filtering it. To do that, run this code before calling the BeautifulSoup constructor:\n+\n+    from bs4 import XMLParsedAsHTMLWarning\n+    import warnings\n+\n+    warnings.filterwarnings(\"ignore\", category=XMLParsedAsHTMLWarning)\n+\"\"\""}, {"sha": "4aae4d330789cf01057918d25eea372f739d6e56", "filename": "lib/bs4/builder/__init__.py", "status": "modified", "additions": 519, "deletions": 307, "changes": 826, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fbuilder%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fbuilder%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fbuilder%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,64 +1,111 @@\n+from __future__ import annotations\n+\n # Use of this source code is governed by the MIT license.\n __license__ = \"MIT\"\n \n from collections import defaultdict\n-import itertools\n import re\n+from types import ModuleType\n+from typing import (\n+    Any,\n+    cast,\n+    Dict,\n+    Iterable,\n+    List,\n+    Optional,\n+    Pattern,\n+    Set,\n+    Tuple,\n+    Type,\n+    TYPE_CHECKING,\n+)\n import warnings\n import sys\n from bs4.element import (\n+    AttributeDict,\n+    AttributeValueList,\n     CharsetMetaAttributeValue,\n     ContentMetaAttributeValue,\n     RubyParenthesisString,\n     RubyTextString,\n     Stylesheet,\n     Script,\n     TemplateString,\n-    nonwhitespace_re\n+    nonwhitespace_re,\n+)\n+\n+# Exceptions were moved to their own module in 4.13. Import here for\n+# backwards compatibility.\n+from bs4.exceptions import ParserRejectedMarkup\n+\n+from bs4._typing import (\n+    _AttributeValues,\n+    _RawAttributeValue,\n )\n \n+from bs4._warnings import XMLParsedAsHTMLWarning\n+\n+if TYPE_CHECKING:\n+    from bs4 import BeautifulSoup\n+    from bs4.element import (\n+        NavigableString,\n+        Tag,\n+    )\n+    from bs4._typing import (\n+        _AttributeValue,\n+        _Encoding,\n+        _Encodings,\n+        _RawOrProcessedAttributeValues,\n+        _RawMarkup,\n+    )\n+\n __all__ = [\n-    'HTMLTreeBuilder',\n-    'SAXTreeBuilder',\n-    'TreeBuilder',\n-    'TreeBuilderRegistry',\n-    ]\n+    \"HTMLTreeBuilder\",\n+    \"SAXTreeBuilder\",\n+    \"TreeBuilder\",\n+    \"TreeBuilderRegistry\",\n+]\n \n # Some useful features for a TreeBuilder to have.\n-FAST = 'fast'\n-PERMISSIVE = 'permissive'\n-STRICT = 'strict'\n-XML = 'xml'\n-HTML = 'html'\n-HTML_5 = 'html5'\n-\n-class XMLParsedAsHTMLWarning(UserWarning):\n-    \"\"\"The warning issued when an HTML parser is used to parse\n-    XML that is not XHTML.\n-    \"\"\"\n-    MESSAGE = \"\"\"It looks like you're parsing an XML document using an HTML parser. If this really is an HTML document (maybe it's XHTML?), you can ignore or filter this warning. If it's XML, you should know that using an XML parser will be more reliable. To parse this document as XML, make sure you have the lxml package installed, and pass the keyword argument `features=\"xml\"` into the BeautifulSoup constructor.\"\"\"\n+FAST = \"fast\"\n+PERMISSIVE = \"permissive\"\n+STRICT = \"strict\"\n+XML = \"xml\"\n+HTML = \"html\"\n+HTML_5 = \"html5\"\n+\n+__all__ = [\n+    \"TreeBuilderRegistry\",\n+    \"TreeBuilder\",\n+    \"HTMLTreeBuilder\",\n+    \"DetectsXMLParsedAsHTML\",\n \n+    \"ParserRejectedMarkup\", # backwards compatibility only as of 4.13.0\n+]\n \n class TreeBuilderRegistry(object):\n     \"\"\"A way of looking up TreeBuilder subclasses by their name or by desired\n     features.\n     \"\"\"\n-    \n-    def __init__(self):\n+\n+    builders_for_feature: Dict[str, List[Type[TreeBuilder]]]\n+    builders: List[Type[TreeBuilder]]\n+\n+    def __init__(self) -> None:\n         self.builders_for_feature = defaultdict(list)\n         self.builders = []\n \n-    def register(self, treebuilder_class):\n+    def register(self, treebuilder_class: type[TreeBuilder]) -> None:\n         \"\"\"Register a treebuilder based on its advertised features.\n \n-        :param treebuilder_class: A subclass of Treebuilder. its .features\n-           attribute should list its features.\n+        :param treebuilder_class: A subclass of `TreeBuilder`. its\n+           `TreeBuilder.features` attribute should list its features.\n         \"\"\"\n         for feature in treebuilder_class.features:\n             self.builders_for_feature[feature].insert(0, treebuilder_class)\n         self.builders.insert(0, treebuilder_class)\n \n-    def lookup(self, *features):\n+    def lookup(self, *features: str) -> Optional[Type[TreeBuilder]]:\n         \"\"\"Look up a TreeBuilder subclass with the desired features.\n \n         :param features: A list of features to look for. If none are\n@@ -78,131 +125,169 @@ def lookup(self, *features):\n \n         # Go down the list of features in order, and eliminate any builders\n         # that don't match every feature.\n-        features = list(features)\n-        features.reverse()\n+        feature_list = list(features)\n+        feature_list.reverse()\n         candidates = None\n         candidate_set = None\n-        while len(features) > 0:\n-            feature = features.pop()\n+        while len(feature_list) > 0:\n+            feature = feature_list.pop()\n             we_have_the_feature = self.builders_for_feature.get(feature, [])\n             if len(we_have_the_feature) > 0:\n                 if candidates is None:\n                     candidates = we_have_the_feature\n                     candidate_set = set(candidates)\n-                else:\n+                elif candidate_set is not None:\n                     # Eliminate any candidates that don't have this feature.\n-                    candidate_set = candidate_set.intersection(\n-                        set(we_have_the_feature))\n+                    candidate_set = candidate_set.intersection(set(we_have_the_feature))\n \n         # The only valid candidates are the ones in candidate_set.\n         # Go through the original list of candidates and pick the first one\n         # that's in candidate_set.\n-        if candidate_set is None:\n+        if candidate_set is None or candidates is None:\n             return None\n         for candidate in candidates:\n             if candidate in candidate_set:\n                 return candidate\n         return None\n \n-# The BeautifulSoup class will take feature lists from developers and use them\n-# to look up builders in this registry.\n-builder_registry = TreeBuilderRegistry()\n+\n+#: The `BeautifulSoup` constructor will take a list of features\n+#: and use it to look up `TreeBuilder` classes in this registry.\n+builder_registry: TreeBuilderRegistry = TreeBuilderRegistry()\n+\n \n class TreeBuilder(object):\n-    \"\"\"Turn a textual document into a Beautiful Soup object tree.\"\"\"\n-\n-    NAME = \"[Unknown tree builder]\"\n-    ALTERNATE_NAMES = []\n-    features = []\n-\n-    is_xml = False\n-    picklable = False\n-    empty_element_tags = None # A tag will be considered an empty-element\n-                              # tag when and only when it has no contents.\n-    \n-    # A value for these tag/attribute combinations is a space- or\n-    # comma-separated list of CDATA, rather than a single CDATA.\n-    DEFAULT_CDATA_LIST_ATTRIBUTES = defaultdict(list)\n-\n-    # Whitespace should be preserved inside these tags.\n-    DEFAULT_PRESERVE_WHITESPACE_TAGS = set()\n-\n-    # The textual contents of tags with these names should be\n-    # instantiated with some class other than NavigableString.\n-    DEFAULT_STRING_CONTAINERS = {}\n-    \n-    USE_DEFAULT = object()\n-\n-    # Most parsers don't keep track of line numbers.\n-    TRACKS_LINE_NUMBERS = False\n-    \n-    def __init__(self, multi_valued_attributes=USE_DEFAULT,\n-                 preserve_whitespace_tags=USE_DEFAULT,\n-                 store_line_numbers=USE_DEFAULT,\n-                 string_containers=USE_DEFAULT,\n+    \"\"\"Turn a textual document into a Beautiful Soup object tree.\n+\n+    This is an abstract superclass which smooths out the behavior of\n+    different parser libraries into a single, unified interface.\n+\n+    :param multi_valued_attributes: If this is set to None, the\n+     TreeBuilder will not turn any values for attributes like\n+     'class' into lists. Setting this to a dictionary will\n+     customize this behavior; look at :py:attr:`bs4.builder.HTMLTreeBuilder.DEFAULT_CDATA_LIST_ATTRIBUTES`\n+     for an example.\n+\n+     Internally, these are called \"CDATA list attributes\", but that\n+     probably doesn't make sense to an end-user, so the argument name\n+     is ``multi_valued_attributes``.\n+\n+    :param preserve_whitespace_tags: A set of tags to treat\n+     the way <pre> tags are treated in HTML. Tags in this set\n+     are immune from pretty-printing; their contents will always be\n+     output as-is.\n+\n+    :param string_containers: A dictionary mapping tag names to\n+     the classes that should be instantiated to contain the textual\n+     contents of those tags. The default is to use NavigableString\n+     for every tag, no matter what the name. You can override the\n+     default by changing :py:attr:`DEFAULT_STRING_CONTAINERS`.\n+\n+    :param store_line_numbers: If the parser keeps track of the line\n+     numbers and positions of the original markup, that information\n+     will, by default, be stored in each corresponding\n+     :py:class:`bs4.element.Tag` object. You can turn this off by\n+     passing store_line_numbers=False; then Tag.sourcepos and\n+     Tag.sourceline will always be None. If the parser you're using\n+     doesn't keep track of this information, then store_line_numbers\n+     is irrelevant.\n+\n+    :param attribute_dict_class: The value of a multi-valued attribute\n+      (such as HTML's 'class') willl be stored in an instance of this\n+      class.  The default is Beautiful Soup's built-in\n+      `AttributeValueList`, which is a normal Python list, and you\n+      will probably never need to change it.\n+    \"\"\"\n+\n+    USE_DEFAULT: Any = object()  #: :meta private:\n+\n+    def __init__(\n+        self,\n+        multi_valued_attributes: Dict[str, Set[str]] = USE_DEFAULT,\n+        preserve_whitespace_tags: Set[str] = USE_DEFAULT,\n+        store_line_numbers: bool = USE_DEFAULT,\n+        string_containers: Dict[str, Type[NavigableString]] = USE_DEFAULT,\n+        empty_element_tags: Set[str] = USE_DEFAULT,\n+        attribute_dict_class: Type[AttributeDict] = AttributeDict,\n+        attribute_value_list_class: Type[AttributeValueList] = AttributeValueList,\n     ):\n-        \"\"\"Constructor.\n-\n-        :param multi_valued_attributes: If this is set to None, the\n-         TreeBuilder will not turn any values for attributes like\n-         'class' into lists. Setting this to a dictionary will\n-         customize this behavior; look at DEFAULT_CDATA_LIST_ATTRIBUTES\n-         for an example.\n-\n-         Internally, these are called \"CDATA list attributes\", but that\n-         probably doesn't make sense to an end-user, so the argument name\n-         is `multi_valued_attributes`.\n-\n-        :param preserve_whitespace_tags: A list of tags to treat\n-         the way <pre> tags are treated in HTML. Tags in this list\n-         are immune from pretty-printing; their contents will always be\n-         output as-is.\n-\n-        :param string_containers: A dictionary mapping tag names to\n-        the classes that should be instantiated to contain the textual\n-        contents of those tags. The default is to use NavigableString\n-        for every tag, no matter what the name. You can override the\n-        default by changing DEFAULT_STRING_CONTAINERS.\n-\n-        :param store_line_numbers: If the parser keeps track of the\n-         line numbers and positions of the original markup, that\n-         information will, by default, be stored in each corresponding\n-         `Tag` object. You can turn this off by passing\n-         store_line_numbers=False. If the parser you're using doesn't \n-         keep track of this information, then setting store_line_numbers=True\n-         will do nothing.\n-        \"\"\"\n         self.soup = None\n         if multi_valued_attributes is self.USE_DEFAULT:\n             multi_valued_attributes = self.DEFAULT_CDATA_LIST_ATTRIBUTES\n         self.cdata_list_attributes = multi_valued_attributes\n         if preserve_whitespace_tags is self.USE_DEFAULT:\n             preserve_whitespace_tags = self.DEFAULT_PRESERVE_WHITESPACE_TAGS\n         self.preserve_whitespace_tags = preserve_whitespace_tags\n+        if empty_element_tags is self.USE_DEFAULT:\n+            self.empty_element_tags = self.DEFAULT_EMPTY_ELEMENT_TAGS\n+        else:\n+            self.empty_element_tags = empty_element_tags\n+        # TODO: store_line_numbers is probably irrelevant now that\n+        # the behavior of sourceline and sourcepos has been made consistent\n+        # everywhere.\n         if store_line_numbers == self.USE_DEFAULT:\n             store_line_numbers = self.TRACKS_LINE_NUMBERS\n-        self.store_line_numbers = store_line_numbers \n+        self.store_line_numbers = store_line_numbers\n         if string_containers == self.USE_DEFAULT:\n             string_containers = self.DEFAULT_STRING_CONTAINERS\n         self.string_containers = string_containers\n-        \n-    def initialize_soup(self, soup):\n+        self.attribute_dict_class = attribute_dict_class\n+        self.attribute_value_list_class = attribute_value_list_class\n+\n+    NAME: str = \"[Unknown tree builder]\"\n+    ALTERNATE_NAMES: Iterable[str] = []\n+    features: Iterable[str] = []\n+\n+    is_xml: bool = False\n+    picklable: bool = False\n+\n+    soup: Optional[BeautifulSoup]  #: :meta private:\n+\n+    #: A tag will be considered an empty-element\n+    #: tag when and only when it has no contents.\n+    empty_element_tags: Optional[Set[str]] = None  #: :meta private:\n+    cdata_list_attributes: Dict[str, Set[str]]  #: :meta private:\n+    preserve_whitespace_tags: Set[str]  #: :meta private:\n+    string_containers: Dict[str, Type[NavigableString]]  #: :meta private:\n+    tracks_line_numbers: bool  #: :meta private:\n+\n+    #: A value for these tag/attribute combinations is a space- or\n+    #: comma-separated list of CDATA, rather than a single CDATA.\n+    DEFAULT_CDATA_LIST_ATTRIBUTES: Dict[str, Set[str]] = defaultdict(set)\n+\n+    #: Whitespace should be preserved inside these tags.\n+    DEFAULT_PRESERVE_WHITESPACE_TAGS: Set[str] = set()\n+\n+    #: The textual contents of tags with these names should be\n+    #: instantiated with some class other than `bs4.element.NavigableString`.\n+    DEFAULT_STRING_CONTAINERS: Dict[str, Type[bs4.element.NavigableString]] = {} # type:ignore\n+\n+    #: By default, tags are treated as empty-element tags if they have\n+    #: no contents--that is, using XML rules. HTMLTreeBuilder\n+    #: defines a different set of DEFAULT_EMPTY_ELEMENT_TAGS based on the\n+    #: HTML 4 and HTML5 standards.\n+    DEFAULT_EMPTY_ELEMENT_TAGS: Optional[Set[str]] = None\n+\n+    #: Most parsers don't keep track of line numbers.\n+    TRACKS_LINE_NUMBERS: bool = False\n+\n+    def initialize_soup(self, soup: BeautifulSoup) -> None:\n         \"\"\"The BeautifulSoup object has been initialized and is now\n         being associated with the TreeBuilder.\n \n         :param soup: A BeautifulSoup object.\n         \"\"\"\n         self.soup = soup\n-        \n-    def reset(self):\n+\n+    def reset(self) -> None:\n         \"\"\"Do any work necessary to reset the underlying parser\n         for a new document.\n \n         By default, this does nothing.\n         \"\"\"\n         pass\n \n-    def can_be_empty_element(self, tag_name):\n+    def can_be_empty_element(self, tag_name: str) -> bool:\n         \"\"\"Might a tag with this name be an empty-element tag?\n \n         The final markup may or may not actually present this tag as\n@@ -224,47 +309,48 @@ def can_be_empty_element(self, tag_name):\n         if self.empty_element_tags is None:\n             return True\n         return tag_name in self.empty_element_tags\n-    \n-    def feed(self, markup):\n-        \"\"\"Run some incoming markup through some parsing process,\n-        populating the `BeautifulSoup` object in self.soup.\n-\n-        This method is not implemented in TreeBuilder; it must be\n-        implemented in subclasses.\n \n-        :return: None.\n-        \"\"\"\n+    def feed(self, markup: _RawMarkup) -> None:\n+        \"\"\"Run incoming markup through some parsing process.\"\"\"\n         raise NotImplementedError()\n \n-    def prepare_markup(self, markup, user_specified_encoding=None,\n-                       document_declared_encoding=None, exclude_encodings=None):\n+    def prepare_markup(\n+        self,\n+        markup: _RawMarkup,\n+        user_specified_encoding: Optional[_Encoding] = None,\n+        document_declared_encoding: Optional[_Encoding] = None,\n+        exclude_encodings: Optional[_Encodings] = None,\n+    ) -> Iterable[Tuple[_RawMarkup, Optional[_Encoding], Optional[_Encoding], bool]]:\n         \"\"\"Run any preliminary steps necessary to make incoming markup\n         acceptable to the parser.\n \n-        :param markup: Some markup -- probably a bytestring.\n-        :param user_specified_encoding: The user asked to try this encoding.\n+        :param markup: The markup that's about to be parsed.\n+        :param user_specified_encoding: The user asked to try this encoding\n+           to convert the markup into a Unicode string.\n         :param document_declared_encoding: The markup itself claims to be\n             in this encoding. NOTE: This argument is not used by the\n             calling code and can probably be removed.\n-        :param exclude_encodings: The user asked _not_ to try any of\n+        :param exclude_encodings: The user asked *not* to try any of\n             these encodings.\n \n-        :yield: A series of 4-tuples:\n-         (markup, encoding, declared encoding,\n-          has undergone character replacement)\n+        :yield: A series of 4-tuples: (markup, encoding, declared encoding,\n+            has undergone character replacement)\n \n-         Each 4-tuple represents a strategy for converting the\n-         document to Unicode and parsing it. Each strategy will be tried \n-         in turn.\n+            Each 4-tuple represents a strategy that the parser can try\n+            to convert the document to Unicode and parse it. Each\n+            strategy will be tried in turn.\n \n          By default, the only strategy is to parse the markup\n          as-is. See `LXMLTreeBuilderForXML` and\n          `HTMLParserTreeBuilder` for implementations that take into\n          account the quirks of particular parsers.\n+\n+        :meta private:\n+\n         \"\"\"\n         yield markup, None, None, False\n \n-    def test_fragment_to_document(self, fragment):\n+    def test_fragment_to_document(self, fragment: str) -> str:\n         \"\"\"Wrap an HTML fragment to make it look like a document.\n \n         Different parsers do this differently. For instance, lxml\n@@ -273,26 +359,29 @@ def test_fragment_to_document(self, fragment):\n         which run HTML fragments through the parser and compare the\n         results against other HTML fragments.\n \n-        This method should not be used outside of tests.\n+        This method should not be used outside of unit tests.\n \n-        :param fragment: A string -- fragment of HTML.\n-        :return: A string -- a full HTML document.\n+        :param fragment: A fragment of HTML.\n+        :return: A full HTML document.\n+        :meta private:\n         \"\"\"\n         return fragment\n \n-    def set_up_substitutions(self, tag):\n-        \"\"\"Set up any substitutions that will need to be performed on \n+    def set_up_substitutions(self, tag: Tag) -> bool:\n+        \"\"\"Set up any substitutions that will need to be performed on\n         a `Tag` when it's output as a string.\n \n         By default, this does nothing. See `HTMLTreeBuilder` for a\n         case where this is used.\n \n-        :param tag: A `Tag`\n         :return: Whether or not a substitution was performed.\n+        :meta private:\n         \"\"\"\n         return False\n \n-    def _replace_cdata_list_attribute_values(self, tag_name, attrs):\n+    def _replace_cdata_list_attribute_values(\n+        self, tag_name: str, attrs: _RawOrProcessedAttributeValues\n+    ) -> _AttributeValues:\n         \"\"\"When an attribute value is associated with a tag that can\n         have multiple values for that attribute, convert the string\n         value to a list of strings.\n@@ -304,170 +393,273 @@ def _replace_cdata_list_attribute_values(self, tag_name, attrs):\n         :param tag_name: The name of a tag.\n         :param attrs: A dictionary containing the tag's attributes.\n            Any appropriate attribute values will be modified in place.\n+        :return: The modified dictionary that was originally passed in.\n         \"\"\"\n-        if not attrs:\n-            return attrs\n-        if self.cdata_list_attributes:\n-            universal = self.cdata_list_attributes.get('*', [])\n-            tag_specific = self.cdata_list_attributes.get(\n-                tag_name.lower(), None)\n-            for attr in list(attrs.keys()):\n-                if attr in universal or (tag_specific and attr in tag_specific):\n-                    # We have a \"class\"-type attribute whose string\n-                    # value is a whitespace-separated list of\n-                    # values. Split it into a list.\n-                    value = attrs[attr]\n-                    if isinstance(value, str):\n-                        values = nonwhitespace_re.findall(value)\n-                    else:\n-                        # html5lib sometimes calls setAttributes twice\n-                        # for the same tag when rearranging the parse\n-                        # tree. On the second call the attribute value\n-                        # here is already a list.  If this happens,\n-                        # leave the value alone rather than trying to\n-                        # split it again.\n-                        values = value\n-                    attrs[attr] = values\n-        return attrs\n-    \n+\n+        # First, cast the attrs dict to _AttributeValues. This might\n+        # not be accurate yet, but it will be by the time this method\n+        # returns.\n+        modified_attrs = cast(_AttributeValues, attrs)\n+        if not modified_attrs or not self.cdata_list_attributes:\n+            # Nothing to do.\n+            return modified_attrs\n+\n+        # There is at least a possibility that we need to modify one of\n+        # the attribute values.\n+        universal: Set[str] = self.cdata_list_attributes.get(\"*\", set())\n+        tag_specific = self.cdata_list_attributes.get(tag_name.lower(), None)\n+        for attr in list(modified_attrs.keys()):\n+            modified_value: _AttributeValue\n+            if attr in universal or (tag_specific and attr in tag_specific):\n+                # We have a \"class\"-type attribute whose string\n+                # value is a whitespace-separated list of\n+                # values. Split it into a list.\n+                original_value: _AttributeValue = modified_attrs[attr]\n+                if isinstance(original_value, _RawAttributeValue):\n+                    # This is a _RawAttributeValue (a string) that\n+                    # needs to be split and converted to a\n+                    # AttributeValueList so it can be an\n+                    # _AttributeValue.\n+                    modified_value = self.attribute_value_list_class(\n+                        nonwhitespace_re.findall(original_value)\n+                    )\n+                else:\n+                    # html5lib calls setAttributes twice for the\n+                    # same tag when rearranging the parse tree. On\n+                    # the second call the attribute value here is\n+                    # already a list. This can also happen when a\n+                    # Tag object is cloned. If this happens, leave\n+                    # the value alone rather than trying to split\n+                    # it again.\n+                    modified_value = original_value\n+                modified_attrs[attr] = modified_value\n+        return modified_attrs\n+\n+\n class SAXTreeBuilder(TreeBuilder):\n     \"\"\"A Beautiful Soup treebuilder that listens for SAX events.\n \n-    This is not currently used for anything, but it demonstrates\n-    how a simple TreeBuilder would work.\n+    This is not currently used for anything, and it will be removed\n+    soon. It was a good idea, but it wasn't properly integrated into the\n+    rest of Beautiful Soup, so there have been long stretches where it\n+    hasn't worked properly.\n     \"\"\"\n \n-    def feed(self, markup):\n+    def __init__(self, *args: Any, **kwargs: Any) -> None:\n+        warnings.warn(\n+            \"The SAXTreeBuilder class was deprecated in 4.13.0 and will be removed soon thereafter. It is completely untested and probably doesn't work; do not use it.\",\n+            DeprecationWarning,\n+            stacklevel=2,\n+        )\n+        super(SAXTreeBuilder, self).__init__(*args, **kwargs)\n+\n+    def feed(self, markup: _RawMarkup) -> None:\n         raise NotImplementedError()\n \n-    def close(self):\n+    def close(self) -> None:\n         pass\n \n-    def startElement(self, name, attrs):\n-        attrs = dict((key[1], value) for key, value in list(attrs.items()))\n-        #print(\"Start %s, %r\" % (name, attrs))\n-        self.soup.handle_starttag(name, attrs)\n+    def startElement(self, name: str, attrs: Dict[str, str]) -> None:\n+        attrs = AttributeDict((key[1], value) for key, value in list(attrs.items()))\n+        # print(\"Start %s, %r\" % (name, attrs))\n+        assert self.soup is not None\n+        self.soup.handle_starttag(name, None, None, attrs)\n \n-    def endElement(self, name):\n-        #print(\"End %s\" % name)\n+    def endElement(self, name: str) -> None:\n+        # print(\"End %s\" % name)\n+        assert self.soup is not None\n         self.soup.handle_endtag(name)\n \n-    def startElementNS(self, nsTuple, nodeName, attrs):\n+    def startElementNS(\n+        self, nsTuple: Tuple[str, str], nodeName: str, attrs: Dict[str, str]\n+    ) -> None:\n         # Throw away (ns, nodeName) for now.\n         self.startElement(nodeName, attrs)\n \n-    def endElementNS(self, nsTuple, nodeName):\n+    def endElementNS(self, nsTuple: Tuple[str, str], nodeName: str) -> None:\n         # Throw away (ns, nodeName) for now.\n         self.endElement(nodeName)\n-        #handler.endElementNS((ns, node.nodeName), node.nodeName)\n+        # handler.endElementNS((ns, node.nodeName), node.nodeName)\n \n-    def startPrefixMapping(self, prefix, nodeValue):\n+    def startPrefixMapping(self, prefix: str, nodeValue: str) -> None:\n         # Ignore the prefix for now.\n         pass\n \n-    def endPrefixMapping(self, prefix):\n+    def endPrefixMapping(self, prefix: str) -> None:\n         # Ignore the prefix for now.\n         # handler.endPrefixMapping(prefix)\n         pass\n \n-    def characters(self, content):\n+    def characters(self, content: str) -> None:\n+        assert self.soup is not None\n         self.soup.handle_data(content)\n \n-    def startDocument(self):\n+    def startDocument(self) -> None:\n         pass\n \n-    def endDocument(self):\n+    def endDocument(self) -> None:\n         pass\n \n \n class HTMLTreeBuilder(TreeBuilder):\n-    \"\"\"This TreeBuilder knows facts about HTML.\n-\n-    Such as which tags are empty-element tags.\n+    \"\"\"This TreeBuilder knows facts about HTML, such as which tags are treated\n+    specially by the HTML standard.\n     \"\"\"\n \n-    empty_element_tags = set([\n-        # These are from HTML5.\n-        'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr',\n-        \n-        # These are from earlier versions of HTML and are removed in HTML5.\n-        'basefont', 'bgsound', 'command', 'frame', 'image', 'isindex', 'nextid', 'spacer'\n-    ])\n-\n-    # The HTML standard defines these as block-level elements. Beautiful\n-    # Soup does not treat these elements differently from other elements,\n-    # but it may do so eventually, and this information is available if\n-    # you need to use it.\n-    block_elements = set([\"address\", \"article\", \"aside\", \"blockquote\", \"canvas\", \"dd\", \"div\", \"dl\", \"dt\", \"fieldset\", \"figcaption\", \"figure\", \"footer\", \"form\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"header\", \"hr\", \"li\", \"main\", \"nav\", \"noscript\", \"ol\", \"output\", \"p\", \"pre\", \"section\", \"table\", \"tfoot\", \"ul\", \"video\"])\n-\n-    # These HTML tags need special treatment so they can be\n-    # represented by a string class other than NavigableString.\n-    #\n-    # For some of these tags, it's because the HTML standard defines\n-    # an unusual content model for them. I made this list by going\n-    # through the HTML spec\n-    # (https://html.spec.whatwg.org/#metadata-content) and looking for\n-    # \"metadata content\" elements that can contain strings.\n-    #\n-    # The Ruby tags (<rt> and <rp>) are here despite being normal\n-    # \"phrasing content\" tags, because the content they contain is\n-    # qualitatively different from other text in the document, and it\n-    # can be useful to be able to distinguish it.\n-    #\n-    # TODO: Arguably <noscript> could go here but it seems\n-    # qualitatively different from the other tags.\n-    DEFAULT_STRING_CONTAINERS = {\n-        'rt' : RubyTextString,\n-        'rp' : RubyParenthesisString,\n-        'style': Stylesheet,\n-        'script': Script,\n-        'template': TemplateString,\n-    }    \n-    \n-    # The HTML standard defines these attributes as containing a\n-    # space-separated list of values, not a single value. That is,\n-    # class=\"foo bar\" means that the 'class' attribute has two values,\n-    # 'foo' and 'bar', not the single value 'foo bar'.  When we\n-    # encounter one of these attributes, we will parse its value into\n-    # a list of values if possible. Upon output, the list will be\n-    # converted back into a string.\n-    DEFAULT_CDATA_LIST_ATTRIBUTES = {\n-        \"*\" : ['class', 'accesskey', 'dropzone'],\n-        \"a\" : ['rel', 'rev'],\n-        \"link\" :  ['rel', 'rev'],\n-        \"td\" : [\"headers\"],\n-        \"th\" : [\"headers\"],\n-        \"td\" : [\"headers\"],\n-        \"form\" : [\"accept-charset\"],\n-        \"object\" : [\"archive\"],\n-\n+    #: Some HTML tags are defined as having no contents. Beautiful Soup\n+    #: treats these specially.\n+    DEFAULT_EMPTY_ELEMENT_TAGS: Optional[Set[str]] = set(\n+        [\n+            # These are from HTML5.\n+            \"area\",\n+            \"base\",\n+            \"br\",\n+            \"col\",\n+            \"embed\",\n+            \"hr\",\n+            \"img\",\n+            \"input\",\n+            \"keygen\",\n+            \"link\",\n+            \"menuitem\",\n+            \"meta\",\n+            \"param\",\n+            \"source\",\n+            \"track\",\n+            \"wbr\",\n+            # These are from earlier versions of HTML and are removed in HTML5.\n+            \"basefont\",\n+            \"bgsound\",\n+            \"command\",\n+            \"frame\",\n+            \"image\",\n+            \"isindex\",\n+            \"nextid\",\n+            \"spacer\",\n+        ]\n+    )\n+\n+    #: The HTML standard defines these tags as block-level elements. Beautiful\n+    #: Soup does not treat these elements differently from other elements,\n+    #: but it may do so eventually, and this information is available if\n+    #: you need to use it.\n+    DEFAULT_BLOCK_ELEMENTS: Set[str] = set(\n+        [\n+            \"address\",\n+            \"article\",\n+            \"aside\",\n+            \"blockquote\",\n+            \"canvas\",\n+            \"dd\",\n+            \"div\",\n+            \"dl\",\n+            \"dt\",\n+            \"fieldset\",\n+            \"figcaption\",\n+            \"figure\",\n+            \"footer\",\n+            \"form\",\n+            \"h1\",\n+            \"h2\",\n+            \"h3\",\n+            \"h4\",\n+            \"h5\",\n+            \"h6\",\n+            \"header\",\n+            \"hr\",\n+            \"li\",\n+            \"main\",\n+            \"nav\",\n+            \"noscript\",\n+            \"ol\",\n+            \"output\",\n+            \"p\",\n+            \"pre\",\n+            \"section\",\n+            \"table\",\n+            \"tfoot\",\n+            \"ul\",\n+            \"video\",\n+        ]\n+    )\n+\n+    #: These HTML tags need special treatment so they can be\n+    #: represented by a string class other than `bs4.element.NavigableString`.\n+    #:\n+    #: For some of these tags, it's because the HTML standard defines\n+    #: an unusual content model for them. I made this list by going\n+    #: through the HTML spec\n+    #: (https://html.spec.whatwg.org/#metadata-content) and looking for\n+    #: \"metadata content\" elements that can contain strings.\n+    #:\n+    #: The Ruby tags (<rt> and <rp>) are here despite being normal\n+    #: \"phrasing content\" tags, because the content they contain is\n+    #: qualitatively different from other text in the document, and it\n+    #: can be useful to be able to distinguish it.\n+    #:\n+    #: TODO: Arguably <noscript> could go here but it seems\n+    #: qualitatively different from the other tags.\n+    DEFAULT_STRING_CONTAINERS: Dict[str, Type[bs4.element.NavigableString]] = { # type:ignore\n+        \"rt\": RubyTextString,\n+        \"rp\": RubyParenthesisString,\n+        \"style\": Stylesheet,\n+        \"script\": Script,\n+        \"template\": TemplateString,\n+    }\n+\n+    #: The HTML standard defines these attributes as containing a\n+    #: space-separated list of values, not a single value. That is,\n+    #: class=\"foo bar\" means that the 'class' attribute has two values,\n+    #: 'foo' and 'bar', not the single value 'foo bar'.  When we\n+    #: encounter one of these attributes, we will parse its value into\n+    #: a list of values if possible. Upon output, the list will be\n+    #: converted back into a string.\n+    DEFAULT_CDATA_LIST_ATTRIBUTES: Dict[str, Set[str]] = {\n+        \"*\": {\"class\", \"accesskey\", \"dropzone\"},\n+        \"a\": {\"rel\", \"rev\"},\n+        \"link\": {\"rel\", \"rev\"},\n+        \"td\": {\"headers\"},\n+        \"th\": {\"headers\"},\n+        \"form\": {\"accept-charset\"},\n+        \"object\": {\"archive\"},\n         # These are HTML5 specific, as are *.accesskey and *.dropzone above.\n-        \"area\" : [\"rel\"],\n-        \"icon\" : [\"sizes\"],\n-        \"iframe\" : [\"sandbox\"],\n-        \"output\" : [\"for\"],\n-        }\n+        \"area\": {\"rel\"},\n+        \"icon\": {\"sizes\"},\n+        \"iframe\": {\"sandbox\"},\n+        \"output\": {\"for\"},\n+    }\n \n-    DEFAULT_PRESERVE_WHITESPACE_TAGS = set(['pre', 'textarea'])\n+    #: By default, whitespace inside these HTML tags will be\n+    #: preserved rather than being collapsed.\n+    DEFAULT_PRESERVE_WHITESPACE_TAGS: set[str] = set([\"pre\", \"textarea\"])\n \n-    def set_up_substitutions(self, tag):\n+    def set_up_substitutions(self, tag: Tag) -> bool:\n         \"\"\"Replace the declared encoding in a <meta> tag with a placeholder,\n         to be substituted when the tag is output to a string.\n \n         An HTML document may come in to Beautiful Soup as one\n         encoding, but exit in a different encoding, and the <meta> tag\n         needs to be changed to reflect this.\n \n-        :param tag: A `Tag`\n         :return: Whether or not a substitution was performed.\n+\n+        :meta private:\n         \"\"\"\n         # We are only interested in <meta> tags\n-        if tag.name != 'meta':\n+        if tag.name != \"meta\":\n             return False\n \n-        http_equiv = tag.get('http-equiv')\n-        content = tag.get('content')\n-        charset = tag.get('charset')\n+        # TODO: This cast will fail in the (very unlikely) scenario\n+        # that the programmer who instantiates the TreeBuilder\n+        # specifies meta['content'] or meta['charset'] as\n+        # cdata_list_attributes.\n+        content: Optional[str] = cast(Optional[str], tag.get(\"content\"))\n+        charset: Optional[str] = cast(Optional[str], tag.get(\"charset\"))\n+\n+        # But we can accommodate meta['http-equiv'] being made a\n+        # cdata_list_attribute (again, very unlikely) without much\n+        # trouble.\n+        http_equiv: List[str] = tag.get_attribute_list(\"http-equiv\")\n \n         # We are interested in <meta> tags that say what encoding the\n         # document was originally in. This means HTML 5-style <meta>\n@@ -478,20 +670,23 @@ def set_up_substitutions(self, tag):\n         # In both cases we will replace the value of the appropriate\n         # attribute with a standin object that can take on any\n         # encoding.\n-        meta_encoding = None\n+        substituted = False\n         if charset is not None:\n             # HTML 5 style:\n             # <meta charset=\"utf8\">\n-            meta_encoding = charset\n-            tag['charset'] = CharsetMetaAttributeValue(charset)\n+            tag[\"charset\"] = CharsetMetaAttributeValue(charset)\n+            substituted = True\n \n-        elif (content is not None and http_equiv is not None\n-              and http_equiv.lower() == 'content-type'):\n+        elif content is not None and any(\n+            x.lower() == \"content-type\" for x in http_equiv\n+        ):\n             # HTML 4 style:\n             # <meta http-equiv=\"content-type\" content=\"text/html; charset=utf8\">\n-            tag['content'] = ContentMetaAttributeValue(content)\n+            tag[\"content\"] = ContentMetaAttributeValue(content)\n+            substituted = True\n+\n+        return substituted\n \n-        return (meta_encoding is not None)\n \n class DetectsXMLParsedAsHTML(object):\n     \"\"\"A mixin class for any class (a TreeBuilder, or some class used by a\n@@ -502,66 +697,88 @@ class DetectsXMLParsedAsHTML(object):\n     This requires being able to observe an incoming processing\n     instruction that might be an XML declaration, and also able to\n     observe tags as they're opened. If you can't do that for a given\n-    TreeBuilder, there's a less reliable implementation based on\n+    `TreeBuilder`, there's a less reliable implementation based on\n     examining the raw markup.\n     \"\"\"\n \n-    # Regular expression for seeing if markup has an <html> tag.\n-    LOOKS_LIKE_HTML = re.compile(\"<[^ +]html\", re.I)\n-    LOOKS_LIKE_HTML_B = re.compile(b\"<[^ +]html\", re.I)\n+    #: Regular expression for seeing if string markup has an <html> tag.\n+    LOOKS_LIKE_HTML: Pattern[str] = re.compile(\"<[^ +]html\", re.I)\n+\n+    #: Regular expression for seeing if byte markup has an <html> tag.\n+    LOOKS_LIKE_HTML_B: Pattern[bytes] = re.compile(b\"<[^ +]html\", re.I)\n+\n+    #: The start of an XML document string.\n+    XML_PREFIX: str = \"<?xml\"\n+\n+    #: The start of an XML document bytestring.\n+    XML_PREFIX_B: bytes = b\"<?xml\"\n+\n+    # This is typed as str, not `ProcessingInstruction`, because this\n+    # check may be run before any Beautiful Soup objects are created.\n+    _first_processing_instruction: Optional[str]  #: :meta private:\n+    _root_tag_name: Optional[str]  #: :meta private:\n \n-    XML_PREFIX = '<?xml'\n-    XML_PREFIX_B = b'<?xml'\n-    \n     @classmethod\n-    def warn_if_markup_looks_like_xml(cls, markup, stacklevel=3):\n+    def warn_if_markup_looks_like_xml(\n+        cls, markup: Optional[_RawMarkup], stacklevel: int = 3\n+    ) -> bool:\n         \"\"\"Perform a check on some markup to see if it looks like XML\n         that's not XHTML. If so, issue a warning.\n \n         This is much less reliable than doing the check while parsing,\n         but some of the tree builders can't do that.\n \n-        :param stacklevel: The stacklevel of the code calling this\n-        function.\n+        :param stacklevel: The stacklevel of the code calling this\\\n+         function.\n \n         :return: True if the markup looks like non-XHTML XML, False\n-        otherwise.\n-\n+         otherwise.\n         \"\"\"\n+        if markup is None:\n+            return False\n+        markup = markup[:500]\n         if isinstance(markup, bytes):\n-            prefix = cls.XML_PREFIX_B\n-            looks_like_html = cls.LOOKS_LIKE_HTML_B\n+            markup_b: bytes = markup\n+            looks_like_xml = markup_b.startswith(\n+                cls.XML_PREFIX_B\n+            ) and not cls.LOOKS_LIKE_HTML_B.search(markup)\n         else:\n-            prefix = cls.XML_PREFIX\n-            looks_like_html = cls.LOOKS_LIKE_HTML\n-        \n-        if (markup is not None\n-            and markup.startswith(prefix)\n-            and not looks_like_html.search(markup[:500])\n-        ):\n-            cls._warn(stacklevel=stacklevel+2)\n+            markup_s: str = markup\n+            looks_like_xml = markup_s.startswith(\n+                cls.XML_PREFIX\n+            ) and not cls.LOOKS_LIKE_HTML.search(markup)\n+\n+        if looks_like_xml:\n+            cls._warn(stacklevel=stacklevel + 2)\n             return True\n         return False\n \n     @classmethod\n-    def _warn(cls, stacklevel=5):\n+    def _warn(cls, stacklevel: int = 5) -> None:\n         \"\"\"Issue a warning about XML being parsed as HTML.\"\"\"\n         warnings.warn(\n-            XMLParsedAsHTMLWarning.MESSAGE, XMLParsedAsHTMLWarning,\n-            stacklevel=stacklevel\n+            XMLParsedAsHTMLWarning.MESSAGE,\n+            XMLParsedAsHTMLWarning,\n+            stacklevel=stacklevel,\n         )\n-        \n-    def _initialize_xml_detector(self):\n+\n+    def _initialize_xml_detector(self) -> None:\n         \"\"\"Call this method before parsing a document.\"\"\"\n         self._first_processing_instruction = None\n-        self._root_tag = None\n-       \n-    def _document_might_be_xml(self, processing_instruction):\n+        self._root_tag_name = None\n+\n+    def _document_might_be_xml(self, processing_instruction: str) -> None:\n         \"\"\"Call this method when encountering an XML declaration, or a\n         \"processing instruction\" that might be an XML declaration.\n+\n+        This helps Beautiful Soup detect potential issues later, if\n+        the XML document turns out to be a non-XHTML document that's\n+        being parsed as XML.\n         \"\"\"\n-        if (self._first_processing_instruction is not None\n-            or self._root_tag is not None):\n+        if (\n+            self._first_processing_instruction is not None\n+            or self._root_tag_name is not None\n+        ):\n             # The document has already started. Don't bother checking\n             # anymore.\n             return\n@@ -570,28 +787,32 @@ def _document_might_be_xml(self, processing_instruction):\n \n         # We won't know until we encounter the first tag whether or\n         # not this is actually a problem.\n-        \n-    def _root_tag_encountered(self, name):\n+\n+    def _root_tag_encountered(self, name: str) -> None:\n         \"\"\"Call this when you encounter the document's root tag.\n \n         This is where we actually check whether an XML document is\n         being incorrectly parsed as HTML, and issue the warning.\n         \"\"\"\n-        if self._root_tag is not None:\n+        if self._root_tag_name is not None:\n             # This method was incorrectly called multiple times. Do\n             # nothing.\n             return\n \n-        self._root_tag = name\n-        if (name != 'html' and self._first_processing_instruction is not None\n-            and self._first_processing_instruction.lower().startswith('xml ')):\n+        self._root_tag_name = name\n+\n+        if (\n+            name != \"html\"\n+            and self._first_processing_instruction is not None\n+            and self._first_processing_instruction.lower().startswith(\"xml \")\n+        ):\n             # We encountered an XML declaration and then a tag other\n             # than 'html'. This is a reliable indicator that a\n             # non-XHTML document is being parsed as XML.\n-            self._warn()\n+            self._warn(stacklevel=10)\n+\n \n-    \n-def register_treebuilders_from(module):\n+def register_treebuilders_from(module: ModuleType) -> None:\n     \"\"\"Copy TreeBuilders from the given module into this module.\"\"\"\n     this_module = sys.modules[__name__]\n     for name in module.__all__:\n@@ -603,33 +824,24 @@ def register_treebuilders_from(module):\n             # Register the builder while we're at it.\n             this_module.builder_registry.register(obj)\n \n-class ParserRejectedMarkup(Exception):\n-    \"\"\"An Exception to be raised when the underlying parser simply\n-    refuses to parse the given markup.\n-    \"\"\"\n-    def __init__(self, message_or_exception):\n-        \"\"\"Explain why the parser rejected the given markup, either\n-        with a textual explanation or another exception.\n-        \"\"\"\n-        if isinstance(message_or_exception, Exception):\n-            e = message_or_exception\n-            message_or_exception = \"%s: %s\" % (e.__class__.__name__, str(e))\n-        super(ParserRejectedMarkup, self).__init__(message_or_exception)\n-            \n+\n # Builders are registered in reverse order of priority, so that custom\n # builder registrations will take precedence. In general, we want lxml\n # to take precedence over html5lib, because it's faster. And we only\n # want to use HTMLParser as a last resort.\n-from . import _htmlparser\n+from . import _htmlparser # noqa: E402\n+\n register_treebuilders_from(_htmlparser)\n try:\n     from . import _html5lib\n+\n     register_treebuilders_from(_html5lib)\n except ImportError:\n     # They don't have html5lib installed.\n     pass\n try:\n     from . import _lxml\n+\n     register_treebuilders_from(_lxml)\n except ImportError:\n     # They don't have lxml installed."}, {"sha": "c90231f6c2014bbed02f320df8c970e9eedbe46a", "filename": "lib/bs4/builder/_html5lib.py", "status": "modified", "additions": 357, "deletions": 227, "changes": 584, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fbuilder%2F_html5lib.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fbuilder%2F_html5lib.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fbuilder%2F_html5lib.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -2,316 +2,395 @@\n __license__ = \"MIT\"\n \n __all__ = [\n-    'HTML5TreeBuilder',\n-    ]\n+    \"HTML5TreeBuilder\",\n+]\n+\n+from typing import (\n+    Any,\n+    cast,\n+    Dict,\n+    Iterable,\n+    Optional,\n+    Sequence,\n+    TYPE_CHECKING,\n+    Tuple,\n+    Union,\n+)\n+from typing_extensions import TypeAlias\n+from bs4._typing import (\n+    _AttributeValue,\n+    _AttributeValues,\n+    _Encoding,\n+    _Encodings,\n+    _NamespaceURL,\n+    _RawMarkup,\n+)\n \n import warnings\n-import re\n from bs4.builder import (\n     DetectsXMLParsedAsHTML,\n     PERMISSIVE,\n     HTML,\n     HTML_5,\n     HTMLTreeBuilder,\n-    )\n+)\n from bs4.element import (\n     NamespacedAttribute,\n+    PageElement,\n     nonwhitespace_re,\n )\n import html5lib\n from html5lib.constants import (\n     namespaces,\n-    prefixes,\n-    )\n+)\n from bs4.element import (\n     Comment,\n     Doctype,\n     NavigableString,\n     Tag,\n-    )\n+)\n+\n+if TYPE_CHECKING:\n+    from bs4 import BeautifulSoup\n+\n+from html5lib.treebuilders import base as treebuilder_base\n \n-try:\n-    # Pre-0.99999999\n-    from html5lib.treebuilders import _base as treebuilder_base\n-    new_html5lib = False\n-except ImportError as e:\n-    # 0.99999999 and up\n-    from html5lib.treebuilders import base as treebuilder_base\n-    new_html5lib = True\n \n class HTML5TreeBuilder(HTMLTreeBuilder):\n-    \"\"\"Use html5lib to build a tree.\n+    \"\"\"Use `html5lib <https://github.com/html5lib/html5lib-python>`_ to\n+    build a tree.\n \n-    Note that this TreeBuilder does not support some features common\n-    to HTML TreeBuilders. Some of these features could theoretically\n+    Note that `HTML5TreeBuilder` does not support some common HTML\n+    `TreeBuilder` features. Some of these features could theoretically\n     be implemented, but at the very least it's quite difficult,\n     because html5lib moves the parse tree around as it's being built.\n \n-    * This TreeBuilder doesn't use different subclasses of NavigableString\n-      based on the name of the tag in which the string was found.\n+    Specifically:\n \n-    * You can't use a SoupStrainer to parse only part of a document.\n+    * This `TreeBuilder` doesn't use different subclasses of\n+      `NavigableString` (e.g. `Script`) based on the name of the tag\n+      in which the string was found.\n+    * You can't use a `SoupStrainer` to parse only part of a document.\n     \"\"\"\n \n-    NAME = \"html5lib\"\n+    NAME: str = \"html5lib\"\n+\n+    features: Iterable[str] = [NAME, PERMISSIVE, HTML_5, HTML]\n+\n+    #: html5lib can tell us which line number and position in the\n+    #: original file is the source of an element.\n+    TRACKS_LINE_NUMBERS: bool = True\n \n-    features = [NAME, PERMISSIVE, HTML_5, HTML]\n+    underlying_builder: \"TreeBuilderForHtml5lib\"  #: :meta private:\n+    user_specified_encoding: Optional[_Encoding]\n \n-    # html5lib can tell us which line number and position in the\n-    # original file is the source of an element.\n-    TRACKS_LINE_NUMBERS = True\n-    \n-    def prepare_markup(self, markup, user_specified_encoding,\n-                       document_declared_encoding=None, exclude_encodings=None):\n+    def prepare_markup(\n+        self,\n+        markup: _RawMarkup,\n+        user_specified_encoding: Optional[_Encoding] = None,\n+        document_declared_encoding: Optional[_Encoding] = None,\n+        exclude_encodings: Optional[_Encodings] = None,\n+    ) -> Iterable[Tuple[_RawMarkup, Optional[_Encoding], Optional[_Encoding], bool]]:\n         # Store the user-specified encoding for use later on.\n         self.user_specified_encoding = user_specified_encoding\n \n         # document_declared_encoding and exclude_encodings aren't used\n         # ATM because the html5lib TreeBuilder doesn't use\n         # UnicodeDammit.\n-        if exclude_encodings:\n-            warnings.warn(\n-                \"You provided a value for exclude_encoding, but the html5lib tree builder doesn't support exclude_encoding.\",\n-                stacklevel=3\n-            )\n+        for variable, name in (\n+            (document_declared_encoding, \"document_declared_encoding\"),\n+            (exclude_encodings, \"exclude_encodings\"),\n+        ):\n+            if variable:\n+                warnings.warn(\n+                    f\"You provided a value for {name}, but the html5lib tree builder doesn't support {name}.\",\n+                    stacklevel=3,\n+                )\n \n         # html5lib only parses HTML, so if it's given XML that's worth\n         # noting.\n-        DetectsXMLParsedAsHTML.warn_if_markup_looks_like_xml(\n-            markup, stacklevel=3\n-        )\n+        DetectsXMLParsedAsHTML.warn_if_markup_looks_like_xml(markup, stacklevel=3)\n \n         yield (markup, None, None, False)\n \n     # These methods are defined by Beautiful Soup.\n-    def feed(self, markup):\n-        if self.soup.parse_only is not None:\n+    def feed(self, markup: _RawMarkup) -> None:\n+        \"\"\"Run some incoming markup through some parsing process,\n+        populating the `BeautifulSoup` object in `HTML5TreeBuilder.soup`.\n+        \"\"\"\n+        if self.soup is not None and self.soup.parse_only is not None:\n             warnings.warn(\n                 \"You provided a value for parse_only, but the html5lib tree builder doesn't support parse_only. The entire document will be parsed.\",\n-                stacklevel=4\n+                stacklevel=4,\n             )\n+\n+        # self.underlying_builder is probably None now, but it'll be set\n+        # when html5lib calls self.create_treebuilder().\n         parser = html5lib.HTMLParser(tree=self.create_treebuilder)\n+        assert self.underlying_builder is not None\n         self.underlying_builder.parser = parser\n         extra_kwargs = dict()\n         if not isinstance(markup, str):\n-            if new_html5lib:\n-                extra_kwargs['override_encoding'] = self.user_specified_encoding\n-            else:\n-                extra_kwargs['encoding'] = self.user_specified_encoding\n-        doc = parser.parse(markup, **extra_kwargs)\n-        \n+            # kwargs, specifically override_encoding, will eventually\n+            # be passed in to html5lib's\n+            # HTMLBinaryInputStream.__init__.\n+            extra_kwargs[\"override_encoding\"] = self.user_specified_encoding\n+\n+        doc = parser.parse(markup, **extra_kwargs) # type:ignore\n+\n         # Set the character encoding detected by the tokenizer.\n         if isinstance(markup, str):\n             # We need to special-case this because html5lib sets\n             # charEncoding to UTF-8 if it gets Unicode input.\n             doc.original_encoding = None\n         else:\n-            original_encoding = parser.tokenizer.stream.charEncoding[0]\n-            if not isinstance(original_encoding, str):\n-                # In 0.99999999 and up, the encoding is an html5lib\n-                # Encoding object. We want to use a string for compatibility\n-                # with other tree builders.\n-                original_encoding = original_encoding.name\n+            original_encoding = parser.tokenizer.stream.charEncoding[0] # type:ignore\n+            # The encoding is an html5lib Encoding object. We want to\n+            # use a string for compatibility with other tree builders.\n+            original_encoding = original_encoding.name\n             doc.original_encoding = original_encoding\n         self.underlying_builder.parser = None\n-            \n-    def create_treebuilder(self, namespaceHTMLElements):\n+\n+    def create_treebuilder(\n+        self, namespaceHTMLElements: bool\n+    ) -> \"TreeBuilderForHtml5lib\":\n+        \"\"\"Called by html5lib to instantiate the kind of class it\n+        calls a 'TreeBuilder'.\n+\n+        :param namespaceHTMLElements: Whether or not to namespace HTML elements.\n+\n+        :meta private:\n+        \"\"\"\n         self.underlying_builder = TreeBuilderForHtml5lib(\n-            namespaceHTMLElements, self.soup,\n-            store_line_numbers=self.store_line_numbers\n+            namespaceHTMLElements, self.soup, store_line_numbers=self.store_line_numbers\n         )\n         return self.underlying_builder\n \n-    def test_fragment_to_document(self, fragment):\n+    def test_fragment_to_document(self, fragment: str) -> str:\n         \"\"\"See `TreeBuilder`.\"\"\"\n-        return '<html><head></head><body>%s</body></html>' % fragment\n+        return \"<html><head></head><body>%s</body></html>\" % fragment\n \n \n class TreeBuilderForHtml5lib(treebuilder_base.TreeBuilder):\n-    \n-    def __init__(self, namespaceHTMLElements, soup=None,\n-                 store_line_numbers=True, **kwargs):\n+    soup: \"BeautifulSoup\"  #: :meta private:\n+    parser: Optional[html5lib.HTMLParser]  #: :meta private:\n+\n+    def __init__(\n+        self,\n+        namespaceHTMLElements: bool,\n+        soup: Optional[\"BeautifulSoup\"] = None,\n+        store_line_numbers: bool = True,\n+        **kwargs: Any,\n+    ):\n         if soup:\n             self.soup = soup\n         else:\n+            warnings.warn(\n+                \"The optionality of the 'soup' argument to the TreeBuilderForHtml5lib constructor is deprecated as of Beautiful Soup 4.13.0: 'soup' is now required. If you can't pass in a BeautifulSoup object here, or you get this warning and it seems mysterious to you, please contact the Beautiful Soup developer team for possible un-deprecation.\",\n+                DeprecationWarning,\n+                stacklevel=2,\n+            )\n             from bs4 import BeautifulSoup\n-            # TODO: Why is the parser 'html.parser' here? To avoid an\n-            # infinite loop?\n+\n+            # TODO: Why is the parser 'html.parser' here? Using\n+            # html5lib doesn't cause an infinite loop and is more\n+            # accurate. Best to get rid of this entire section, I think.\n             self.soup = BeautifulSoup(\n-                \"\", \"html.parser\", store_line_numbers=store_line_numbers,\n-                **kwargs\n+                \"\", \"html.parser\", store_line_numbers=store_line_numbers, **kwargs\n             )\n         # TODO: What are **kwargs exactly? Should they be passed in\n         # here in addition to/instead of being passed to the BeautifulSoup\n         # constructor?\n         super(TreeBuilderForHtml5lib, self).__init__(namespaceHTMLElements)\n \n-        # This will be set later to an html5lib.html5parser.HTMLParser\n-        # object, which we can use to track the current line number.\n+        # This will be set later to a real html5lib HTMLParser object,\n+        # which we can use to track the current line number.\n         self.parser = None\n         self.store_line_numbers = store_line_numbers\n-        \n-    def documentClass(self):\n+\n+    def documentClass(self) -> \"Element\":\n         self.soup.reset()\n         return Element(self.soup, self.soup, None)\n \n-    def insertDoctype(self, token):\n-        name = token[\"name\"]\n-        publicId = token[\"publicId\"]\n-        systemId = token[\"systemId\"]\n+    def insertDoctype(self, token: Dict[str, Any]) -> None:\n+        name: str = cast(str, token[\"name\"])\n+        publicId: Optional[str] = cast(Optional[str], token[\"publicId\"])\n+        systemId: Optional[str] = cast(Optional[str], token[\"systemId\"])\n \n         doctype = Doctype.for_name_and_ids(name, publicId, systemId)\n         self.soup.object_was_parsed(doctype)\n \n-    def elementClass(self, name, namespace):\n-        kwargs = {}\n-        if self.parser and self.store_line_numbers:\n+    def elementClass(self, name: str, namespace: str) -> \"Element\":\n+        sourceline: Optional[int] = None\n+        sourcepos: Optional[int] = None\n+        if self.parser is not None and self.store_line_numbers:\n             # This represents the point immediately after the end of the\n             # tag. We don't know when the tag started, but we do know\n             # where it ended -- the character just before this one.\n-            sourceline, sourcepos = self.parser.tokenizer.stream.position()\n-            kwargs['sourceline'] = sourceline\n-            kwargs['sourcepos'] = sourcepos-1\n-        tag = self.soup.new_tag(name, namespace, **kwargs)\n+            sourceline, sourcepos = self.parser.tokenizer.stream.position() # type:ignore\n+            assert sourcepos is not None\n+            sourcepos = sourcepos - 1\n+        tag = self.soup.new_tag(\n+            name, namespace, sourceline=sourceline, sourcepos=sourcepos\n+        )\n \n         return Element(tag, self.soup, namespace)\n \n-    def commentClass(self, data):\n+    def commentClass(self, data: str) -> \"TextNode\":\n         return TextNode(Comment(data), self.soup)\n \n-    def fragmentClass(self):\n-        from bs4 import BeautifulSoup\n-        # TODO: Why is the parser 'html.parser' here? To avoid an\n-        # infinite loop?\n-        self.soup = BeautifulSoup(\"\", \"html.parser\")\n-        self.soup.name = \"[document_fragment]\"\n-        return Element(self.soup, self.soup, None)\n-\n-    def appendChild(self, node):\n-        # XXX This code is not covered by the BS4 tests.\n+    def fragmentClass(self) -> \"Element\":\n+        \"\"\"This is only used by html5lib HTMLParser.parseFragment(),\n+        which is never used by Beautiful Soup, only by the html5lib\n+        unit tests. Since we don't currently hook into those tests,\n+        the implementation is left blank.\n+        \"\"\"\n+        raise NotImplementedError()\n+\n+    def getFragment(self) -> \"Element\":\n+        \"\"\"This is only used by the html5lib unit tests. Since we\n+        don't currently hook into those tests, the implementation is\n+        left blank.\n+        \"\"\"\n+        raise NotImplementedError()\n+\n+    def appendChild(self, node: \"Element\") -> None:\n+        # TODO: This code is not covered by the BS4 tests, and\n+        # apparently not triggered by the html5lib test suite either.\n+        # But it doesn't seem test-specific and there are calls to it\n+        # (or a method with the same name) all over html5lib, so I'm\n+        # leaving the implementation in place rather than replacing it\n+        # with NotImplementedError()\n         self.soup.append(node.element)\n \n-    def getDocument(self):\n+    def getDocument(self) -> \"BeautifulSoup\":\n         return self.soup\n \n-    def getFragment(self):\n-        return treebuilder_base.TreeBuilder.getFragment(self).element\n-\n-    def testSerializer(self, element):\n-        from bs4 import BeautifulSoup\n-        rv = []\n-        doctype_re = re.compile(r'^(.*?)(?: PUBLIC \"(.*?)\"(?: \"(.*?)\")?| SYSTEM \"(.*?)\")?$')\n-\n-        def serializeElement(element, indent=0):\n-            if isinstance(element, BeautifulSoup):\n-                pass\n-            if isinstance(element, Doctype):\n-                m = doctype_re.match(element)\n-                if m:\n-                    name = m.group(1)\n-                    if m.lastindex > 1:\n-                        publicId = m.group(2) or \"\"\n-                        systemId = m.group(3) or m.group(4) or \"\"\n-                        rv.append(\"\"\"|%s<!DOCTYPE %s \"%s\" \"%s\">\"\"\" %\n-                                  (' ' * indent, name, publicId, systemId))\n-                    else:\n-                        rv.append(\"|%s<!DOCTYPE %s>\" % (' ' * indent, name))\n-                else:\n-                    rv.append(\"|%s<!DOCTYPE >\" % (' ' * indent,))\n-            elif isinstance(element, Comment):\n-                rv.append(\"|%s<!-- %s -->\" % (' ' * indent, element))\n-            elif isinstance(element, NavigableString):\n-                rv.append(\"|%s\\\"%s\\\"\" % (' ' * indent, element))\n-            else:\n-                if element.namespace:\n-                    name = \"%s %s\" % (prefixes[element.namespace],\n-                                      element.name)\n-                else:\n-                    name = element.name\n-                rv.append(\"|%s<%s>\" % (' ' * indent, name))\n-                if element.attrs:\n-                    attributes = []\n-                    for name, value in list(element.attrs.items()):\n-                        if isinstance(name, NamespacedAttribute):\n-                            name = \"%s %s\" % (prefixes[name.namespace], name.name)\n-                        if isinstance(value, list):\n-                            value = \" \".join(value)\n-                        attributes.append((name, value))\n-\n-                    for name, value in sorted(attributes):\n-                        rv.append('|%s%s=\"%s\"' % (' ' * (indent + 2), name, value))\n-                indent += 2\n-                for child in element.children:\n-                    serializeElement(child, indent)\n-        serializeElement(element, 0)\n-\n-        return \"\\n\".join(rv)\n+    def testSerializer(self, node: \"Element\") -> None:\n+        \"\"\"This is only used by the html5lib unit tests. Since we\n+        don't currently hook into those tests, the implementation is\n+        left blank.\n+        \"\"\"\n+        raise NotImplementedError()\n+\n \n class AttrList(object):\n-    def __init__(self, element):\n+    \"\"\"Represents a Tag's attributes in a way compatible with html5lib.\"\"\"\n+\n+    element: Tag\n+    attrs: _AttributeValues\n+\n+    def __init__(self, element: Tag):\n         self.element = element\n         self.attrs = dict(self.element.attrs)\n-    def __iter__(self):\n+\n+    def __iter__(self) -> Iterable[Tuple[str, _AttributeValue]]:\n         return list(self.attrs.items()).__iter__()\n-    def __setitem__(self, name, value):\n+\n+    def __setitem__(self, name: str, value: _AttributeValue) -> None:\n         # If this attribute is a multi-valued attribute for this element,\n         # turn its value into a list.\n         list_attr = self.element.cdata_list_attributes or {}\n-        if (name in list_attr.get('*', [])\n-            or (self.element.name in list_attr\n-                and name in list_attr.get(self.element.name, []))):\n+        if name in list_attr.get(\"*\", []) or (\n+            self.element.name in list_attr\n+            and name in list_attr.get(self.element.name, [])\n+        ):\n             # A node that is being cloned may have already undergone\n-            # this procedure.\n+            # this procedure. Check for this and skip it.\n             if not isinstance(value, list):\n-                value = nonwhitespace_re.findall(value)\n+                assert isinstance(value, str)\n+                value = self.element.attribute_value_list_class(\n+                    nonwhitespace_re.findall(value)\n+                )\n         self.element[name] = value\n-    def items(self):\n+\n+    def items(self) -> Iterable[Tuple[str, _AttributeValue]]:\n         return list(self.attrs.items())\n-    def keys(self):\n+\n+    def keys(self) -> Iterable[str]:\n         return list(self.attrs.keys())\n-    def __len__(self):\n+\n+    def __len__(self) -> int:\n         return len(self.attrs)\n-    def __getitem__(self, name):\n+\n+    def __getitem__(self, name: str) -> _AttributeValue:\n         return self.attrs[name]\n-    def __contains__(self, name):\n+\n+    def __contains__(self, name: str) -> bool:\n         return name in list(self.attrs.keys())\n \n \n-class Element(treebuilder_base.Node):\n-    def __init__(self, element, soup, namespace):\n-        treebuilder_base.Node.__init__(self, element.name)\n-        self.element = element\n+class BeautifulSoupNode(treebuilder_base.Node):\n+    # A node can correspond to _either_ a Tag _or_ a NavigableString.\n+    tag: Optional[Tag]\n+    string: Optional[NavigableString]\n+    soup: \"BeautifulSoup\"\n+    namespace: Optional[_NamespaceURL]\n+\n+    @property\n+    def element(self) -> PageElement:\n+        assert self.tag is not None or self.string is not None\n+        if self.tag is not None:\n+            return self.tag\n+        else:\n+            assert self.string is not None\n+            return self.string\n+\n+    @property\n+    def nodeType(self) -> int:\n+        \"\"\"Return the html5lib constant corresponding to the type of\n+        the underlying DOM object.\n+\n+        NOTE: This property is only accessed by the html5lib test\n+        suite, not by Beautiful Soup proper.\n+        \"\"\"\n+        raise NotImplementedError()\n+\n+    # TODO-TYPING: typeshed stubs are incorrect about this;\n+    # cloneNode returns a new Node, not None.\n+    def cloneNode(self) -> treebuilder_base.Node: # type:ignore\n+        raise NotImplementedError()\n+\n+\n+class Element(BeautifulSoupNode):\n+    namespace: Optional[_NamespaceURL]\n+\n+    def __init__(\n+        self, element: Tag, soup: \"BeautifulSoup\", namespace: Optional[_NamespaceURL]\n+    ):\n+        self.tag = element\n+        self.string = None\n         self.soup = soup\n         self.namespace = namespace\n+        treebuilder_base.Node.__init__(self, element.name)\n \n-    def appendChild(self, node):\n-        string_child = child = None\n-        if isinstance(node, str):\n-            # Some other piece of code decided to pass in a string\n-            # instead of creating a TextElement object to contain the\n-            # string.\n-            string_child = child = node\n-        elif isinstance(node, Tag):\n-            # Some other piece of code decided to pass in a Tag\n-            # instead of creating an Element object to contain the\n-            # Tag.\n-            child = node\n-        elif node.element.__class__ == NavigableString:\n-            string_child = child = node.element\n-            node.parent = self\n+    def appendChild(self, node: \"BeautifulSoupNode\") -> None:\n+        string_child: Optional[NavigableString] = None\n+        child: PageElement\n+        if type(node.string) is NavigableString:\n+            # We check for NavigableString *only* because we want to avoid\n+            # joining PreformattedStrings, such as Comments, with nearby strings.\n+            string_child = child = node.string\n         else:\n             child = node.element\n-            node.parent = self\n+        node.parent = self\n \n-        if not isinstance(child, str) and child.parent is not None:\n+        if (\n+            child is not None\n+            and child.parent is not None\n+            and not isinstance(child, str)\n+        ):\n             node.element.extract()\n \n-        if (string_child is not None and self.element.contents\n-            and self.element.contents[-1].__class__ == NavigableString):\n+        if (\n+            string_child is not None\n+            and self.tag is not None and self.tag.contents\n+            and type(self.tag.contents[-1]) is NavigableString\n+        ):\n             # We are appending a string onto another string.\n             # TODO This has O(n^2) performance, for input like\n             # \"a</a>a</a>a</a>...\"\n-            old_element = self.element.contents[-1]\n+            old_element = self.tag.contents[-1]\n             new_element = self.soup.new_string(old_element + string_child)\n             old_element.replace_with(new_element)\n             self.soup._most_recent_element = new_element\n@@ -323,8 +402,8 @@ def appendChild(self, node):\n             # Tell Beautiful Soup to act as if it parsed this element\n             # immediately after the parent's last descendant. (Or\n             # immediately after the parent, if it has no children.)\n-            if self.element.contents:\n-                most_recent_element = self.element._last_descendant(False)\n+            if self.tag is not None and self.tag.contents:\n+                most_recent_element = self.tag._last_descendant(False)\n             elif self.element.next_element is not None:\n                 # Something from further ahead in the parse tree is\n                 # being inserted into this earlier element. This is\n@@ -335,66 +414,96 @@ def appendChild(self, node):\n                 most_recent_element = self.element\n \n             self.soup.object_was_parsed(\n-                child, parent=self.element,\n-                most_recent_element=most_recent_element)\n+                child, parent=self.tag, most_recent_element=most_recent_element\n+            )\n \n-    def getAttributes(self):\n-        if isinstance(self.element, Comment):\n-            return {}\n-        return AttrList(self.element)\n+    def getAttributes(self) -> AttrList:\n+        assert self.tag is not None\n+        return AttrList(self.tag)\n \n-    def setAttributes(self, attributes):\n+    # An HTML5lib attribute name may either be a single string,\n+    # or a tuple (namespace, name).\n+    _Html5libAttributeName: TypeAlias = Union[str, Tuple[str, str]]\n+    # Now we can define the type this method accepts as a dictionary\n+    # mapping those attribute names to single string values.\n+    _Html5libAttributes: TypeAlias = Dict[_Html5libAttributeName, str]\n+\n+    def setAttributes(self, attributes: Optional[_Html5libAttributes]) -> None:\n+        assert self.tag is not None\n         if attributes is not None and len(attributes) > 0:\n-            converted_attributes = []\n+            # Replace any namespaced attributes with\n+            # NamespacedAttribute objects.\n             for name, value in list(attributes.items()):\n                 if isinstance(name, tuple):\n                     new_name = NamespacedAttribute(*name)\n                     del attributes[name]\n                     attributes[new_name] = value\n \n+            # We can now cast attributes to the type of Dict\n+            # used by Beautiful Soup.\n+            normalized_attributes = cast(_AttributeValues, attributes)\n+\n+            # Values for tags like 'class' came in as single strings;\n+            # replace them with lists of strings as appropriate.\n             self.soup.builder._replace_cdata_list_attribute_values(\n-                self.name, attributes)\n-            for name, value in list(attributes.items()):\n-                self.element[name] = value\n+                self.name, normalized_attributes\n+            )\n+\n+            # Then set the attributes on the Tag associated with this\n+            # BeautifulSoupNode.\n+            for name, value_or_values in list(normalized_attributes.items()):\n+                self.tag[name] = value_or_values\n \n             # The attributes may contain variables that need substitution.\n             # Call set_up_substitutions manually.\n             #\n             # The Tag constructor called this method when the Tag was created,\n             # but we just set/changed the attributes, so call it again.\n-            self.soup.builder.set_up_substitutions(self.element)\n+            self.soup.builder.set_up_substitutions(self.tag)\n+\n     attributes = property(getAttributes, setAttributes)\n \n-    def insertText(self, data, insertBefore=None):\n+    def insertText(\n+        self, data: str, insertBefore: Optional[\"BeautifulSoupNode\"] = None\n+    ) -> None:\n         text = TextNode(self.soup.new_string(data), self.soup)\n         if insertBefore:\n             self.insertBefore(text, insertBefore)\n         else:\n             self.appendChild(text)\n \n-    def insertBefore(self, node, refNode):\n-        index = self.element.index(refNode.element)\n-        if (node.element.__class__ == NavigableString and self.element.contents\n-            and self.element.contents[index-1].__class__ == NavigableString):\n+    def insertBefore(\n+        self, node: \"BeautifulSoupNode\", refNode: \"BeautifulSoupNode\"\n+    ) -> None:\n+        assert self.tag is not None\n+        index = self.tag.index(refNode.element)\n+        if (\n+            type(node.element) is NavigableString\n+            and self.tag.contents\n+            and type(self.tag.contents[index - 1]) is NavigableString\n+        ):\n             # (See comments in appendChild)\n-            old_node = self.element.contents[index-1]\n+            old_node = self.tag.contents[index - 1]\n+            assert type(old_node) is NavigableString\n             new_str = self.soup.new_string(old_node + node.element)\n             old_node.replace_with(new_str)\n         else:\n-            self.element.insert(index, node.element)\n+            self.tag.insert(index, node.element)\n             node.parent = self\n \n-    def removeChild(self, node):\n+    def removeChild(self, node: \"Element\") -> None:\n         node.element.extract()\n \n-    def reparentChildren(self, new_parent):\n+    def reparentChildren(self, newParent: \"Element\") -> None:\n         \"\"\"Move all of this tag's children into another tag.\"\"\"\n         # print(\"MOVE\", self.element.contents)\n         # print(\"FROM\", self.element)\n         # print(\"TO\", new_parent.element)\n \n-        element = self.element\n-        new_parent_element = new_parent.element\n+        element = self.tag\n+        assert element is not None\n+        new_parent_element = newParent.tag\n+        assert new_parent_element is not None\n         # Determine what this tag's next_element will be once all the children\n         # are removed.\n         final_next_element = element.next_sibling\n@@ -403,8 +512,14 @@ def reparentChildren(self, new_parent):\n         if len(new_parent_element.contents) > 0:\n             # The new parent already contains children. We will be\n             # appending this tag's children to the end.\n+\n+            # We can make this assertion since we know new_parent has\n+            # children.\n+            assert new_parents_last_descendant is not None\n             new_parents_last_child = new_parent_element.contents[-1]\n-            new_parents_last_descendant_next_element = new_parents_last_descendant.next_element\n+            new_parents_last_descendant_next_element = (\n+                new_parents_last_descendant.next_element\n+            )\n         else:\n             # The new parent contains no children.\n             new_parents_last_child = None\n@@ -431,14 +546,24 @@ def reparentChildren(self, new_parent):\n             # parent's last descendant. It has no .next_sibling and\n             # its .next_element is whatever the previous last\n             # descendant had.\n-            last_childs_last_descendant = to_append[-1]._last_descendant(False, True)\n+            last_childs_last_descendant = to_append[-1]._last_descendant(\n+                is_initialized=False, accept_self=True\n+            )\n \n-            last_childs_last_descendant.next_element = new_parents_last_descendant_next_element\n+            # Since we passed accept_self=True into _last_descendant,\n+            # there's no possibility that the result is None.\n+            assert last_childs_last_descendant is not None\n+            last_childs_last_descendant.next_element = (\n+                new_parents_last_descendant_next_element\n+            )\n             if new_parents_last_descendant_next_element is not None:\n-                # TODO: This code has no test coverage and I'm not sure\n-                # how to get html5lib to go through this path, but it's\n-                # just the other side of the previous line.\n-                new_parents_last_descendant_next_element.previous_element = last_childs_last_descendant\n+                # TODO-COVERAGE: This code has no test coverage and\n+                # I'm not sure how to get html5lib to go through this\n+                # path, but it's just the other side of the previous\n+                # line.\n+                new_parents_last_descendant_next_element.previous_element = (\n+                    last_childs_last_descendant\n+                )\n             last_childs_last_descendant.next_sibling = None\n \n         for child in to_append:\n@@ -453,29 +578,34 @@ def reparentChildren(self, new_parent):\n         # print(\"FROM\", self.element)\n         # print(\"TO\", new_parent_element)\n \n-    def cloneNode(self):\n-        tag = self.soup.new_tag(self.element.name, self.namespace)\n+    # TODO-TYPING: typeshed stubs are incorrect about this;\n+    # hasContent returns a boolean, not None.\n+    def hasContent(self) -> bool: # type:ignore\n+        return self.tag is None or len(self.tag.contents) > 0\n+\n+    # TODO-TYPING: typeshed stubs are incorrect about this;\n+    # cloneNode returns a new Node, not None.\n+    def cloneNode(self) -> treebuilder_base.Node: # type:ignore\n+        assert self.tag is not None\n+        tag = self.soup.new_tag(self.tag.name, self.namespace)\n         node = Element(tag, self.soup, self.namespace)\n-        for key,value in self.attributes:\n+        for key, value in self.attributes:\n             node.attributes[key] = value\n         return node\n \n-    def hasContent(self):\n-        return self.element.contents\n-\n-    def getNameTuple(self):\n-        if self.namespace == None:\n+    def getNameTuple(self) -> Tuple[Optional[_NamespaceURL], str]:\n+        if self.namespace is None:\n             return namespaces[\"html\"], self.name\n         else:\n             return self.namespace, self.name\n \n     nameTuple = property(getNameTuple)\n \n-class TextNode(Element):\n-    def __init__(self, element, soup):\n+\n+class TextNode(BeautifulSoupNode):\n+\n+    def __init__(self, element: NavigableString, soup: \"BeautifulSoup\"):\n         treebuilder_base.Node.__init__(self, None)\n-        self.element = element\n+        self.tag = None\n+        self.string = element\n         self.soup = soup\n-\n-    def cloneNode(self):\n-        raise NotImplementedError"}, {"sha": "165a3d89cfcbd2207380999dcbf4e55ff47acb79", "filename": "lib/bs4/builder/_htmlparser.py", "status": "modified", "additions": 209, "deletions": 134, "changes": 343, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fbuilder%2F_htmlparser.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fbuilder%2F_htmlparser.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fbuilder%2F_htmlparser.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,50 +1,75 @@\n # encoding: utf-8\n \"\"\"Use the HTMLParser library to parse HTML files that aren't too bad.\"\"\"\n+from __future__ import annotations\n \n # Use of this source code is governed by the MIT license.\n __license__ = \"MIT\"\n \n __all__ = [\n-    'HTMLParserTreeBuilder',\n-    ]\n+    \"HTMLParserTreeBuilder\",\n+]\n \n from html.parser import HTMLParser\n \n-import sys\n-import warnings\n+from typing import (\n+    Any,\n+    Callable,\n+    cast,\n+    Dict,\n+    Iterable,\n+    List,\n+    Optional,\n+    TYPE_CHECKING,\n+    Tuple,\n+    Type,\n+    Union,\n+)\n \n from bs4.element import (\n+    AttributeDict,\n     CData,\n     Comment,\n     Declaration,\n     Doctype,\n     ProcessingInstruction,\n-    )\n+)\n from bs4.dammit import EntitySubstitution, UnicodeDammit\n \n from bs4.builder import (\n     DetectsXMLParsedAsHTML,\n-    ParserRejectedMarkup,\n     HTML,\n     HTMLTreeBuilder,\n     STRICT,\n+)\n+\n+from bs4.exceptions import ParserRejectedMarkup\n+\n+if TYPE_CHECKING:\n+    from bs4 import BeautifulSoup\n+    from bs4.element import NavigableString\n+    from bs4._typing import (\n+        _Encoding,\n+        _Encodings,\n+        _RawMarkup,\n     )\n \n+HTMLPARSER = \"html.parser\"\n+\n+_DuplicateAttributeHandler = Callable[[Dict[str, str], str, str], None]\n \n-HTMLPARSER = 'html.parser'\n \n class BeautifulSoupHTMLParser(HTMLParser, DetectsXMLParsedAsHTML):\n+    #: Constant to handle duplicate attributes by ignoring later values\n+    #: and keeping the earlier ones.\n+    REPLACE: str = \"replace\"\n+\n+    #: Constant to handle duplicate attributes by replacing earlier values\n+    #: with later ones.\n+    IGNORE: str = \"ignore\"\n+\n     \"\"\"A subclass of the Python standard library's HTMLParser class, which\n     listens for HTMLParser events and translates them into calls\n     to Beautiful Soup's tree construction API.\n-    \"\"\"\n-\n-    # Strategies for handling duplicate attributes\n-    IGNORE = 'ignore'\n-    REPLACE = 'replace'\n-    \n-    def __init__(self, *args, **kwargs):\n-        \"\"\"Constructor.\n \n         :param on_duplicate_attribute: A strategy for what to do if a\n             tag includes the same attribute more than once. Accepted\n@@ -53,11 +78,19 @@ def __init__(self, *args, **kwargs):\n             encountered), or a callable. A callable must take three\n             arguments: the dictionary of attributes already processed,\n             the name of the duplicate attribute, and the most recent value\n-            encountered.           \n-        \"\"\"\n-        self.on_duplicate_attribute = kwargs.pop(\n-            'on_duplicate_attribute', self.REPLACE\n-        )\n+            encountered.\n+    \"\"\"\n+\n+    def __init__(\n+        self,\n+        soup: BeautifulSoup,\n+        *args: Any,\n+        on_duplicate_attribute: Union[str, _DuplicateAttributeHandler] = REPLACE,\n+        **kwargs: Any,\n+    ):\n+        self.soup = soup\n+        self.on_duplicate_attribute = on_duplicate_attribute\n+        self.attribute_dict_class = soup.builder.attribute_dict_class\n         HTMLParser.__init__(self, *args, **kwargs)\n \n         # Keep a list of empty-element tags that were encountered\n@@ -71,7 +104,11 @@ def __init__(self, *args, **kwargs):\n \n         self._initialize_xml_detector()\n \n-    def error(self, message):\n+    on_duplicate_attribute: Union[str, _DuplicateAttributeHandler]\n+    already_closed_empty_element: List[str]\n+    soup: BeautifulSoup\n+\n+    def error(self, message: str) -> None:\n         # NOTE: This method is required so long as Python 3.9 is\n         # supported. The corresponding code is removed from HTMLParser\n         # in 3.5, but not removed from ParserBase until 3.10.\n@@ -87,37 +124,40 @@ def error(self, message):\n         # catch this error and wrap it in a ParserRejectedMarkup.)\n         raise ParserRejectedMarkup(message)\n \n-    def handle_startendtag(self, name, attrs):\n+    def handle_startendtag(\n+        self, tag: str, attrs: List[Tuple[str, Optional[str]]]\n+    ) -> None:\n         \"\"\"Handle an incoming empty-element tag.\n \n-        This is only called when the markup looks like <tag/>.\n-\n-        :param name: Name of the tag.\n-        :param attrs: Dictionary of the tag's attributes.\n+        html.parser only calls this method when the markup looks like\n+        <tag/>.\n         \"\"\"\n-        # is_startend() tells handle_starttag not to close the tag\n+        # `handle_empty_element` tells handle_starttag not to close the tag\n         # just because its name matches a known empty-element tag. We\n-        # know that this is an empty-element tag and we want to call\n+        # know that this is an empty-element tag, and we want to call\n         # handle_endtag ourselves.\n-        tag = self.handle_starttag(name, attrs, handle_empty_element=False)\n-        self.handle_endtag(name)\n-        \n-    def handle_starttag(self, name, attrs, handle_empty_element=True):\n+        self.handle_starttag(tag, attrs, handle_empty_element=False)\n+        self.handle_endtag(tag)\n+\n+    def handle_starttag(\n+        self,\n+        tag: str,\n+        attrs: List[Tuple[str, Optional[str]]],\n+        handle_empty_element: bool = True,\n+    ) -> None:\n         \"\"\"Handle an opening tag, e.g. '<tag>'\n \n-        :param name: Name of the tag.\n-        :param attrs: Dictionary of the tag's attributes.\n         :param handle_empty_element: True if this tag is known to be\n             an empty-element tag (i.e. there is not expected to be any\n             closing tag).\n         \"\"\"\n-        # XXX namespace\n-        attr_dict = {}\n+        # TODO: handle namespaces here?\n+        attr_dict: AttributeDict = self.attribute_dict_class()\n         for key, value in attrs:\n             # Change None attribute values to the empty string\n             # for consistency with the other tree builders.\n             if value is None:\n-                value = ''\n+                value = \"\"\n             if key in attr_dict:\n                 # A single attribute shows up multiple times in this\n                 # tag. How to handle it depends on the\n@@ -128,17 +168,21 @@ def handle_starttag(self, name, attrs, handle_empty_element=True):\n                 elif on_dupe in (None, self.REPLACE):\n                     attr_dict[key] = value\n                 else:\n+                    on_dupe = cast(_DuplicateAttributeHandler, on_dupe)\n                     on_dupe(attr_dict, key, value)\n             else:\n                 attr_dict[key] = value\n-            attrvalue = '\"\"'\n-        #print(\"START\", name)\n-        sourceline, sourcepos = self.getpos()\n-        tag = self.soup.handle_starttag(\n-            name, None, None, attr_dict, sourceline=sourceline,\n-            sourcepos=sourcepos\n+        # print(\"START\", tag)\n+        sourceline: Optional[int]\n+        sourcepos: Optional[int]\n+        if self.soup.builder.store_line_numbers:\n+            sourceline, sourcepos = self.getpos()\n+        else:\n+            sourceline = sourcepos = None\n+        tagObj = self.soup.handle_starttag(\n+            tag, None, None, attr_dict, sourceline=sourceline, sourcepos=sourcepos\n         )\n-        if tag and tag.is_empty_element and handle_empty_element:\n+        if tagObj is not None and tagObj.is_empty_element and handle_empty_element:\n             # Unlike other parsers, html.parser doesn't send separate end tag\n             # events for empty-element tags. (It's handled in\n             # handle_startendtag, but only if the original markup looked like\n@@ -148,38 +192,38 @@ def handle_starttag(self, name, attrs, handle_empty_element=True):\n             # know the start event is identical to the end event, we\n             # don't want handle_endtag() to cross off any previous end\n             # events for tags of this name.\n-            self.handle_endtag(name, check_already_closed=False)\n+            self.handle_endtag(tag, check_already_closed=False)\n \n             # But we might encounter an explicit closing tag for this tag\n             # later on. If so, we want to ignore it.\n-            self.already_closed_empty_element.append(name)\n+            self.already_closed_empty_element.append(tag)\n \n-        if self._root_tag is None:\n-            self._root_tag_encountered(name)\n-            \n-    def handle_endtag(self, name, check_already_closed=True):\n+        if self._root_tag_name is None:\n+            self._root_tag_encountered(tag)\n+\n+    def handle_endtag(self, tag: str, check_already_closed: bool = True) -> None:\n         \"\"\"Handle a closing tag, e.g. '</tag>'\n-        \n-        :param name: A tag name.\n+\n+        :param tag: A tag name.\n         :param check_already_closed: True if this tag is expected to\n            be the closing portion of an empty-element tag,\n            e.g. '<tag></tag>'.\n         \"\"\"\n-        #print(\"END\", name)\n-        if check_already_closed and name in self.already_closed_empty_element:\n+        # print(\"END\", tag)\n+        if check_already_closed and tag in self.already_closed_empty_element:\n             # This is a redundant end tag for an empty-element tag.\n             # We've already called handle_endtag() for it, so just\n             # check it off the list.\n-            #print(\"ALREADY CLOSED\", name)\n-            self.already_closed_empty_element.remove(name)\n+            # print(\"ALREADY CLOSED\", tag)\n+            self.already_closed_empty_element.remove(tag)\n         else:\n-            self.soup.handle_endtag(name)\n-            \n-    def handle_data(self, data):\n+            self.soup.handle_endtag(tag)\n+\n+    def handle_data(self, data: str) -> None:\n         \"\"\"Handle some textual data that shows up between tags.\"\"\"\n         self.soup.handle_data(data)\n \n-    def handle_charref(self, name):\n+    def handle_charref(self, name: str) -> None:\n         \"\"\"Handle a numeric character reference by converting it to the\n         corresponding Unicode character and treating it as textual\n         data.\n@@ -190,36 +234,20 @@ def handle_charref(self, name):\n         # HTMLParser. (http://bugs.python.org/issue13633) The bug has\n         # been fixed, but removing this code still makes some\n         # Beautiful Soup tests fail. This needs investigation.\n-        if name.startswith('x'):\n-            real_name = int(name.lstrip('x'), 16)\n-        elif name.startswith('X'):\n-            real_name = int(name.lstrip('X'), 16)\n+        real_name:int\n+        if name.startswith(\"x\"):\n+            real_name = int(name.lstrip(\"x\"), 16)\n+        elif name.startswith(\"X\"):\n+            real_name = int(name.lstrip(\"X\"), 16)\n         else:\n             real_name = int(name)\n \n-        data = None\n-        if real_name < 256:\n-            # HTML numeric entities are supposed to reference Unicode\n-            # code points, but sometimes they reference code points in\n-            # some other encoding (ahem, Windows-1252). E.g. &#147;\n-            # instead of &#201; for LEFT DOUBLE QUOTATION MARK. This\n-            # code tries to detect this situation and compensate.\n-            for encoding in (self.soup.original_encoding, 'windows-1252'):\n-                if not encoding:\n-                    continue\n-                try:\n-                    data = bytearray([real_name]).decode(encoding)\n-                except UnicodeDecodeError as e:\n-                    pass\n-        if not data:\n-            try:\n-                data = chr(real_name)\n-            except (ValueError, OverflowError) as e:\n-                pass\n-        data = data or \"\\N{REPLACEMENT CHARACTER}\"\n+        data, replacement_added = UnicodeDammit.numeric_character_reference(real_name)\n+        if replacement_added:\n+            self.soup.contains_replacement_characters = True\n         self.handle_data(data)\n \n-    def handle_entityref(self, name):\n+    def handle_entityref(self, name: str) -> None:\n         \"\"\"Handle a named entity reference by converting it to the\n         corresponding Unicode character(s) and treating it as textual\n         data.\n@@ -238,7 +266,7 @@ def handle_entityref(self, name):\n             data = \"&%s\" % name\n         self.handle_data(data)\n \n-    def handle_comment(self, data):\n+    def handle_comment(self, data: str) -> None:\n         \"\"\"Handle an HTML comment.\n \n         :param data: The text of the comment.\n@@ -247,31 +275,32 @@ def handle_comment(self, data):\n         self.soup.handle_data(data)\n         self.soup.endData(Comment)\n \n-    def handle_decl(self, data):\n+    def handle_decl(self, decl: str) -> None:\n         \"\"\"Handle a DOCTYPE declaration.\n \n         :param data: The text of the declaration.\n         \"\"\"\n         self.soup.endData()\n-        data = data[len(\"DOCTYPE \"):]\n-        self.soup.handle_data(data)\n+        decl = decl[len(\"DOCTYPE \") :]\n+        self.soup.handle_data(decl)\n         self.soup.endData(Doctype)\n \n-    def unknown_decl(self, data):\n+    def unknown_decl(self, data: str) -> None:\n         \"\"\"Handle a declaration of unknown type -- probably a CDATA block.\n \n         :param data: The text of the declaration.\n         \"\"\"\n-        if data.upper().startswith('CDATA['):\n+        cls: Type[NavigableString]\n+        if data.upper().startswith(\"CDATA[\"):\n             cls = CData\n-            data = data[len('CDATA['):]\n+            data = data[len(\"CDATA[\") :]\n         else:\n             cls = Declaration\n         self.soup.endData()\n         self.soup.handle_data(data)\n         self.soup.endData(cls)\n \n-    def handle_pi(self, data):\n+    def handle_pi(self, data: str) -> None:\n         \"\"\"Handle a processing instruction.\n \n         :param data: The text of the instruction.\n@@ -283,46 +312,59 @@ def handle_pi(self, data):\n \n \n class HTMLParserTreeBuilder(HTMLTreeBuilder):\n-    \"\"\"A Beautiful soup `TreeBuilder` that uses the `HTMLParser` parser,\n-    found in the Python standard library.\n-    \"\"\"\n-    is_xml = False\n-    picklable = True\n-    NAME = HTMLPARSER\n-    features = [NAME, HTML, STRICT]\n+    \"\"\"A Beautiful soup `bs4.builder.TreeBuilder` that uses the\n+    :py:class:`html.parser.HTMLParser` parser, found in the Python\n+    standard library.\n \n-    # The html.parser knows which line number and position in the\n-    # original file is the source of an element.\n-    TRACKS_LINE_NUMBERS = True\n+    \"\"\"\n \n-    def __init__(self, parser_args=None, parser_kwargs=None, **kwargs):\n+    is_xml: bool = False\n+    picklable: bool = True\n+    NAME: str = HTMLPARSER\n+    features: Iterable[str] = [NAME, HTML, STRICT]\n+    parser_args: Tuple[Iterable[Any], Dict[str, Any]]\n+\n+    #: The html.parser knows which line number and position in the\n+    #: original file is the source of an element.\n+    TRACKS_LINE_NUMBERS: bool = True\n+\n+    def __init__(\n+        self,\n+        parser_args: Optional[Iterable[Any]] = None,\n+        parser_kwargs: Optional[Dict[str, Any]] = None,\n+        **kwargs: Any,\n+    ):\n         \"\"\"Constructor.\n \n-        :param parser_args: Positional arguments to pass into \n+        :param parser_args: Positional arguments to pass into\n             the BeautifulSoupHTMLParser constructor, once it's\n             invoked.\n-        :param parser_kwargs: Keyword arguments to pass into \n+        :param parser_kwargs: Keyword arguments to pass into\n             the BeautifulSoupHTMLParser constructor, once it's\n             invoked.\n         :param kwargs: Keyword arguments for the superclass constructor.\n         \"\"\"\n         # Some keyword arguments will be pulled out of kwargs and placed\n         # into parser_kwargs.\n         extra_parser_kwargs = dict()\n-        for arg in ('on_duplicate_attribute',):\n+        for arg in (\"on_duplicate_attribute\",):\n             if arg in kwargs:\n                 value = kwargs.pop(arg)\n                 extra_parser_kwargs[arg] = value\n         super(HTMLParserTreeBuilder, self).__init__(**kwargs)\n         parser_args = parser_args or []\n         parser_kwargs = parser_kwargs or {}\n         parser_kwargs.update(extra_parser_kwargs)\n-        parser_kwargs['convert_charrefs'] = False\n+        parser_kwargs[\"convert_charrefs\"] = False\n         self.parser_args = (parser_args, parser_kwargs)\n-        \n-    def prepare_markup(self, markup, user_specified_encoding=None,\n-                       document_declared_encoding=None, exclude_encodings=None):\n \n+    def prepare_markup(\n+        self,\n+        markup: _RawMarkup,\n+        user_specified_encoding: Optional[_Encoding] = None,\n+        document_declared_encoding: Optional[_Encoding] = None,\n+        exclude_encodings: Optional[_Encodings] = None,\n+    ) -> Iterable[Tuple[str, Optional[_Encoding], Optional[_Encoding], bool]]:\n         \"\"\"Run any preliminary steps necessary to make incoming markup\n         acceptable to the parser.\n \n@@ -333,13 +375,13 @@ def prepare_markup(self, markup, user_specified_encoding=None,\n         :param exclude_encodings: The user asked _not_ to try any of\n             these encodings.\n \n-        :yield: A series of 4-tuples:\n-         (markup, encoding, declared encoding,\n-          has undergone character replacement)\n+        :yield: A series of 4-tuples: (markup, encoding, declared encoding,\n+             has undergone character replacement)\n \n-         Each 4-tuple represents a strategy for converting the\n-         document to Unicode and parsing it. Each strategy will be tried \n-         in turn.\n+            Each 4-tuple represents a strategy for parsing the document.\n+            This TreeBuilder uses Unicode, Dammit to convert the markup\n+            into Unicode, so the ``markup`` element of the tuple will\n+            always be a string.\n         \"\"\"\n         if isinstance(markup, str):\n             # Parse Unicode as-is.\n@@ -348,34 +390,67 @@ def prepare_markup(self, markup, user_specified_encoding=None,\n \n         # Ask UnicodeDammit to sniff the most likely encoding.\n \n-        # This was provided by the end-user; treat it as a known\n-        # definite encoding per the algorithm laid out in the HTML5\n-        # spec.  (See the EncodingDetector class for details.)\n-        known_definite_encodings = [user_specified_encoding]\n+        known_definite_encodings: List[_Encoding] = []\n+        if user_specified_encoding:\n+            # This was provided by the end-user; treat it as a known\n+            # definite encoding per the algorithm laid out in the\n+            # HTML5 spec. (See the EncodingDetector class for\n+            # details.)\n+            known_definite_encodings.append(user_specified_encoding)\n \n-        # This was found in the document; treat it as a slightly lower-priority\n-        # user encoding.\n-        user_encodings = [document_declared_encoding]\n+        user_encodings: List[_Encoding] = []\n+        if document_declared_encoding:\n+            # This was found in the document; treat it as a slightly\n+            # lower-priority user encoding.\n+            user_encodings.append(document_declared_encoding)\n \n-        try_encodings = [user_specified_encoding, document_declared_encoding]\n         dammit = UnicodeDammit(\n             markup,\n             known_definite_encodings=known_definite_encodings,\n             user_encodings=user_encodings,\n             is_html=True,\n-            exclude_encodings=exclude_encodings\n+            exclude_encodings=exclude_encodings,\n         )\n-        yield (dammit.markup, dammit.original_encoding,\n-               dammit.declared_html_encoding,\n-               dammit.contains_replacement_characters)\n \n-    def feed(self, markup):\n-        \"\"\"Run some incoming markup through some parsing process,\n-        populating the `BeautifulSoup` object in self.soup.\n+        if dammit.unicode_markup is None:\n+            # In every case I've seen, Unicode, Dammit is able to\n+            # convert the markup into Unicode, even if it needs to use\n+            # REPLACEMENT CHARACTER. But there is a code path that\n+            # could result in unicode_markup being None, and\n+            # HTMLParser can only parse Unicode, so here we handle\n+            # that code path.\n+            raise ParserRejectedMarkup(\n+                \"Could not convert input to Unicode, and html.parser will not accept bytestrings.\"\n+            )\n+        else:\n+            yield (\n+                dammit.unicode_markup,\n+                dammit.original_encoding,\n+                dammit.declared_html_encoding,\n+                dammit.contains_replacement_characters,\n+            )\n+\n+    def feed(self, markup: _RawMarkup, _parser_class:type[BeautifulSoupHTMLParser] =BeautifulSoupHTMLParser) -> None:\n+        \"\"\"\n+        :param markup: The markup to feed into the parser.\n+        :param _parser_class: An HTMLParser subclass to use. This is only intended for use in unit tests.\n         \"\"\"\n         args, kwargs = self.parser_args\n-        parser = BeautifulSoupHTMLParser(*args, **kwargs)\n-        parser.soup = self.soup\n+\n+        # HTMLParser.feed will only handle str, but\n+        # BeautifulSoup.markup is allowed to be _RawMarkup, because\n+        # it's set by the yield value of\n+        # TreeBuilder.prepare_markup. Fortunately,\n+        # HTMLParserTreeBuilder.prepare_markup always yields a str\n+        # (UnicodeDammit.unicode_markup).\n+        assert isinstance(markup, str)\n+\n+        # We know BeautifulSoup calls TreeBuilder.initialize_soup\n+        # before calling feed(), so we can assume self.soup\n+        # is set.\n+        assert self.soup is not None\n+        parser = _parser_class(self.soup, *args, **kwargs)\n+\n         try:\n             parser.feed(markup)\n             parser.close()"}, {"sha": "264d86147840ed583e3f6816fab3d47b58d814cc", "filename": "lib/bs4/builder/_lxml.py", "status": "modified", "additions": 247, "deletions": 134, "changes": 381, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fbuilder%2F_lxml.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fbuilder%2F_lxml.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fbuilder%2F_lxml.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,20 +1,37 @@\n+# encoding: utf-8\n+from __future__ import annotations\n+\n # Use of this source code is governed by the MIT license.\n __license__ = \"MIT\"\n \n __all__ = [\n-    'LXMLTreeBuilderForXML',\n-    'LXMLTreeBuilder',\n-    ]\n-\n-try:\n-    from collections.abc import Callable # Python 3.6\n-except ImportError as e:\n-    from collections import Callable\n+    \"LXMLTreeBuilderForXML\",\n+    \"LXMLTreeBuilder\",\n+]\n+\n+\n+from typing import (\n+    Any,\n+    Dict,\n+    Iterable,\n+    List,\n+    Optional,\n+    Set,\n+    Tuple,\n+    Type,\n+    TYPE_CHECKING,\n+    Union,\n+)\n \n from io import BytesIO\n from io import StringIO\n-from lxml import etree\n+\n+from typing_extensions import TypeAlias\n+\n+from lxml import etree # type:ignore\n from bs4.element import (\n+    AttributeDict,\n+    XMLAttributeDict,\n     Comment,\n     Doctype,\n     NamespacedAttribute,\n@@ -27,54 +44,89 @@\n     HTML,\n     HTMLTreeBuilder,\n     PERMISSIVE,\n-    ParserRejectedMarkup,\n     TreeBuilder,\n-    XML)\n+    XML,\n+)\n from bs4.dammit import EncodingDetector\n+from bs4.exceptions import ParserRejectedMarkup\n+\n+if TYPE_CHECKING:\n+    from bs4._typing import (\n+        _Encoding,\n+        _Encodings,\n+        _NamespacePrefix,\n+        _NamespaceURL,\n+        _NamespaceMapping,\n+        _InvertedNamespaceMapping,\n+        _RawMarkup,\n+    )\n+    from bs4 import BeautifulSoup\n+\n+LXML: str = \"lxml\"\n \n-LXML = 'lxml'\n \n-def _invert(d):\n+def _invert(d: dict[Any, Any]) -> dict[Any, Any]:\n     \"Invert a dictionary.\"\n-    return dict((v,k) for k, v in list(d.items()))\n+    return dict((v, k) for k, v in list(d.items()))\n+\n+\n+_LXMLParser: TypeAlias = Union[etree.XMLParser, etree.HTMLParser]\n+_ParserOrParserClass: TypeAlias = Union[\n+    _LXMLParser, Type[etree.XMLParser], Type[etree.HTMLParser]\n+]\n+\n \n class LXMLTreeBuilderForXML(TreeBuilder):\n-    DEFAULT_PARSER_CLASS = etree.XMLParser\n+    DEFAULT_PARSER_CLASS: Type[etree.XMLParser] = etree.XMLParser\n+\n+    is_xml: bool = True\n+\n+    #: Set this to true (probably by passing huge_tree=True into the :\n+    #: BeautifulSoup constructor) to enable the lxml feature \"disable security\n+    #: restrictions and support very deep trees and very long text\n+    #: content\".\n+    huge_tree: bool\n \n-    is_xml = True\n-    processing_instruction_class = XMLProcessingInstruction\n+    processing_instruction_class: Type[ProcessingInstruction]\n \n-    NAME = \"lxml-xml\"\n-    ALTERNATE_NAMES = [\"xml\"]\n+    NAME: str = \"lxml-xml\"\n+    ALTERNATE_NAMES: Iterable[str] = [\"xml\"]\n \n     # Well, it's permissive by XML parser standards.\n-    features = [NAME, LXML, XML, FAST, PERMISSIVE]\n+    features: Iterable[str] = [NAME, LXML, XML, FAST, PERMISSIVE]\n \n-    CHUNK_SIZE = 512\n+    CHUNK_SIZE: int = 512\n \n     # This namespace mapping is specified in the XML Namespace\n     # standard.\n-    DEFAULT_NSMAPS = dict(xml='http://www.w3.org/XML/1998/namespace')\n+    DEFAULT_NSMAPS: _NamespaceMapping = dict(xml=\"http://www.w3.org/XML/1998/namespace\")\n \n-    DEFAULT_NSMAPS_INVERTED = _invert(DEFAULT_NSMAPS)\n+    DEFAULT_NSMAPS_INVERTED: _InvertedNamespaceMapping = _invert(DEFAULT_NSMAPS)\n+\n+    nsmaps: List[Optional[_InvertedNamespaceMapping]]\n+    empty_element_tags: Optional[Set[str]]\n+    parser: Any\n+    _default_parser: Optional[etree.XMLParser]\n \n     # NOTE: If we parsed Element objects and looked at .sourceline,\n     # we'd be able to see the line numbers from the original document.\n     # But instead we build an XMLParser or HTMLParser object to serve\n     # as the target of parse messages, and those messages don't include\n     # line numbers.\n     # See: https://bugs.launchpad.net/lxml/+bug/1846906\n-    \n-    def initialize_soup(self, soup):\n+\n+    def initialize_soup(self, soup: BeautifulSoup) -> None:\n         \"\"\"Let the BeautifulSoup object know about the standard namespace\n         mapping.\n \n         :param soup: A `BeautifulSoup`.\n         \"\"\"\n+        # Beyond this point, self.soup is set, so we can assume (and\n+        # assert) it's not None whenever necessary.\n         super(LXMLTreeBuilderForXML, self).initialize_soup(soup)\n         self._register_namespaces(self.DEFAULT_NSMAPS)\n \n-    def _register_namespaces(self, mapping):\n+    def _register_namespaces(self, mapping: Dict[str, str]) -> None:\n         \"\"\"Let the BeautifulSoup object know about namespaces encountered\n         while parsing the document.\n \n@@ -87,6 +139,7 @@ def _register_namespaces(self, mapping):\n \n         :param mapping: A dictionary mapping namespace prefixes to URIs.\n         \"\"\"\n+        assert self.soup is not None\n         for key, value in list(mapping.items()):\n             # This is 'if key' and not 'if key is not None' because we\n             # don't track un-prefixed namespaces. Soupselect will\n@@ -97,20 +150,18 @@ def _register_namespaces(self, mapping):\n                 # If there are multiple namespaces defined with the same\n                 # prefix, the first one in the document takes precedence.\n                 self.soup._namespaces[key] = value\n-                \n-    def default_parser(self, encoding):\n+\n+    def default_parser(self, encoding: Optional[_Encoding]) -> _ParserOrParserClass:\n         \"\"\"Find the default parser for the given encoding.\n \n-        :param encoding: A string.\n         :return: Either a parser object or a class, which\n           will be instantiated with default arguments.\n         \"\"\"\n         if self._default_parser is not None:\n             return self._default_parser\n-        return etree.XMLParser(\n-            target=self, strip_cdata=False, recover=True, encoding=encoding)\n+        return self.DEFAULT_PARSER_CLASS(target=self, recover=True, huge_tree=self.huge_tree, encoding=encoding)\n \n-    def parser_for(self, encoding):\n+    def parser_for(self, encoding: Optional[_Encoding]) -> _LXMLParser:\n         \"\"\"Instantiate an appropriate parser for the given encoding.\n \n         :param encoding: A string.\n@@ -119,36 +170,53 @@ def parser_for(self, encoding):\n         # Use the default parser.\n         parser = self.default_parser(encoding)\n \n-        if isinstance(parser, Callable):\n+        if callable(parser):\n             # Instantiate the parser with default arguments\n-            parser = parser(\n-                target=self, strip_cdata=False, recover=True, encoding=encoding\n-            )\n+            parser = parser(target=self, recover=True, huge_tree=self.huge_tree, encoding=encoding)\n         return parser\n \n-    def __init__(self, parser=None, empty_element_tags=None, **kwargs):\n+    def __init__(\n+            self,\n+            parser: Optional[etree.XMLParser] = None,\n+            empty_element_tags: Optional[Set[str]] = None,\n+            huge_tree: bool = False,\n+            **kwargs: Any,\n+    ):\n         # TODO: Issue a warning if parser is present but not a\n         # callable, since that means there's no way to create new\n         # parsers for different encodings.\n         self._default_parser = parser\n-        if empty_element_tags is not None:\n-            self.empty_element_tags = set(empty_element_tags)\n         self.soup = None\n         self.nsmaps = [self.DEFAULT_NSMAPS_INVERTED]\n         self.active_namespace_prefixes = [dict(self.DEFAULT_NSMAPS)]\n+        if self.is_xml:\n+            self.processing_instruction_class = XMLProcessingInstruction\n+        else:\n+            self.processing_instruction_class = ProcessingInstruction\n+\n+        if \"attribute_dict_class\" not in kwargs:\n+            kwargs[\"attribute_dict_class\"] = XMLAttributeDict\n+        self.huge_tree = huge_tree\n+\n         super(LXMLTreeBuilderForXML, self).__init__(**kwargs)\n-        \n-    def _getNsTag(self, tag):\n+\n+    def _getNsTag(self, tag: str) -> Tuple[Optional[str], str]:\n         # Split the namespace URL out of a fully-qualified lxml tag\n         # name. Copied from lxml's src/lxml/sax.py.\n-        if tag[0] == '{':\n-            return tuple(tag[1:].split('}', 1))\n-        else:\n-            return (None, tag)\n-\n-    def prepare_markup(self, markup, user_specified_encoding=None,\n-                       exclude_encodings=None,\n-                       document_declared_encoding=None):\n+        if tag[0] == \"{\" and \"}\" in tag:\n+            namespace, name = tag[1:].split(\"}\", 1)\n+            return (namespace, name)\n+        return (None, tag)\n+\n+    def prepare_markup(\n+        self,\n+        markup: _RawMarkup,\n+        user_specified_encoding: Optional[_Encoding] = None,\n+        document_declared_encoding: Optional[_Encoding] = None,\n+        exclude_encodings: Optional[_Encodings] = None,\n+    ) -> Iterable[\n+        Tuple[Union[str, bytes], Optional[_Encoding], Optional[_Encoding], bool]\n+    ]:\n         \"\"\"Run any preliminary steps necessary to make incoming markup\n         acceptable to the parser.\n \n@@ -166,24 +234,17 @@ def prepare_markup(self, markup, user_specified_encoding=None,\n         :param exclude_encodings: The user asked _not_ to try any of\n             these encodings.\n \n-        :yield: A series of 4-tuples:\n-         (markup, encoding, declared encoding,\n-          has undergone character replacement)\n+        :yield: A series of 4-tuples: (markup, encoding, declared encoding,\n+            has undergone character replacement)\n \n-         Each 4-tuple represents a strategy for converting the\n-         document to Unicode and parsing it. Each strategy will be tried \n-         in turn.\n+            Each 4-tuple represents a strategy for converting the\n+            document to Unicode and parsing it. Each strategy will be tried\n+            in turn.\n         \"\"\"\n-        is_html = not self.is_xml\n-        if is_html:\n-            self.processing_instruction_class = ProcessingInstruction\n+        if not self.is_xml:\n             # We're in HTML mode, so if we're given XML, that's worth\n             # noting.\n-            DetectsXMLParsedAsHTML.warn_if_markup_looks_like_xml(\n-                markup, stacklevel=3\n-            )\n-        else:\n-            self.processing_instruction_class = XMLProcessingInstruction\n+            DetectsXMLParsedAsHTML.warn_if_markup_looks_like_xml(markup, stacklevel=3)\n \n         if isinstance(markup, str):\n             # We were given Unicode. Maybe lxml can parse Unicode on\n@@ -192,66 +253,107 @@ def prepare_markup(self, markup, user_specified_encoding=None,\n             # TODO: This is a workaround for\n             # https://bugs.launchpad.net/lxml/+bug/1948551.\n             # We can remove it once the upstream issue is fixed.\n-            if len(markup) > 0 and markup[0] == u'\\N{BYTE ORDER MARK}':\n+            if len(markup) > 0 and markup[0] == \"\\N{BYTE ORDER MARK}\":\n                 markup = markup[1:]\n             yield markup, None, document_declared_encoding, False\n \n         if isinstance(markup, str):\n             # No, apparently not. Convert the Unicode to UTF-8 and\n             # tell lxml to parse it as UTF-8.\n-            yield (markup.encode(\"utf8\"), \"utf8\",\n-                   document_declared_encoding, False)\n+            yield (markup.encode(\"utf8\"), \"utf8\", document_declared_encoding, False)\n+\n+            # Since the document was Unicode in the first place, there\n+            # is no need to try any more strategies; we know this will\n+            # work.\n+            return\n+\n+        known_definite_encodings: List[_Encoding] = []\n+        if user_specified_encoding:\n+            # This was provided by the end-user; treat it as a known\n+            # definite encoding per the algorithm laid out in the\n+            # HTML5 spec. (See the EncodingDetector class for\n+            # details.)\n+            known_definite_encodings.append(user_specified_encoding)\n+\n+        user_encodings: List[_Encoding] = []\n+        if document_declared_encoding:\n+            # This was found in the document; treat it as a slightly\n+            # lower-priority user encoding.\n+            user_encodings.append(document_declared_encoding)\n \n-        # This was provided by the end-user; treat it as a known\n-        # definite encoding per the algorithm laid out in the HTML5\n-        # spec.  (See the EncodingDetector class for details.)\n-        known_definite_encodings = [user_specified_encoding]\n-\n-        # This was found in the document; treat it as a slightly lower-priority\n-        # user encoding.\n-        user_encodings = [document_declared_encoding]\n         detector = EncodingDetector(\n-            markup, known_definite_encodings=known_definite_encodings,\n-            user_encodings=user_encodings, is_html=is_html,\n-            exclude_encodings=exclude_encodings\n+            markup,\n+            known_definite_encodings=known_definite_encodings,\n+            user_encodings=user_encodings,\n+            is_html=not self.is_xml,\n+            exclude_encodings=exclude_encodings,\n         )\n         for encoding in detector.encodings:\n             yield (detector.markup, encoding, document_declared_encoding, False)\n \n-    def feed(self, markup):\n+    def feed(self, markup: _RawMarkup) -> None:\n+        io: Union[BytesIO, StringIO]\n         if isinstance(markup, bytes):\n-            markup = BytesIO(markup)\n+            io = BytesIO(markup)\n         elif isinstance(markup, str):\n-            markup = StringIO(markup)\n+            io = StringIO(markup)\n+\n+        # initialize_soup is called before feed, so we know this\n+        # is not None.\n+        assert self.soup is not None\n \n         # Call feed() at least once, even if the markup is empty,\n         # or the parser won't be initialized.\n-        data = markup.read(self.CHUNK_SIZE)\n+        data = io.read(self.CHUNK_SIZE)\n         try:\n             self.parser = self.parser_for(self.soup.original_encoding)\n             self.parser.feed(data)\n             while len(data) != 0:\n                 # Now call feed() on the rest of the data, chunk by chunk.\n-                data = markup.read(self.CHUNK_SIZE)\n+                data = io.read(self.CHUNK_SIZE)\n                 if len(data) != 0:\n                     self.parser.feed(data)\n             self.parser.close()\n         except (UnicodeDecodeError, LookupError, etree.ParserError) as e:\n             raise ParserRejectedMarkup(e)\n \n-    def close(self):\n+    def close(self) -> None:\n         self.nsmaps = [self.DEFAULT_NSMAPS_INVERTED]\n \n-    def start(self, name, attrs, nsmap={}):\n-        # Make sure attrs is a mutable dict--lxml may send an immutable dictproxy.\n-        attrs = dict(attrs)\n-        nsprefix = None\n+    def start(\n+        self,\n+        tag: str | bytes,\n+        attrib: Dict[str | bytes, str | bytes],\n+        nsmap: _NamespaceMapping = {},\n+    ) -> None:\n+        # This is called by lxml code as a result of calling\n+        # BeautifulSoup.feed(), and we know self.soup is set by the time feed()\n+        # is called.\n+        assert self.soup is not None\n+        assert isinstance(tag, str)\n+\n+        # We need to recreate the attribute dict for three\n+        # reasons. First, for type checking, so we can assert there\n+        # are no bytestrings in the keys or values. Second, because we\n+        # need a mutable dict--lxml might send us an immutable\n+        # dictproxy. Third, so we can handle namespaced attribute\n+        # names by converting the keys to NamespacedAttributes.\n+        new_attrib: Dict[Union[str, NamespacedAttribute], str] = (\n+            self.attribute_dict_class()\n+        )\n+        for k, v in attrib.items():\n+            assert isinstance(k, str)\n+            assert isinstance(v, str)\n+            new_attrib[k] = v\n+\n+        nsprefix: Optional[_NamespacePrefix] = None\n+        namespace: Optional[_NamespaceURL] = None\n         # Invert each namespace map as it comes in.\n         if len(nsmap) == 0 and len(self.nsmaps) > 1:\n-                # There are no new namespaces for this tag, but\n-                # non-default namespaces are in play, so we need a\n-                # separate tag stack to know when they end.\n-                self.nsmaps.append(None)\n+            # There are no new namespaces for this tag, but\n+            # non-default namespaces are in play, so we need a\n+            # separate tag stack to know when they end.\n+            self.nsmaps.append(None)\n         elif len(nsmap) > 0:\n             # A new namespace mapping has come into play.\n \n@@ -272,40 +374,44 @@ def start(self, name, attrs, nsmap={}):\n             # We should not track un-prefixed namespaces as we can only hold one\n             # and it will be recognized as the default namespace by soupsieve,\n             # which may be confusing in some situations.\n-            if '' in current_mapping:\n-                del current_mapping['']\n+            if \"\" in current_mapping:\n+                del current_mapping[\"\"]\n             self.active_namespace_prefixes.append(current_mapping)\n-            \n+\n             # Also treat the namespace mapping as a set of attributes on the\n             # tag, so we can recreate it later.\n-            attrs = attrs.copy()\n             for prefix, namespace in list(nsmap.items()):\n                 attribute = NamespacedAttribute(\n-                    \"xmlns\", prefix, \"http://www.w3.org/2000/xmlns/\")\n-                attrs[attribute] = namespace\n+                    \"xmlns\", prefix, \"http://www.w3.org/2000/xmlns/\"\n+                )\n+                new_attrib[attribute] = namespace\n \n         # Namespaces are in play. Find any attributes that came in\n         # from lxml with namespaces attached to their names, and\n         # turn then into NamespacedAttribute objects.\n-        new_attrs = {}\n-        for attr, value in list(attrs.items()):\n+        final_attrib: AttributeDict = self.attribute_dict_class()\n+        for attr, value in list(new_attrib.items()):\n             namespace, attr = self._getNsTag(attr)\n             if namespace is None:\n-                new_attrs[attr] = value\n+                final_attrib[attr] = value\n             else:\n                 nsprefix = self._prefix_for_namespace(namespace)\n                 attr = NamespacedAttribute(nsprefix, attr, namespace)\n-                new_attrs[attr] = value\n-        attrs = new_attrs\n+                final_attrib[attr] = value\n \n-        namespace, name = self._getNsTag(name)\n+        namespace, tag = self._getNsTag(tag)\n         nsprefix = self._prefix_for_namespace(namespace)\n         self.soup.handle_starttag(\n-            name, namespace, nsprefix, attrs,\n-            namespaces=self.active_namespace_prefixes[-1]\n+            tag,\n+            namespace,\n+            nsprefix,\n+            final_attrib,\n+            namespaces=self.active_namespace_prefixes[-1],\n         )\n-        \n-    def _prefix_for_namespace(self, namespace):\n+\n+    def _prefix_for_namespace(\n+        self, namespace: Optional[_NamespaceURL]\n+    ) -> Optional[_NamespacePrefix]:\n         \"\"\"Find the currently active prefix for the given namespace.\"\"\"\n         if namespace is None:\n             return None\n@@ -314,17 +420,18 @@ def _prefix_for_namespace(self, namespace):\n                 return inverted_nsmap[namespace]\n         return None\n \n-    def end(self, name):\n+    def end(self, tag: str | bytes) -> None:\n+        assert self.soup is not None\n+        assert isinstance(tag, str)\n         self.soup.endData()\n-        completed_tag = self.soup.tagStack[-1]\n-        namespace, name = self._getNsTag(name)\n+        namespace, tag = self._getNsTag(tag)\n         nsprefix = None\n         if namespace is not None:\n             for inverted_nsmap in reversed(self.nsmaps):\n                 if inverted_nsmap is not None and namespace in inverted_nsmap:\n                     nsprefix = inverted_nsmap[namespace]\n                     break\n-        self.soup.handle_endtag(name, nsprefix)\n+        self.soup.handle_endtag(tag, nsprefix)\n         if len(self.nsmaps) > 1:\n             # This tag, or one of its parents, introduced a namespace\n             # mapping, so pop it off the stack.\n@@ -335,45 +442,52 @@ def end(self, name):\n                 # longer in scope. Recalculate the currently active\n                 # namespace prefixes.\n                 self.active_namespace_prefixes.pop()\n-            \n-    def pi(self, target, data):\n+\n+    def pi(self, target: str, data: str) -> None:\n+        assert self.soup is not None\n         self.soup.endData()\n-        data = target + ' ' + data\n+        data = target + \" \" + data\n         self.soup.handle_data(data)\n         self.soup.endData(self.processing_instruction_class)\n-        \n-    def data(self, content):\n-        self.soup.handle_data(content)\n \n-    def doctype(self, name, pubid, system):\n+    def data(self, data: str | bytes) -> None:\n+        assert self.soup is not None\n+        assert isinstance(data, str)\n+        self.soup.handle_data(data)\n+\n+    def doctype(self, name: str, pubid: str, system: str) -> None:\n+        assert self.soup is not None\n         self.soup.endData()\n-        doctype = Doctype.for_name_and_ids(name, pubid, system)\n-        self.soup.object_was_parsed(doctype)\n+        doctype_string = Doctype._string_for_name_and_ids(name, pubid, system)\n+        self.soup.handle_data(doctype_string)\n+        self.soup.endData(containerClass=Doctype)\n \n-    def comment(self, content):\n+    def comment(self, text: str | bytes) -> None:\n         \"Handle comments as Comment objects.\"\n+        assert self.soup is not None\n+        assert isinstance(text, str)\n         self.soup.endData()\n-        self.soup.handle_data(content)\n+        self.soup.handle_data(text)\n         self.soup.endData(Comment)\n \n-    def test_fragment_to_document(self, fragment):\n+    def test_fragment_to_document(self, fragment: str) -> str:\n         \"\"\"See `TreeBuilder`.\"\"\"\n         return '<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n%s' % fragment\n \n \n class LXMLTreeBuilder(HTMLTreeBuilder, LXMLTreeBuilderForXML):\n+    NAME: str = LXML\n+    ALTERNATE_NAMES: Iterable[str] = [\"lxml-html\"]\n \n-    NAME = LXML\n-    ALTERNATE_NAMES = [\"lxml-html\"]\n+    features: Iterable[str] = list(ALTERNATE_NAMES) + [NAME, HTML, FAST, PERMISSIVE]\n+    is_xml: bool = False\n \n-    features = ALTERNATE_NAMES + [NAME, HTML, FAST, PERMISSIVE]\n-    is_xml = False\n-    processing_instruction_class = ProcessingInstruction\n-\n-    def default_parser(self, encoding):\n+    def default_parser(self, encoding: Optional[_Encoding]) -> _ParserOrParserClass:\n         return etree.HTMLParser\n \n-    def feed(self, markup):\n+    def feed(self, markup: _RawMarkup) -> None:\n+        # We know self.soup is set by the time feed() is called.\n+        assert self.soup is not None\n         encoding = self.soup.original_encoding\n         try:\n             self.parser = self.parser_for(encoding)\n@@ -382,7 +496,6 @@ def feed(self, markup):\n         except (UnicodeDecodeError, LookupError, etree.ParserError) as e:\n             raise ParserRejectedMarkup(e)\n \n-\n-    def test_fragment_to_document(self, fragment):\n+    def test_fragment_to_document(self, fragment: str) -> str:\n         \"\"\"See `TreeBuilder`.\"\"\"\n-        return '<html><body>%s</body></html>' % fragment\n+        return \"<html><body>%s</body></html>\" % fragment"}, {"sha": "75ad998349af45e66a5fc2bbe1966960b7eab3ba", "filename": "lib/bs4/css.py", "status": "modified", "additions": 167, "deletions": 108, "changes": 275, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fcss.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fcss.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fcss.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "patch": "@@ -1,62 +1,86 @@\n-\"\"\"Integration code for CSS selectors using Soup Sieve (pypi: soupsieve).\"\"\"\n-\n+\"\"\"Integration code for CSS selectors using `Soup Sieve <https://facelessuser.github.io/soupsieve/>`_ (pypi: ``soupsieve``).\n+\n+Acquire a `CSS` object through the `element.Tag.css` attribute of\n+the starting point of your CSS selector, or (if you want to run a\n+selector against the entire document) of the `BeautifulSoup` object\n+itself.\n+\n+The main advantage of doing this instead of using ``soupsieve``\n+functions is that you don't need to keep passing the `element.Tag` to be\n+selected against, since the `CSS` object is permanently scoped to that\n+`element.Tag`.\n+\n+\"\"\"\n+\n+from __future__ import annotations\n+\n+from types import ModuleType\n+from typing import (\n+    Any,\n+    cast,\n+    Iterable,\n+    Iterator,\n+    MutableSequence,\n+    Optional,\n+    TYPE_CHECKING,\n+)\n import warnings\n+from bs4._typing import _NamespaceMapping\n+\n+if TYPE_CHECKING:\n+    from soupsieve import SoupSieve\n+    from bs4 import element\n+    from bs4.element import ResultSet, Tag\n+\n+soupsieve: Optional[ModuleType]\n try:\n     import soupsieve\n-except ImportError as e:\n+except ImportError:\n     soupsieve = None\n     warnings.warn(\n-        'The soupsieve package is not installed. CSS selectors cannot be used.'\n+        \"The soupsieve package is not installed. CSS selectors cannot be used.\"\n     )\n \n \n class CSS(object):\n-    \"\"\"A proxy object against the soupsieve library, to simplify its\n+    \"\"\"A proxy object against the ``soupsieve`` library, to simplify its\n     CSS selector API.\n \n-    Acquire this object through the .css attribute on the\n-    BeautifulSoup object, or on the Tag you want to use as the\n-    starting point for a CSS selector.\n-\n-    The main advantage of doing this is that the tag to be selected\n-    against doesn't need to be explicitly specified in the function\n-    calls, since it's already scoped to a tag.\n-    \"\"\"\n-\n-    def __init__(self, tag, api=soupsieve):\n-        \"\"\"Constructor.\n+    You don't need to instantiate this class yourself; instead, use\n+    `element.Tag.css`.\n \n-        You don't need to instantiate this class yourself; instead,\n-        access the .css attribute on the BeautifulSoup object, or on\n-        the Tag you want to use as the starting point for your CSS\n-        selector.\n+    :param tag: All CSS selectors run by this object will use this as\n+        their starting point.\n \n-        :param tag: All CSS selectors will use this as their starting\n-        point.\n+    :param api: An optional drop-in replacement for the ``soupsieve`` module,\n+        intended for use in unit tests.\n+    \"\"\"\n \n-        :param api: A plug-in replacement for the soupsieve module,\n-        designed mainly for use in tests.\n-        \"\"\"\n+    def __init__(self, tag: element.Tag, api: Optional[ModuleType] = None):\n+        if api is None:\n+            api = soupsieve\n         if api is None:\n             raise NotImplementedError(\n                 \"Cannot execute CSS selectors because the soupsieve package is not installed.\"\n             )\n         self.api = api\n         self.tag = tag\n \n-    def escape(self, ident):\n+    def escape(self, ident: str) -> str:\n         \"\"\"Escape a CSS identifier.\n \n-        This is a simple wrapper around soupselect.escape(). See the\n+        This is a simple wrapper around `soupsieve.escape() <https://facelessuser.github.io/soupsieve/api/#soupsieveescape>`_. See the\n         documentation for that function for more information.\n         \"\"\"\n         if soupsieve is None:\n             raise NotImplementedError(\n                 \"Cannot escape CSS identifiers because the soupsieve package is not installed.\"\n             )\n-        return self.api.escape(ident)\n+        return cast(str, self.api.escape(ident))\n \n-    def _ns(self, ns, select):\n+    def _ns(\n+        self, ns: Optional[_NamespaceMapping], select: str\n+    ) -> Optional[_NamespaceMapping]:\n         \"\"\"Normalize a dictionary of namespaces.\"\"\"\n         if not isinstance(select, self.api.SoupSieve) and ns is None:\n             # If the selector is a precompiled pattern, it already has\n@@ -65,19 +89,26 @@ def _ns(self, ns, select):\n             ns = self.tag._namespaces\n         return ns\n \n-    def _rs(self, results):\n-        \"\"\"Normalize a list of results to a Resultset.\n+    def _rs(self, results: MutableSequence[Tag]) -> ResultSet[Tag]:\n+        \"\"\"Normalize a list of results to a py:class:`ResultSet`.\n \n-        A ResultSet is more consistent with the rest of Beautiful\n-        Soup's API, and ResultSet.__getattr__ has a helpful error\n-        message if you try to treat a list of results as a single\n-        result (a common mistake).\n+        A py:class:`ResultSet` is more consistent with the rest of\n+        Beautiful Soup's API, and :py:meth:`ResultSet.__getattr__` has\n+        a helpful error message if you try to treat a list of results\n+        as a single result (a common mistake).\n         \"\"\"\n         # Import here to avoid circular import\n-        from bs4.element import ResultSet\n+        from bs4 import ResultSet\n+\n         return ResultSet(None, results)\n \n-    def compile(self, select, namespaces=None, flags=0, **kwargs):\n+    def compile(\n+        self,\n+        select: str,\n+        namespaces: Optional[_NamespaceMapping] = None,\n+        flags: int = 0,\n+        **kwargs: Any,\n+    ) -> SoupSieve:\n         \"\"\"Pre-compile a selector and return the compiled object.\n \n         :param selector: A CSS selector.\n@@ -88,25 +119,28 @@ def compile(self, select, namespaces=None, flags=0, **kwargs):\n            parsing the document.\n \n         :param flags: Flags to be passed into Soup Sieve's\n-            soupsieve.compile() method.\n+            `soupsieve.compile() <https://facelessuser.github.io/soupsieve/api/#soupsievecompile>`_ method.\n \n-        :param kwargs: Keyword arguments to be passed into SoupSieve's\n-           soupsieve.compile() method.\n+        :param kwargs: Keyword arguments to be passed into Soup Sieve's\n+           `soupsieve.compile() <https://facelessuser.github.io/soupsieve/api/#soupsievecompile>`_ method.\n \n         :return: A precompiled selector object.\n         :rtype: soupsieve.SoupSieve\n         \"\"\"\n-        return self.api.compile(\n-            select, self._ns(namespaces, select), flags, **kwargs\n-        )\n-\n-    def select_one(self, select, namespaces=None, flags=0, **kwargs):\n+        return self.api.compile(select, self._ns(namespaces, select), flags, **kwargs)\n+\n+    def select_one(\n+        self,\n+        select: str,\n+        namespaces: Optional[_NamespaceMapping] = None,\n+        flags: int = 0,\n+        **kwargs: Any,\n+    ) -> element.Tag | None:\n         \"\"\"Perform a CSS selection operation on the current Tag and return the\n-        first result.\n+        first result, if any.\n \n         This uses the Soup Sieve library. For more information, see\n-        that library's documentation for the soupsieve.select_one()\n-        method.\n+        that library's documentation for the `soupsieve.select_one() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect_one>`_ method.\n \n         :param selector: A CSS selector.\n \n@@ -116,27 +150,29 @@ def select_one(self, select, namespaces=None, flags=0, **kwargs):\n            parsing the document.\n \n         :param flags: Flags to be passed into Soup Sieve's\n-            soupsieve.select_one() method.\n-\n-        :param kwargs: Keyword arguments to be passed into SoupSieve's\n-           soupsieve.select_one() method.\n-\n-        :return: A Tag, or None if the selector has no match.\n-        :rtype: bs4.element.Tag\n+            `soupsieve.select_one() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect_one>`_ method.\n \n+        :param kwargs: Keyword arguments to be passed into Soup Sieve's\n+           `soupsieve.select_one() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect_one>`_ method.\n         \"\"\"\n         return self.api.select_one(\n             select, self.tag, self._ns(namespaces, select), flags, **kwargs\n         )\n \n-    def select(self, select, namespaces=None, limit=0, flags=0, **kwargs):\n-        \"\"\"Perform a CSS selection operation on the current Tag.\n+    def select(\n+        self,\n+        select: str,\n+        namespaces: Optional[_NamespaceMapping] = None,\n+        limit: int = 0,\n+        flags: int = 0,\n+        **kwargs: Any,\n+    ) -> ResultSet[element.Tag]:\n+        \"\"\"Perform a CSS selection operation on the current `element.Tag`.\n \n         This uses the Soup Sieve library. For more information, see\n-        that library's documentation for the soupsieve.select()\n-        method.\n+        that library's documentation for the `soupsieve.select() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect>`_ method.\n \n-        :param selector: A string containing a CSS selector.\n+        :param selector: A CSS selector.\n \n         :param namespaces: A dictionary mapping namespace prefixes\n             used in the CSS selector to namespace URIs. By default,\n@@ -146,30 +182,33 @@ def select(self, select, namespaces=None, limit=0, flags=0, **kwargs):\n         :param limit: After finding this number of results, stop looking.\n \n         :param flags: Flags to be passed into Soup Sieve's\n-            soupsieve.select() method.\n-\n-        :param kwargs: Keyword arguments to be passed into SoupSieve's\n-            soupsieve.select() method.\n-\n-        :return: A ResultSet of Tag objects.\n-        :rtype: bs4.element.ResultSet\n+            `soupsieve.select() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect>`_ method.\n \n+        :param kwargs: Keyword arguments to be passed into Soup Sieve's\n+           `soupsieve.select() <https://facelessuser.github.io/soupsieve/api/#soupsieveselect>`_ method.\n         \"\"\"\n         if limit is None:\n             limit = 0\n \n         return self._rs(\n             self.api.select(\n-                select, self.tag, self._ns(namespaces, select), limit, flags,\n-                **kwargs\n+                select, self.tag, self._ns(namespaces, select), limit, flags, **kwargs\n             )\n         )\n \n-    def iselect(self, select, namespaces=None, limit=0, flags=0, **kwargs):\n-        \"\"\"Perform a CSS selection operation on the current Tag.\n+    def iselect(\n+        self,\n+        select: str,\n+        namespaces: Optional[_NamespaceMapping] = None,\n+        limit: int = 0,\n+        flags: int = 0,\n+        **kwargs: Any,\n+    ) -> Iterator[element.Tag]:\n+        \"\"\"Perform a CSS selection operation on the current `element.Tag`.\n \n         This uses the Soup Sieve library. For more information, see\n-        that library's documentation for the soupsieve.iselect()\n+        that library's documentation for the `soupsieve.iselect()\n+        <https://facelessuser.github.io/soupsieve/api/#soupsieveiselect>`_\n         method. It is the same as select(), but it returns a generator\n         instead of a list.\n \n@@ -183,23 +222,27 @@ def iselect(self, select, namespaces=None, limit=0, flags=0, **kwargs):\n         :param limit: After finding this number of results, stop looking.\n \n         :param flags: Flags to be passed into Soup Sieve's\n-            soupsieve.iselect() method.\n-\n-        :param kwargs: Keyword arguments to be passed into SoupSieve's\n-            soupsieve.iselect() method.\n+            `soupsieve.iselect() <https://facelessuser.github.io/soupsieve/api/#soupsieveiselect>`_ method.\n \n-        :return: A generator\n-        :rtype: types.GeneratorType\n+        :param kwargs: Keyword arguments to be passed into Soup Sieve's\n+           `soupsieve.iselect() <https://facelessuser.github.io/soupsieve/api/#soupsieveiselect>`_ method.\n         \"\"\"\n         return self.api.iselect(\n             select, self.tag, self._ns(namespaces, select), limit, flags, **kwargs\n         )\n \n-    def closest(self, select, namespaces=None, flags=0, **kwargs):\n-        \"\"\"Find the Tag closest to this one that matches the given selector.\n+    def closest(\n+        self,\n+        select: str,\n+        namespaces: Optional[_NamespaceMapping] = None,\n+        flags: int = 0,\n+        **kwargs: Any,\n+    ) -> Optional[element.Tag]:\n+        \"\"\"Find the `element.Tag` closest to this one that matches the given selector.\n \n         This uses the Soup Sieve library. For more information, see\n-        that library's documentation for the soupsieve.closest()\n+        that library's documentation for the `soupsieve.closest()\n+        <https://facelessuser.github.io/soupsieve/api/#soupsieveclosest>`_\n         method.\n \n         :param selector: A string containing a CSS selector.\n@@ -210,24 +253,28 @@ def closest(self, select, namespaces=None, flags=0, **kwargs):\n             parsing the document.\n \n         :param flags: Flags to be passed into Soup Sieve's\n-            soupsieve.closest() method.\n-\n-        :param kwargs: Keyword arguments to be passed into SoupSieve's\n-            soupsieve.closest() method.\n+            `soupsieve.closest() <https://facelessuser.github.io/soupsieve/api/#soupsieveclosest>`_ method.\n \n-        :return: A Tag, or None if there is no match.\n-        :rtype: bs4.Tag\n+        :param kwargs: Keyword arguments to be passed into Soup Sieve's\n+           `soupsieve.closest() <https://facelessuser.github.io/soupsieve/api/#soupsieveclosest>`_ method.\n \n         \"\"\"\n         return self.api.closest(\n             select, self.tag, self._ns(namespaces, select), flags, **kwargs\n         )\n \n-    def match(self, select, namespaces=None, flags=0, **kwargs):\n-        \"\"\"Check whether this Tag matches the given CSS selector.\n+    def match(\n+        self,\n+        select: str,\n+        namespaces: Optional[_NamespaceMapping] = None,\n+        flags: int = 0,\n+        **kwargs: Any,\n+    ) -> bool:\n+        \"\"\"Check whether or not this `element.Tag` matches the given CSS selector.\n \n         This uses the Soup Sieve library. For more information, see\n-        that library's documentation for the soupsieve.match()\n+        that library's documentation for the `soupsieve.match()\n+        <https://facelessuser.github.io/soupsieve/api/#soupsievematch>`_\n         method.\n \n         :param: a CSS selector.\n@@ -238,40 +285,52 @@ def match(self, select, namespaces=None, flags=0, **kwargs):\n             parsing the document.\n \n         :param flags: Flags to be passed into Soup Sieve's\n-            soupsieve.match() method.\n+            `soupsieve.match()\n+            <https://facelessuser.github.io/soupsieve/api/#soupsievematch>`_\n+            method.\n \n         :param kwargs: Keyword arguments to be passed into SoupSieve's\n-            soupsieve.match() method.\n-\n-        :return: True if this Tag matches the selector; False otherwise.\n-        :rtype: bool\n+            `soupsieve.match()\n+            <https://facelessuser.github.io/soupsieve/api/#soupsievematch>`_\n+            method.\n         \"\"\"\n-        return self.api.match(\n-            select, self.tag, self._ns(namespaces, select), flags, **kwargs\n+        return cast(\n+            bool,\n+            self.api.match(\n+                select, self.tag, self._ns(namespaces, select), flags, **kwargs\n+            ),\n         )\n \n-    def filter(self, select, namespaces=None, flags=0, **kwargs):\n-        \"\"\"Filter this Tag's direct children based on the given CSS selector.\n+    def filter(\n+        self,\n+        select: str,\n+        namespaces: Optional[_NamespaceMapping] = None,\n+        flags: int = 0,\n+        **kwargs: Any,\n+    ) -> ResultSet[element.Tag]:\n+        \"\"\"Filter this `element.Tag`'s direct children based on the given CSS selector.\n \n         This uses the Soup Sieve library. It works the same way as\n-        passing this Tag into that library's soupsieve.filter()\n-        method. More information, for more information see the\n-        documentation for soupsieve.filter().\n+        passing a `element.Tag` into that library's `soupsieve.filter()\n+        <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_\n+        method. For more information, see the documentation for\n+        `soupsieve.filter()\n+        <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_.\n \n         :param namespaces: A dictionary mapping namespace prefixes\n             used in the CSS selector to namespace URIs. By default,\n             Beautiful Soup will pass in the prefixes it encountered while\n             parsing the document.\n \n         :param flags: Flags to be passed into Soup Sieve's\n-            soupsieve.filter() method.\n+            `soupsieve.filter()\n+            <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_\n+            method.\n \n         :param kwargs: Keyword arguments to be passed into SoupSieve's\n-            soupsieve.filter() method.\n-\n-        :return: A ResultSet of Tag objects.\n-        :rtype: bs4.element.ResultSet\n-\n+            `soupsieve.filter()\n+            <https://facelessuser.github.io/soupsieve/api/#soupsievefilter>`_\n+            method.\n         \"\"\"\n         return self._rs(\n             self.api.filter("}, {"sha": "52f0babd3fd07990fb4febd1041898a9f0ac1fc7", "filename": "lib/bs4/dammit.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fdammit.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fdammit.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fdammit.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "4d11239c472c4d9cfc55abf985df15591a39048c", "filename": "lib/bs4/diagnose.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fdiagnose.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fdiagnose.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fdiagnose.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "dd07d8ebcad653106cc1dc247740758525556023", "filename": "lib/bs4/element.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Felement.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Felement.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Felement.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "1d1a8fb29c6e5c141cd282a8a723133b6f764be6", "filename": "lib/bs4/exceptions.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fexceptions.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fexceptions.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fexceptions.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "8dbfa0034301dd04a3a9123b3e145d8ba05279f7", "filename": "lib/bs4/filter.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Ffilter.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Ffilter.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ffilter.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "4fdfbf012b09033182801a1a97d81fb28ca2de25", "filename": "lib/bs4/formatter.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fformatter.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fformatter.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fformatter.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", "filename": "lib/bs4/py.typed", "status": "renamed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fpy.typed", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fbs4%2Fpy.typed", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Fpy.typed?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "previous_filename": "lib/inflect/py.typed"}, {"sha": "325affefcd90af8d5515991e0e33bf665aa51b51", "filename": "lib/bs4/tests/__init__.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2F__init__.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "4828f8a4238386304bd8a9c74a0ac83cba211e70", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-4670634698080256.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-4670634698080256.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-4670634698080256.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-4670634698080256.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "b34be8b1039279793b331acbbac9a031b7aa358b", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-4818336571064320.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-4818336571064320.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-4818336571064320.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-4818336571064320.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "dbeed3f57aba4e459e3f8d8036db72ad223e1845", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-4999465949331456.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-4999465949331456.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-4999465949331456.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-4999465949331456.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "8a585ce9270e58b125922297f2d099298adac822", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-5000587759190016.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5000587759190016.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5000587759190016.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5000587759190016.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "0fe66dd23fe94a8e451dfb1a84da38ed0984c1f9", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-5167584867909632.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5167584867909632.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5167584867909632.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5167584867909632.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "fd411427d765e9b17e1e6a015485a70632bb9554", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-5270998950477824.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5270998950477824.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5270998950477824.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5270998950477824.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "6248b2c54f4f80460c55c03302f6afd6ace6b0b3", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-5375146639360000.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5375146639360000.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5375146639360000.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5375146639360000.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "107da539fc15bd1664e35287ace9feda21cb5820", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-5492400320282624.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5492400320282624.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5492400320282624.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5492400320282624.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "367106c76f6408cd57cd51e622cf01a9903e5f34", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-5703933063462912.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5703933063462912.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5703933063462912.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5703933063462912.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "b8536ef0387195baad9fe9c0b29f26f7db569c2f", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-5843991618256896.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5843991618256896.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5843991618256896.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5843991618256896.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "d8b549c55aa2fa4988dc1097054c8a995542a271", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-5984173902397440.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5984173902397440.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5984173902397440.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-5984173902397440.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "123e56d4d0b75ccd43b59fccd09df12e29d916d9", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-6124268085182464.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6124268085182464.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6124268085182464.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6124268085182464.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "2831c484e5f5e6d2d2ab98fa7c77230a9d865450", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-6241471367348224.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6241471367348224.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6241471367348224.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6241471367348224.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "b60a250cad41140e10d80d30a40c892fb3c0ef53", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-6306874195312640.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6306874195312640.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6306874195312640.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6306874195312640.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "a823d557b4bebed8a9cdb880d281c9de69a1aa0a", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-6450958476902400.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6450958476902400.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6450958476902400.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6450958476902400.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "65af44d8c1bdb043fa6810902e1ce623533bd34a", "filename": "lib/bs4/tests/fuzz/clusterfuzz-testcase-minimized-bs4_fuzzer-6600557255327744.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6600557255327744.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6600557255327744.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fclusterfuzz-testcase-minimized-bs4_fuzzer-6600557255327744.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "5559adbb352524a8992fcae8243952cb82d01d27", "filename": "lib/bs4/tests/fuzz/crash-0d306a50c8ed8bcd0785b67000fcd5dea1d33f08.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fcrash-0d306a50c8ed8bcd0785b67000fcd5dea1d33f08.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fcrash-0d306a50c8ed8bcd0785b67000fcd5dea1d33f08.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fcrash-0d306a50c8ed8bcd0785b67000fcd5dea1d33f08.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "885711554056588a381fd9a251d48ce76a916a59", "filename": "lib/bs4/tests/fuzz/crash-ffbdfa8a2b26f13537b68d3794b0478a4090ee4a.testcase", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fcrash-ffbdfa8a2b26f13537b68d3794b0478a4090ee4a.testcase", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ffuzz%2Fcrash-ffbdfa8a2b26f13537b68d3794b0478a4090ee4a.testcase", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ffuzz%2Fcrash-ffbdfa8a2b26f13537b68d3794b0478a4090ee4a.testcase?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "75370712a8c38162b167b1ee358e4adc75c6fff2", "filename": "lib/bs4/tests/test_builder.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_builder.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_builder.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_builder.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "9327174f3775a763f5379eeaafddbd6cf1c69c52", "filename": "lib/bs4/tests/test_builder_registry.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_builder_registry.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_builder_registry.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_builder_registry.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "359dbcd246168d3e81968341cf3412077ccbd3b7", "filename": "lib/bs4/tests/test_css.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_css.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_css.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_css.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "9aad0ac695a301b6eec081bc15c2a8e29edb891c", "filename": "lib/bs4/tests/test_dammit.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_dammit.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_dammit.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_dammit.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "0194d697367edde31c9ce13a0d5e43904d14c2a7", "filename": "lib/bs4/tests/test_docs.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_docs.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_docs.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_docs.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "6d08ab5d063cde52c26d805aed64993c6cfd7995", "filename": "lib/bs4/tests/test_element.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_element.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_element.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_element.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "528b16d5b5b9ca6552254ee19f66828f78a86946", "filename": "lib/bs4/tests/test_formatter.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_formatter.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_formatter.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_formatter.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "f29802d4203068b89e6fff5e8bbe7d40f81bf4f9", "filename": "lib/bs4/tests/test_fuzz.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_fuzz.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_fuzz.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_fuzz.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "4197720f3bca31cfe6cee9edd17d48edc603657a", "filename": "lib/bs4/tests/test_html5lib.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_html5lib.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_html5lib.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_html5lib.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "a1195d815f6903751af5139986bf7d1c2880d9e1", "filename": "lib/bs4/tests/test_htmlparser.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_htmlparser.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_htmlparser.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_htmlparser.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "5065b6fc835e019fcb7cd4e60a3a36d267f34235", "filename": "lib/bs4/tests/test_lxml.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_lxml.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_lxml.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_lxml.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "e4c179e9d8869474eeadbf8221ffc325af62ffc4", "filename": "lib/bs4/tests/test_navigablestring.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_navigablestring.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_navigablestring.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_navigablestring.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "24f9385de243f853f5de8ea456976d6e2dbef80f", "filename": "lib/bs4/tests/test_pageelement.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_pageelement.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_pageelement.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_pageelement.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "28013b87bbffd8896f8986e7ac261806e53a858e", "filename": "lib/bs4/tests/test_soup.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_soup.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_soup.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_soup.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "c48f334567962c0260d467887a6de33d2d301141", "filename": "lib/bs4/tests/test_tag.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_tag.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_tag.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_tag.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "26995f95a282ab72d7cf9bf7d8f05adca86ad68c", "filename": "lib/bs4/tests/test_tree.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_tree.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fbs4%2Ftests%2Ftest_tree.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fbs4%2Ftests%2Ftest_tree.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "16c0c7c268f3d28336595254df687d2f5f48b574", "filename": "lib/certifi/__init__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcertifi%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcertifi%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcertifi%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "5ec1afe02d4d859082d132cf74f4c56954e3d6be", "filename": "lib/certifi/cacert.pem", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcertifi%2Fcacert.pem", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcertifi%2Fcacert.pem", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcertifi%2Fcacert.pem?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "1c9661cc7c2f6917c2506a30b2710002f81ab23a", "filename": "lib/certifi/core.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcertifi%2Fcore.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcertifi%2Fcore.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcertifi%2Fcore.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "0d3a37990145e94ad85406166dbaf52f4c311e5e", "filename": "lib/charset_normalizer/__init__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "e0e76f7bfbb411d4424d3a1834b0ea803d80ea7e", "filename": "lib/charset_normalizer/__main__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2F__main__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2F__main__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2F__main__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "1f32091f229ea698da564c1a40e4f14dec440323", "filename": "lib/charset_normalizer/api.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fapi.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fapi.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Fapi.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "9545d35d1ec3f0c170dc6243325955db3856033b", "filename": "lib/charset_normalizer/cd.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fcd.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fcd.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Fcd.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "543a5a4de49d07690e73df778aa580589d0789c6", "filename": "lib/charset_normalizer/cli/__init__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fcli%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fcli%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Fcli%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "ad843c1d0f2b8df7c467724a20f6457c68e49261", "filename": "lib/charset_normalizer/cli/__main__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fcli%2F__main__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fcli%2F__main__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Fcli%2F__main__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "6ed7795675050fb80ebb33dbe503b323a3a1912b", "filename": "lib/charset_normalizer/constant.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fconstant.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fconstant.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Fconstant.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "293c1efaf045559ea088e7991b318571a4ff71a8", "filename": "lib/charset_normalizer/legacy.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Flegacy.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Flegacy.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Flegacy.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "b41d9cfc575d5ea2dd0dab5b0e4350b8485bd70b", "filename": "lib/charset_normalizer/md.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fmd.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fmd.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Fmd.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "30e8a163e2ef7b801a2ce013f0aee41e75701783", "filename": "lib/charset_normalizer/models.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fmodels.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fmodels.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Fmodels.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "0f529b59cfdf6f2b4f6aca39b439b2e35dd93d00", "filename": "lib/charset_normalizer/utils.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Futils.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Futils.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Futils.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "a80346fc7ef899107a55de8550da98085fb4b122", "filename": "lib/charset_normalizer/version.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fversion.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcharset_normalizer%2Fversion.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcharset_normalizer%2Fversion.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "4ae1d9ae11ca9e32d818deb69c593ced8f2845b2", "filename": "lib/cheroot/__init__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "c5bb97ee0b2821b05901e323916d48d796526627", "filename": "lib/cheroot/__main__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2F__main__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2F__main__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2F__main__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "8b36b8870015212b5aec8b5e4550040aaa61f77d", "filename": "lib/cheroot/_compat.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2F_compat.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2F_compat.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2F_compat.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "82891113fc93b4e0ab829c9edce7b928ae8f79e7", "filename": "lib/cheroot/_compat.pyi", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2F_compat.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2F_compat.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2F_compat.pyi?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "f1d2b1204de6ade37ddb9fbb68e5cf1f2f02b6d6", "filename": "lib/cheroot/cli.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fcli.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fcli.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fcli.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "04df0d42a386b0fcd1038b7bf93ff26cfee0ecc4", "filename": "lib/cheroot/connections.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fconnections.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fconnections.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fconnections.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "794903736b391cd7210779535f25856477d13643", "filename": "lib/cheroot/connections.pyi", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fconnections.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fconnections.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fconnections.pyi?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "a1103595c22a35297a0795f19aec5146188eee45", "filename": "lib/cheroot/errors.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ferrors.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ferrors.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ferrors.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "f5780a1ede899619f3def8d82c34ddff066512da", "filename": "lib/cheroot/makefile.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fmakefile.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fmakefile.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fmakefile.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "9a288539c723fbdc1972b5772e1a45ff165db639", "filename": "lib/cheroot/server.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fserver.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fserver.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fserver.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "c5c0f517f60de424cab982614bec6c2ad84136fc", "filename": "lib/cheroot/server.pyi", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fserver.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fserver.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fserver.pyi?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "c0072c0557ffe885769828d95606b24973253828", "filename": "lib/cheroot/ssl/__init__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fssl%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "c5951215468d330b7ec32da684beb26105a796f5", "filename": "lib/cheroot/ssl/__init__.pyi", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2F__init__.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2F__init__.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fssl%2F__init__.pyi?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "ed747ab6e3bbe62683227c32131f1f6e5603987b", "filename": "lib/cheroot/ssl/builtin.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2Fbuiltin.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2Fbuiltin.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fssl%2Fbuiltin.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "b05aaf5ad73e2e2062a8a8398b0561dede6f24d9", "filename": "lib/cheroot/ssl/builtin.pyi", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2Fbuiltin.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2Fbuiltin.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fssl%2Fbuiltin.pyi?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "8a041a79b849fc82975c2b333e14d34965637d9f", "filename": "lib/cheroot/ssl/pyopenssl.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2Fpyopenssl.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2Fpyopenssl.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fssl%2Fpyopenssl.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "59dae05ae7bdbe5b762a50d37d7eb85355c69cf1", "filename": "lib/cheroot/ssl/pyopenssl.pyi", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2Fpyopenssl.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fssl%2Fpyopenssl.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fssl%2Fpyopenssl.pyi?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "51d22a0a05763d1921f070bbb3f12fe0f78096c4", "filename": "lib/cheroot/test/_pytest_plugin.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2F_pytest_plugin.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2F_pytest_plugin.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2F_pytest_plugin.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "657997c3f269aa6e92159b97e83c9c06dc46b269", "filename": "lib/cheroot/test/conftest.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Fconftest.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Fconftest.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Fconftest.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "76342657dc5ef988b817805a3f02281f657636a5", "filename": "lib/cheroot/test/helper.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Fhelper.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Fhelper.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Fhelper.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "67aca43ef27b86c9814ef738426a6deedb46e59e", "filename": "lib/cheroot/test/test__compat.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest__compat.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest__compat.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest__compat.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "372c86aaa1eae64fe1366b5d08d94f0897023b81", "filename": "lib/cheroot/test/test_cli.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_cli.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_cli.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest_cli.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "ff819b2aa90edef69a2c2df0c46c8dc2031c3554", "filename": "lib/cheroot/test/test_conn.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_conn.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_conn.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest_conn.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "cd3841d4284ff204c4836d769e11e9029dd4bb87", "filename": "lib/cheroot/test/test_core.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_core.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_core.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest_core.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "99cc2208ec7af9b680e2e71ed5ef69092ff5de8d", "filename": "lib/cheroot/test/test_dispatch.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_dispatch.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_dispatch.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest_dispatch.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "71940dcbc50be1bf9ff57bb640cbe8bcd8b547a9", "filename": "lib/cheroot/test/test_errors.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_errors.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_errors.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest_errors.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "d65d4ea268311f1c61141c447e522ec6175171a9", "filename": "lib/cheroot/test/test_makefile.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_makefile.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_makefile.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest_makefile.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "ae6e390a44e8d9a831651a8bf19213d32a771d39", "filename": "lib/cheroot/test/test_server.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_server.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_server.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest_server.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "115237f4f70f1168df370d69d2dae298208da1d2", "filename": "lib/cheroot/test/test_ssl.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_ssl.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_ssl.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest_ssl.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "0612d2e54307381c379c6f8a7208d10ab5ee7325", "filename": "lib/cheroot/test/test_wsgi.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_wsgi.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Ftest_wsgi.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Ftest_wsgi.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "e7840516ff2d8cc506a501864502c2cba31e3cf2", "filename": "lib/cheroot/test/threadleakcheck.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Fthreadleakcheck.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Fthreadleakcheck.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Fthreadleakcheck.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "fbdd86dc6d972be8f3adf2e8f8d86b0361dc132b", "filename": "lib/cheroot/test/webtest.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Fwebtest.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftest%2Fwebtest.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftest%2Fwebtest.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "12e5599fc78557f921be97945f4b2ccff194af50", "filename": "lib/cheroot/testing.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftesting.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Ftesting.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Ftesting.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "269574514c8e2c708affa3cec18c5b67f99cbbc0", "filename": "lib/cheroot/workers/threadpool.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fworkers%2Fthreadpool.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fworkers%2Fthreadpool.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fworkers%2Fthreadpool.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "856f8a0cef24a9a4361d154abe674b22a1eb8040", "filename": "lib/cheroot/workers/threadpool.pyi", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fworkers%2Fthreadpool.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fworkers%2Fthreadpool.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fworkers%2Fthreadpool.pyi?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "4ac4b16764126e2329b09440c41baa02abddcab3", "filename": "lib/cheroot/wsgi.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fwsgi.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fwsgi.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fwsgi.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "acbd54e3b7894f8d7c24b5b677f26dfaa336ab42", "filename": "lib/cheroot/wsgi.pyi", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fwsgi.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcheroot%2Fwsgi.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcheroot%2Fwsgi.pyi?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "5400345132ad011aa9dd5107ef6700b8d0feab48", "filename": "lib/cherrypy_cors/__init__.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcherrypy_cors%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcherrypy_cors%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcherrypy_cors%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "1aa547c57af68954b3bcd6012873881030e53d91", "filename": "lib/click/__init__.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "f2726b93afe2ed69ba20d8e709c9cbd22b9d2339", "filename": "lib/click/_compat.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_compat.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_compat.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2F_compat.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "ee8225c4c29344e6c672800f97f65322d245e166", "filename": "lib/click/_termui_impl.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_termui_impl.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_termui_impl.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2F_termui_impl.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "97fbee3dc6ce372600e702d38d9139bcf6a40bec", "filename": "lib/click/_textwrap.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_textwrap.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_textwrap.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2F_textwrap.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "09fb00855e68840d18187f3de3a604e7defc37e8", "filename": "lib/click/_utils.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_utils.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_utils.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2F_utils.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "e56c7c6ae7ee7e577db6ace659cd58c0c652074c", "filename": "lib/click/_winconsole.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_winconsole.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2F_winconsole.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2F_winconsole.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "57f549c7936beb8111d40bc90edd2ac74cacdd8d", "filename": "lib/click/core.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fcore.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fcore.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Fcore.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "21f4c342245fa93a7946b17d7661e0d2fa6a0c29", "filename": "lib/click/decorators.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fdecorators.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fdecorators.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Fdecorators.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "4d782ee361442e4aafeac7e371ab12789c8a3751", "filename": "lib/click/exceptions.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fexceptions.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fexceptions.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Fexceptions.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "0b64f831b5578e7262302ec9218fa498a2336794", "filename": "lib/click/formatting.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fformatting.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fformatting.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Fformatting.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "a2f91723d21cefdf658be327174e1f8ccdc20fcd", "filename": "lib/click/globals.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fglobals.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fglobals.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Fglobals.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "1ea1f7166e4b9eac5bcb632ecd86fe0331829141", "filename": "lib/click/parser.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fparser.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fparser.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Fparser.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", "filename": "lib/click/py.typed", "status": "renamed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fpy.typed", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fpy.typed", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Fpy.typed?ref=5610c167a17b0898d93d0588a0df81c03306ac52", "previous_filename": "lib/packaging/py.typed"}, {"sha": "8f1564c49be8e138014841d8f7cb35a34265674f", "filename": "lib/click/shell_completion.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fshell_completion.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Fshell_completion.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Fshell_completion.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "2e98a0771ca410a7a2ed83199cf401e75cc6d41f", "filename": "lib/click/termui.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Ftermui.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Ftermui.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Ftermui.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "f6f60b809a065e2d67de5a7ec13d9d3c51b5e760", "filename": "lib/click/testing.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Ftesting.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Ftesting.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Ftesting.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "e71c1c21e4c2e1f954ceeed228507478f138993a", "filename": "lib/click/types.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Ftypes.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Ftypes.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Ftypes.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "beae26f761607f0a7a617d4ce8789bf49bb99b6b", "filename": "lib/click/utils.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Futils.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fclick%2Futils.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fclick%2Futils.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "756e9750d34dbdf96a28a62de58751808bbbcdc4", "filename": "lib/cloudinary/__init__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcloudinary%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "b905e45b7dee01f50f76c78d1ccf0008d637e2b2", "filename": "lib/cloudinary/api.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fapi.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fapi.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcloudinary%2Fapi.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "0e93b33cd68fcaeb69f84f3e2f312e244105dbc9", "filename": "lib/cloudinary/api_client/call_api.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fapi_client%2Fcall_api.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fapi_client%2Fcall_api.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcloudinary%2Fapi_client%2Fcall_api.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "84c155d87ec5db697baaff05537542f8467529ce", "filename": "lib/cloudinary/api_client/execute_request.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fapi_client%2Fexecute_request.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fapi_client%2Fexecute_request.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcloudinary%2Fapi_client%2Fexecute_request.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "417c9901c34fd65fb4e87c89af00ce26642919da", "filename": "lib/cloudinary/auth_token.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fauth_token.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fauth_token.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcloudinary%2Fauth_token.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "49aa65b4b1a0e58aabdc123568ee754532803415", "filename": "lib/cloudinary/search.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fsearch.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fsearch.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcloudinary%2Fsearch.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "57efe5f66d7ce97c4c2636be33d5766e44743596", "filename": "lib/cloudinary/uploader.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fuploader.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Fuploader.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcloudinary%2Fuploader.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "dd895370469324ae5a2f1ae2d7dfddef3c0b45c4", "filename": "lib/cloudinary/utils.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Futils.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcloudinary%2Futils.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcloudinary%2Futils.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "383101cdb38706c305449674044e9288b92b7d75", "filename": "lib/colorama/__init__.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "11ec695ff79627463a0282d25079527562de9e42", "filename": "lib/colorama/ansi.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Fansi.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Fansi.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Fansi.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "abf209e60c7c4a9b1ae57452e36b383969848c2e", "filename": "lib/colorama/ansitowin32.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Fansitowin32.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Fansitowin32.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Fansitowin32.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "d5fd4b71fed1bb4871717f978f0c470280f099c1", "filename": "lib/colorama/initialise.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Finitialise.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Finitialise.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Finitialise.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "8c5661e93a205bf4fb22404d4fc50f902cc31369", "filename": "lib/colorama/tests/__init__.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Ftests%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "0a20c80f882066e0e1323b0c7f61e22913c32e35", "filename": "lib/colorama/tests/ansi_test.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Fansi_test.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Fansi_test.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Ftests%2Fansi_test.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "91ca551f97b4576c680711e826a1855fb944c872", "filename": "lib/colorama/tests/ansitowin32_test.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Fansitowin32_test.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Fansitowin32_test.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Ftests%2Fansitowin32_test.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "89f9b07511c8fee74686d9cc434bf66345a46d6d", "filename": "lib/colorama/tests/initialise_test.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Finitialise_test.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Finitialise_test.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Ftests%2Finitialise_test.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "0f84e4befe550d4386d24264648abf1323e682ff", "filename": "lib/colorama/tests/isatty_test.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Fisatty_test.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Fisatty_test.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Ftests%2Fisatty_test.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "472fafb4403efb9673d5cc724dafd9cf764aac5b", "filename": "lib/colorama/tests/utils.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Futils.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Futils.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Ftests%2Futils.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "d0955f9e608377940f0d548576964f2fcf3caf48", "filename": "lib/colorama/tests/winterm_test.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Fwinterm_test.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Ftests%2Fwinterm_test.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Ftests%2Fwinterm_test.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "841b0e270a381cdfaca544a9be976d7276d83b1e", "filename": "lib/colorama/win32.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Fwin32.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Fwin32.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Fwin32.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "aad867e8c80b826bf6a060116f17fa08a8eb0765", "filename": "lib/colorama/winterm.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Fwinterm.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fcolorama%2Fwinterm.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fcolorama%2Fwinterm.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "5f2be1e4ef5be67b8c0ebfa95d1506ae0c0276be", "filename": "lib/dateutil-stubs/METADATA.toml", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2FMETADATA.toml", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2FMETADATA.toml", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2FMETADATA.toml?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "75bf74c526e3ffc119a7eb20623bacaefdb88571", "filename": "lib/dateutil-stubs/_common.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2F_common.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2F_common.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2F_common.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "952d119142e5caafccd4b150fac9df6851324c62", "filename": "lib/dateutil-stubs/easter.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Feaster.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Feaster.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Feaster.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "c375ff1580ac4c4018e46f4cc571087aa5c60dbc", "filename": "lib/dateutil-stubs/parser/__init__.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fparser%2F__init__.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fparser%2F__init__.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Fparser%2F__init__.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "fc5e46d97e50d75b005b77e38ed636c7f931af51", "filename": "lib/dateutil-stubs/parser/isoparser.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fparser%2Fisoparser.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fparser%2Fisoparser.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Fparser%2Fisoparser.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "b648ac92333301658c4f49b315f21130c689c819", "filename": "lib/dateutil-stubs/py.typed", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fpy.typed", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fpy.typed", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Fpy.typed?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "394713db0e4e42e6634c731d0dbe45bb08c2e4a4", "filename": "lib/dateutil-stubs/relativedelta.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Frelativedelta.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Frelativedelta.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Frelativedelta.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "f091cfbd6e77cdbfbbb4a7c100acacea19219aa2", "filename": "lib/dateutil-stubs/rrule.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Frrule.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Frrule.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Frrule.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "334ca4825094f1bb181fcea9a37d40cf5206d3b8", "filename": "lib/dateutil-stubs/tz/__init__.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Ftz%2F__init__.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Ftz%2F__init__.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Ftz%2F__init__.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "4e64822fa09c4adab485b68f7948e6b0efad3b6b", "filename": "lib/dateutil-stubs/tz/_common.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Ftz%2F_common.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Ftz%2F_common.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Ftz%2F_common.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "6addba659e0233a69cffdad070fea713dbb6d576", "filename": "lib/dateutil-stubs/tz/tz.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Ftz%2Ftz.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Ftz%2Ftz.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Ftz%2Ftz.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "8c9f5e44a294a5addf4bbc0d627ddd7d2cacf9a7", "filename": "lib/dateutil-stubs/utils.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Futils.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Futils.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Futils.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "7bd2845a21e5e5847fc82ac3488f1f41b8b6e06f", "filename": "lib/dateutil-stubs/zoneinfo/__init__.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fzoneinfo%2F__init__.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fzoneinfo%2F__init__.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Fzoneinfo%2F__init__.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "e38459267d91ed1cdb92b0aef7c7e1a900021890", "filename": "lib/dateutil-stubs/zoneinfo/rebuild.pyi", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fzoneinfo%2Frebuild.pyi", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdateutil-stubs%2Fzoneinfo%2Frebuild.pyi", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdateutil-stubs%2Fzoneinfo%2Frebuild.pyi?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "5ba765f151349d1feb897aaa95983119a4a1400c", "filename": "lib/defusedxml/ElementTree.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2FElementTree.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2FElementTree.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2FElementTree.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "4b5a23009c7f2b29f2bb3766a2143b92b577e728", "filename": "lib/defusedxml/__init__.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "84670c68c0ab70e79d11d2b354ac58ee4b53eb27", "filename": "lib/defusedxml/cElementTree.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2FcElementTree.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2FcElementTree.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2FcElementTree.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "5ceda1fb22f2d159ac8b1b1fd458e4c5bb7d74a6", "filename": "lib/defusedxml/common.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fcommon.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fcommon.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2Fcommon.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "7bfc57e4f9971426f5e41974cd52940bfc4b1028", "filename": "lib/defusedxml/expatbuilder.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fexpatbuilder.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fexpatbuilder.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2Fexpatbuilder.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "890e1d167e9c4e4bf2d08802c518b27f51c21132", "filename": "lib/defusedxml/expatreader.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fexpatreader.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fexpatreader.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2Fexpatreader.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "99d5be93f008cbbe3892d42ecb3840dced65e047", "filename": "lib/defusedxml/lxml.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Flxml.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Flxml.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2Flxml.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "78033b6cebbcb3bdeedd558f44d0e56fc12a722c", "filename": "lib/defusedxml/minidom.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fminidom.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fminidom.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2Fminidom.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "e3b10a467599f09291b22db733115bd0adcbc87e", "filename": "lib/defusedxml/pulldom.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fpulldom.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fpulldom.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2Fpulldom.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "b2786f74f65e26b759ab6ce364faf1a6a2c25dc0", "filename": "lib/defusedxml/sax.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fsax.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fsax.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2Fsax.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "fbc674da44e6d7edebc4c15d90bcf9809680be6a", "filename": "lib/defusedxml/xmlrpc.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fxmlrpc.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdefusedxml%2Fxmlrpc.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdefusedxml%2Fxmlrpc.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "7892741347d632d48f3fbe11b417c4705f9968f3", "filename": "lib/distro.py", "status": "removed", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdistro.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8/lib%2Fdistro.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdistro.py?ref=a96fd23d72f5aa3042d6f9a55d8051653ff6f0b8"}, {"sha": "d30fd742a227e80f984eac729d441d265612867e", "filename": "lib/dns/__init__.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F__init__.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F__init__.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2F__init__.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "23455db37403f201c22bea60e59afb0438d59fdb", "filename": "lib/dns/_asyncbackend.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_asyncbackend.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_asyncbackend.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2F_asyncbackend.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "303908cebf6e637307f3d1e04d233f24db6bc0e1", "filename": "lib/dns/_asyncio_backend.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_asyncio_backend.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_asyncio_backend.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2F_asyncio_backend.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "65a9a2a35a23395b6fbb34a2e47ee9405280019c", "filename": "lib/dns/_features.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_features.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_features.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2F_features.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "b3d72deef2a60f0ffcfd66688388816a08893558", "filename": "lib/dns/_immutable_ctx.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_immutable_ctx.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_immutable_ctx.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2F_immutable_ctx.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "edb452de1067441216afcc2b772eb0d5edc69485", "filename": "lib/dns/_no_ssl.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_no_ssl.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_no_ssl.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2F_no_ssl.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "10ddf727921bfa1b0dad9cfd810d0e9b54690522", "filename": "lib/dns/_tls_util.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_tls_util.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_tls_util.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2F_tls_util.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "bde7e8bab226942aa23681824579eef29d8bb069", "filename": "lib/dns/_trio_backend.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_trio_backend.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2F_trio_backend.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2F_trio_backend.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "bb7704582ad277d502cd1f042107e8ce35ec0439", "filename": "lib/dns/asyncquery.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fasyncquery.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fasyncquery.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2Fasyncquery.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "6f8c69fd3fe6a5fbbf9f9060a0f0459bb477ace5", "filename": "lib/dns/asyncresolver.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fasyncresolver.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fasyncresolver.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2Fasyncresolver.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "12da9f56e9a5130f5030e8a54464a3fc7f9f904b", "filename": "lib/dns/btree.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fbtree.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fbtree.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2Fbtree.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "27b5bb6febdb1472d211d36a551d6043748fde91", "filename": "lib/dns/btreezone.py", "status": "added", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fbtreezone.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fbtreezone.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2Fbtreezone.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}, {"sha": "0b2aa709791a786db1778cecb4b4a5ced56559d2", "filename": "lib/dns/dnssec.py", "status": "modified", "additions": 0, "deletions": 0, "changes": 0, "blob_url": "https://github.com/Tautulli/Tautulli/blob/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fdnssec.py", "raw_url": "https://github.com/Tautulli/Tautulli/raw/5610c167a17b0898d93d0588a0df81c03306ac52/lib%2Fdns%2Fdnssec.py", "contents_url": "https://api.github.com/repos/Tautulli/Tautulli/contents/lib%2Fdns%2Fdnssec.py?ref=5610c167a17b0898d93d0588a0df81c03306ac52"}]}, "_cache_time": 1776938182, "_release_version": "v2.15.1"}