Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c7dc03d06 | ||
|
|
f511049eaa | ||
|
|
76118ba196 | ||
|
|
83b10ab2f3 | ||
|
|
604c52e608 | ||
|
|
8949599232 | ||
|
|
c9c235061c | ||
|
|
328889689f |
2
Pipfile
2
Pipfile
@@ -4,7 +4,7 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
httpx = {extras = ["http2"], version = "0.16.1"}
|
||||
httpx = {extras = ["http2"]}
|
||||
|
||||
[dev-packages]
|
||||
pytest-mock = "*"
|
||||
|
||||
258
Pipfile.lock
generated
258
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "e1eb34f14c75998519a90838b283ccd23bd168afa8e4837f956c5c4df66376f9"
|
||||
"sha256": "74b83f2e50bc16f8d90c06ddc775d24ee427f8481a2501f62170bf5b76a2f1bd"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
@@ -14,6 +14,14 @@
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"anyio": {
|
||||
"hashes": [
|
||||
"sha256:929a6852074397afe1d989002aa96d457e3e1e5441357c60d03e7eea0e65e1b0",
|
||||
"sha256:ae57a67583e5ff8b4af47666ff5651c3732d45fd26c929253748e796af860374"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.2'",
|
||||
"version": "==3.3.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
|
||||
@@ -26,6 +34,7 @@
|
||||
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
|
||||
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.12.0"
|
||||
},
|
||||
"h2": {
|
||||
@@ -44,21 +53,22 @@
|
||||
},
|
||||
"httpcore": {
|
||||
"hashes": [
|
||||
"sha256:37ae835fb370049b2030c3290e12ed298bf1473c41bb72ca4aa78681eba9b7c9",
|
||||
"sha256:93e822cd16c32016b414b789aeff4e855d0ccbfc51df563ee34d4dbadbb3bcdc"
|
||||
"sha256:b0d16f0012ec88d8cc848f5a55f8a03158405f4bca02ee49bc4ca2c1fda49f3e",
|
||||
"sha256:db4c0dcb8323494d01b8c6d812d80091a31e520033e7b0120883d6f52da649ff"
|
||||
],
|
||||
"version": "==0.12.3"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.13.6"
|
||||
},
|
||||
"httpx": {
|
||||
"extras": [
|
||||
"http2"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:126424c279c842738805974687e0518a94c7ae8d140cd65b9c4f77ac46ffa537",
|
||||
"sha256:9cffb8ba31fac6536f2c8cde30df859013f59e4bcc5b8d43901cb3654a8e0a5b"
|
||||
"sha256:979afafecb7d22a1d10340bafb403cf2cb75aff214426ff206521fc79d26408c",
|
||||
"sha256:9f99c15d33642d38bce8405df088c1c4cfd940284b4290cacbfb02e64f4877c6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.16.1"
|
||||
"version": "==0.18.2"
|
||||
},
|
||||
"hyperframe": {
|
||||
"hashes": [
|
||||
@@ -89,23 +99,42 @@
|
||||
"sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663",
|
||||
"sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.2.0"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"anyio": {
|
||||
"hashes": [
|
||||
"sha256:929a6852074397afe1d989002aa96d457e3e1e5441357c60d03e7eea0e65e1b0",
|
||||
"sha256:ae57a67583e5ff8b4af47666ff5651c3732d45fd26c929253748e796af860374"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.2'",
|
||||
"version": "==3.3.0"
|
||||
},
|
||||
"atomicwrites": {
|
||||
"hashes": [
|
||||
"sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197",
|
||||
"sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"
|
||||
],
|
||||
"markers": "sys_platform == 'win32'",
|
||||
"version": "==1.4.0"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
|
||||
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==21.2.0"
|
||||
},
|
||||
"bleach": {
|
||||
"hashes": [
|
||||
"sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125",
|
||||
"sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"
|
||||
"sha256:306483a5a9795474160ad57fce3ddd1b50551e981eed8e15a582d34cef28aafa",
|
||||
"sha256:ae976d7174bba988c0b632def82fdc94235756edfb14e6558a9c5be555c9fb78"
|
||||
],
|
||||
"version": "==3.3.0"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==3.3.1"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
@@ -114,96 +143,28 @@
|
||||
],
|
||||
"version": "==2021.5.30"
|
||||
},
|
||||
"cffi": {
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813",
|
||||
"sha256:04c468b622ed31d408fea2346bec5bbffba2cc44226302a0de1ade9f5ea3d373",
|
||||
"sha256:06d7cd1abac2ffd92e65c0609661866709b4b2d82dd15f611e602b9b188b0b69",
|
||||
"sha256:06db6321b7a68b2bd6df96d08a5adadc1fa0e8f419226e25b2a5fbf6ccc7350f",
|
||||
"sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06",
|
||||
"sha256:0f861a89e0043afec2a51fd177a567005847973be86f709bbb044d7f42fc4e05",
|
||||
"sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea",
|
||||
"sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee",
|
||||
"sha256:1bf1ac1984eaa7675ca8d5745a8cb87ef7abecb5592178406e55858d411eadc0",
|
||||
"sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396",
|
||||
"sha256:24a570cd11895b60829e941f2613a4f79df1a27344cbbb82164ef2e0116f09c7",
|
||||
"sha256:24ec4ff2c5c0c8f9c6b87d5bb53555bf267e1e6f70e52e5a9740d32861d36b6f",
|
||||
"sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73",
|
||||
"sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315",
|
||||
"sha256:293e7ea41280cb28c6fcaaa0b1aa1f533b8ce060b9e701d78511e1e6c4a1de76",
|
||||
"sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1",
|
||||
"sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49",
|
||||
"sha256:3c3f39fa737542161d8b0d680df2ec249334cd70a8f420f71c9304bd83c3cbed",
|
||||
"sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892",
|
||||
"sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482",
|
||||
"sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058",
|
||||
"sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5",
|
||||
"sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53",
|
||||
"sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045",
|
||||
"sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3",
|
||||
"sha256:681d07b0d1e3c462dd15585ef5e33cb021321588bebd910124ef4f4fb71aef55",
|
||||
"sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5",
|
||||
"sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e",
|
||||
"sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c",
|
||||
"sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369",
|
||||
"sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827",
|
||||
"sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053",
|
||||
"sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa",
|
||||
"sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4",
|
||||
"sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322",
|
||||
"sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132",
|
||||
"sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62",
|
||||
"sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa",
|
||||
"sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0",
|
||||
"sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396",
|
||||
"sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e",
|
||||
"sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991",
|
||||
"sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6",
|
||||
"sha256:cc5a8e069b9ebfa22e26d0e6b97d6f9781302fe7f4f2b8776c3e1daea35f1adc",
|
||||
"sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1",
|
||||
"sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406",
|
||||
"sha256:df5052c5d867c1ea0b311fb7c3cd28b19df469c056f7fdcfe88c7473aa63e333",
|
||||
"sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d",
|
||||
"sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"
|
||||
"sha256:88fce3fa5b1a84fdcb3f603d889f723d1dd89b26059d0123ca435570e848d5e1",
|
||||
"sha256:c46c3ace2d744cfbdebceaa3c19ae691f53ae621b39fd7570f59d14fb7f2fd12"
|
||||
],
|
||||
"version": "==1.14.5"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
|
||||
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
|
||||
],
|
||||
"version": "==4.0.0"
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==2.0.3"
|
||||
},
|
||||
"colorama": {
|
||||
"hashes": [
|
||||
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
|
||||
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
|
||||
],
|
||||
"markers": "platform_system == 'Windows' and sys_platform == 'win32'",
|
||||
"version": "==0.4.4"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d",
|
||||
"sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959",
|
||||
"sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6",
|
||||
"sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873",
|
||||
"sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2",
|
||||
"sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713",
|
||||
"sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1",
|
||||
"sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177",
|
||||
"sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250",
|
||||
"sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca",
|
||||
"sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d",
|
||||
"sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"
|
||||
],
|
||||
"version": "==3.4.7"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125",
|
||||
"sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==0.17.1"
|
||||
},
|
||||
"h11": {
|
||||
@@ -211,25 +172,27 @@
|
||||
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
|
||||
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.12.0"
|
||||
},
|
||||
"httpcore": {
|
||||
"hashes": [
|
||||
"sha256:37ae835fb370049b2030c3290e12ed298bf1473c41bb72ca4aa78681eba9b7c9",
|
||||
"sha256:93e822cd16c32016b414b789aeff4e855d0ccbfc51df563ee34d4dbadbb3bcdc"
|
||||
"sha256:b0d16f0012ec88d8cc848f5a55f8a03158405f4bca02ee49bc4ca2c1fda49f3e",
|
||||
"sha256:db4c0dcb8323494d01b8c6d812d80091a31e520033e7b0120883d6f52da649ff"
|
||||
],
|
||||
"version": "==0.12.3"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.13.6"
|
||||
},
|
||||
"httpx": {
|
||||
"extras": [
|
||||
"http2"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:126424c279c842738805974687e0518a94c7ae8d140cd65b9c4f77ac46ffa537",
|
||||
"sha256:9cffb8ba31fac6536f2c8cde30df859013f59e4bcc5b8d43901cb3654a8e0a5b"
|
||||
"sha256:979afafecb7d22a1d10340bafb403cf2cb75aff214426ff206521fc79d26408c",
|
||||
"sha256:9f99c15d33642d38bce8405df088c1c4cfd940284b4290cacbfb02e64f4877c6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.16.1"
|
||||
"version": "==0.18.2"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
@@ -240,10 +203,11 @@
|
||||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:960d52ba7c21377c990412aca380bf3642d734c2eaab78a2c39319f67c6a5786",
|
||||
"sha256:e592faad8de1bda9fe920cf41e15261e7131bcf266c30306eec00e8e225c1dd5"
|
||||
"sha256:079ada16b7fc30dfbb5d13399a5113110dab1aa7c2bc62f66af75f0b717c8cac",
|
||||
"sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e"
|
||||
],
|
||||
"version": "==4.4.0"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==4.6.1"
|
||||
},
|
||||
"iniconfig": {
|
||||
"hashes": [
|
||||
@@ -252,40 +216,35 @@
|
||||
],
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"jeepney": {
|
||||
"hashes": [
|
||||
"sha256:7d59b6622675ca9e993a6bd38de845051d315f8b0c72cca3aef733a20b648657",
|
||||
"sha256:aec56c0eb1691a841795111e184e13cad504f7703b9a64f63020816afa79a8ae"
|
||||
],
|
||||
"markers": "sys_platform == 'linux'",
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"keyring": {
|
||||
"hashes": [
|
||||
"sha256:045703609dd3fccfcdb27da201684278823b72af515aedec1a8515719a038cb8",
|
||||
"sha256:8f607d7d1cc502c43a932a275a56fe47db50271904513a379d39df1af277ac48"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==23.0.1"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
|
||||
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
|
||||
"sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
|
||||
"sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
|
||||
],
|
||||
"version": "==20.9"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==21.0"
|
||||
},
|
||||
"pkginfo": {
|
||||
"hashes": [
|
||||
"sha256:029a70cb45c6171c329dfc890cde0879f8c52d6f3922794796e06f577bb03db4",
|
||||
"sha256:9fdbea6495622e022cc72c2e5e1b735218e4ffb2a2a69cde2694a6c1f16afb75"
|
||||
"sha256:37ecd857b47e5f55949c41ed061eb51a0bee97a87c969219d144c0e023982779",
|
||||
"sha256:e7432f81d08adec7297633191bbf0bd47faf13cd8724c3a13250e51d542635bd"
|
||||
],
|
||||
"version": "==1.7.0"
|
||||
"version": "==1.7.1"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.13.1"
|
||||
},
|
||||
"py": {
|
||||
@@ -293,20 +252,15 @@
|
||||
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
|
||||
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.10.0"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
|
||||
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
|
||||
],
|
||||
"version": "==2.20"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f",
|
||||
"sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==2.9.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
@@ -314,6 +268,7 @@
|
||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.4.7"
|
||||
},
|
||||
"pytest": {
|
||||
@@ -321,23 +276,32 @@
|
||||
"sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b",
|
||||
"sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==6.2.4"
|
||||
},
|
||||
"pytest-httpx": {
|
||||
"hashes": [
|
||||
"sha256:0a7c56e559b23efbf857054cd74de60a7c540694a162423f89c70da6ad358d8e",
|
||||
"sha256:d32e8f6fb7e028f0313f5f5a2d463c8673eb43fd11a9bfe8527299717a7764c4"
|
||||
"sha256:1e135b8779060091fa1c87d8aff7904921e8bea95fce5e971a0262764d064b12",
|
||||
"sha256:e262932f2d3ce380da8273c7bacbcfdc2c94e167fa94da29571caaf1f4d3ba27"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.10.1"
|
||||
"version": "==0.12.0"
|
||||
},
|
||||
"pytest-mock": {
|
||||
"hashes": [
|
||||
"sha256:024e405ad382646318c4281948aadf6fe1135632bea9cc67366ea0c4098ef5f2",
|
||||
"sha256:a4d6d37329e4a893e77d9ffa89e838dd2b45d5dc099984cf03c703ac8411bb82"
|
||||
"sha256:30c2f2cc9759e76eee674b81ea28c9f0b94f8f0445a1b87762cadf774f0df7e3",
|
||||
"sha256:40217a058c52a63f1042f0784f62009e976ba824c418cced42e88d5f40ab0e62"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.3.1"
|
||||
"version": "==3.6.1"
|
||||
},
|
||||
"pywin32-ctypes": {
|
||||
"hashes": [
|
||||
"sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942",
|
||||
"sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"
|
||||
],
|
||||
"markers": "sys_platform == 'win32'",
|
||||
"version": "==0.2.0"
|
||||
},
|
||||
"readme-renderer": {
|
||||
"hashes": [
|
||||
@@ -348,10 +312,11 @@
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
|
||||
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
|
||||
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
|
||||
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
|
||||
],
|
||||
"version": "==2.25.1"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==2.26.0"
|
||||
},
|
||||
"requests-toolbelt": {
|
||||
"hashes": [
|
||||
@@ -370,19 +335,12 @@
|
||||
],
|
||||
"version": "==1.5.0"
|
||||
},
|
||||
"secretstorage": {
|
||||
"hashes": [
|
||||
"sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f",
|
||||
"sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"
|
||||
],
|
||||
"markers": "sys_platform == 'linux'",
|
||||
"version": "==3.3.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"sniffio": {
|
||||
@@ -390,6 +348,7 @@
|
||||
"sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663",
|
||||
"sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"toml": {
|
||||
@@ -397,30 +356,32 @@
|
||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.10.2"
|
||||
},
|
||||
"tqdm": {
|
||||
"hashes": [
|
||||
"sha256:736524215c690621b06fc89d0310a49822d75e599fcd0feb7cc742b98d692493",
|
||||
"sha256:cd5791b5d7c3f2f1819efc81d36eb719a38e0906a7380365c556779f585ea042"
|
||||
"sha256:5aa445ea0ad8b16d82b15ab342de6b195a722d75fc1ef9934a46bba6feafbc64",
|
||||
"sha256:8bb94db0d4468fea27d004a0f1d1c02da3cdedc00fe491c0de986b76a04d6b0a"
|
||||
],
|
||||
"version": "==4.61.0"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==4.61.2"
|
||||
},
|
||||
"twine": {
|
||||
"hashes": [
|
||||
"sha256:34352fd52ec3b9d29837e6072d5a2a7c6fe4290e97bba46bb8d478b5c598f7ab",
|
||||
"sha256:ba9ff477b8d6de0c89dd450e70b2185da190514e91c42cc62f96850025c10472"
|
||||
"sha256:087328e9bb405e7ce18527a2dca4042a84c7918658f951110b38bc135acab218",
|
||||
"sha256:4caec0f1ed78dc4c9b83ad537e453d03ce485725f2aea57f1bb3fdde78dae936"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.2.0"
|
||||
"version": "==3.4.2"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
|
||||
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
|
||||
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
|
||||
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.26.5"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==1.26.6"
|
||||
},
|
||||
"webencodings": {
|
||||
"hashes": [
|
||||
@@ -431,18 +392,19 @@
|
||||
},
|
||||
"wheel": {
|
||||
"hashes": [
|
||||
"sha256:906864fb722c0ab5f2f9c35b2c65e3af3c009402c108a709c0aca27bc2c9187b",
|
||||
"sha256:aaef9b8c36db72f8bf7f1e54f85f875c4d466819940863ca0b3f3f77f0a1646f"
|
||||
"sha256:78b5b185f0e5763c26ca1e324373aadd49182ca90e825f7853f4b2509215dc0e",
|
||||
"sha256:e11eefd162658ea59a60a0f6c7d493a7190ea4b9a85e335b33489d9f17e0245e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.36.1"
|
||||
"version": "==0.36.2"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76",
|
||||
"sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"
|
||||
"sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3",
|
||||
"sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"
|
||||
],
|
||||
"version": "==3.4.1"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
pytchat is a lightweight python library to browse youtube livechat without Selenium or BeautifulSoup.
|
||||
"""
|
||||
__copyright__ = 'Copyright (C) 2019, 2020 taizan-hokuto'
|
||||
__version__ = '0.5.4'
|
||||
__copyright__ = 'Copyright (C) 2019, 2020, 2021 taizan-hokuto'
|
||||
__version__ = '0.5.5'
|
||||
__license__ = 'MIT'
|
||||
__author__ = 'taizan-hokuto'
|
||||
__author_email__ = '55448286+taizan-hokuto@users.noreply.github.com'
|
||||
|
||||
@@ -29,9 +29,13 @@ class PytchatCore:
|
||||
|
||||
processor : ChatProcessor
|
||||
|
||||
client : httpx.Client
|
||||
The client for connecting youtube.
|
||||
You can specify any customized httpx client (e.g. coolies, user agent).
|
||||
|
||||
interruptable : bool
|
||||
Allows keyboard interrupts.
|
||||
Set this parameter to False if your own threading program causes
|
||||
Set this parameter to False if your own multi-threading program causes
|
||||
the problem.
|
||||
|
||||
force_replay : bool
|
||||
@@ -57,6 +61,7 @@ class PytchatCore:
|
||||
def __init__(self, video_id,
|
||||
seektime=-1,
|
||||
processor=DefaultProcessor(),
|
||||
client = httpx.Client(http2=True),
|
||||
interruptable=True,
|
||||
force_replay=False,
|
||||
topchat_only=False,
|
||||
@@ -64,6 +69,7 @@ class PytchatCore:
|
||||
logger=config.logger(__name__),
|
||||
replay_continuation=None
|
||||
):
|
||||
self._client = client
|
||||
self._video_id = util.extract_video_id(video_id)
|
||||
self.seektime = seektime
|
||||
if isinstance(processor, tuple):
|
||||
@@ -97,7 +103,7 @@ class PytchatCore:
|
||||
"""
|
||||
self.continuation = liveparam.getparam(
|
||||
self._video_id,
|
||||
channel_id=util.get_channelid(httpx.Client(http2=True), self._video_id),
|
||||
channel_id=util.get_channelid(self._client, self._video_id),
|
||||
past_sec=3)
|
||||
|
||||
def _get_chat_component(self):
|
||||
@@ -110,19 +116,18 @@ class PytchatCore:
|
||||
parameter for next chat data
|
||||
'''
|
||||
try:
|
||||
with httpx.Client(http2=True) as client:
|
||||
if self.continuation and self._is_alive:
|
||||
contents = self._get_contents(self.continuation, client, headers)
|
||||
metadata, chatdata = self._parser.parse(contents)
|
||||
timeout = metadata['timeoutMs'] / 1000
|
||||
chat_component = {
|
||||
"video_id": self._video_id,
|
||||
"timeout": timeout,
|
||||
"chatdata": chatdata
|
||||
}
|
||||
self.continuation = metadata.get('continuation')
|
||||
self._last_offset_ms = metadata.get('last_offset_ms', 0)
|
||||
return chat_component
|
||||
if self.continuation and self._is_alive:
|
||||
contents = self._get_contents(self.continuation, self._client, headers)
|
||||
metadata, chatdata = self._parser.parse(contents)
|
||||
timeout = metadata['timeoutMs'] / 1000
|
||||
chat_component = {
|
||||
"video_id": self._video_id,
|
||||
"timeout": timeout,
|
||||
"chatdata": chatdata
|
||||
}
|
||||
self.continuation = metadata.get('continuation')
|
||||
self._last_offset_ms = metadata.get('last_offset_ms', 0)
|
||||
return chat_component
|
||||
except exceptions.ChatParseException as e:
|
||||
self._logger.debug(f"[{self._video_id}]{str(e)}")
|
||||
self._raise_exception(e)
|
||||
@@ -139,9 +144,8 @@ class PytchatCore:
|
||||
-------
|
||||
'continuationContents' which includes metadata & chat data.
|
||||
'''
|
||||
livechat_json = (
|
||||
self._get_livechat_json(continuation, client, replay=self._is_replay, offset_ms=self._last_offset_ms)
|
||||
)
|
||||
livechat_json = self._get_livechat_json(
|
||||
continuation, client, replay=self._is_replay, offset_ms=self._last_offset_ms)
|
||||
contents, dat = self._parser.get_contents(livechat_json)
|
||||
if self._dat == '' and dat:
|
||||
self._dat = dat
|
||||
@@ -152,7 +156,8 @@ class PytchatCore:
|
||||
self._fetch_url = config._smr
|
||||
continuation = arcparam.getparam(
|
||||
self._video_id, self.seektime, self._topchat_only, util.get_channelid(client, self._video_id))
|
||||
livechat_json = self._get_livechat_json(continuation, client, replay=True, offset_ms=self.seektime * 1000)
|
||||
livechat_json = self._get_livechat_json(
|
||||
continuation, client, replay=True, offset_ms=self.seektime * 1000)
|
||||
reload_continuation = self._parser.reload_continuation(
|
||||
self._parser.get_contents(livechat_json)[0])
|
||||
if reload_continuation:
|
||||
@@ -173,15 +178,14 @@ class PytchatCore:
|
||||
offset_ms = 0
|
||||
param = util.get_param(continuation, dat=self._dat, replay=replay, offsetms=offset_ms)
|
||||
for _ in range(MAX_RETRY + 1):
|
||||
with httpx.Client(http2=True) as client:
|
||||
try:
|
||||
response = client.post(self._fetch_url, json=param)
|
||||
livechat_json = json.loads(response.text)
|
||||
break
|
||||
except (json.JSONDecodeError, httpx.ConnectTimeout, httpx.ReadTimeout, httpx.ConnectError) as e:
|
||||
err = e
|
||||
time.sleep(2)
|
||||
continue
|
||||
try:
|
||||
response = client.post(self._fetch_url, json=param)
|
||||
livechat_json = response.json()
|
||||
break
|
||||
except (json.JSONDecodeError, httpx.ConnectTimeout, httpx.ReadTimeout, httpx.ConnectError) as e:
|
||||
err = e
|
||||
time.sleep(2)
|
||||
continue
|
||||
else:
|
||||
self._logger.error(f"[{self._video_id}]"
|
||||
f"Exceeded retry count. Last error: {str(err)}")
|
||||
@@ -202,6 +206,8 @@ class PytchatCore:
|
||||
return self._is_alive
|
||||
|
||||
def terminate(self):
|
||||
if not self.is_alive():
|
||||
return
|
||||
self._is_alive = False
|
||||
self.processor.finalize()
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ class LiveChatAsync:
|
||||
seektime=-1,
|
||||
processor=DefaultProcessor(),
|
||||
buffer=None,
|
||||
client = httpx.AsyncClient(http2=True),
|
||||
interruptable=True,
|
||||
callback=None,
|
||||
done_callback=None,
|
||||
@@ -88,6 +89,7 @@ class LiveChatAsync:
|
||||
logger=config.logger(__name__),
|
||||
replay_continuation=None
|
||||
):
|
||||
self._client:httpx.AsyncClient = client
|
||||
self._video_id = util.extract_video_id(video_id)
|
||||
self.seektime = seektime
|
||||
if isinstance(processor, tuple):
|
||||
@@ -152,9 +154,10 @@ class LiveChatAsync:
|
||||
create and start _listen loop.
|
||||
"""
|
||||
if not self.continuation:
|
||||
channel_id = await util.get_channelid_async(self._client, self._video_id)
|
||||
self.continuation = liveparam.getparam(
|
||||
self._video_id,
|
||||
channel_id=util.get_channelid(httpx.Client(http2=True), self._video_id),
|
||||
channel_id,
|
||||
past_sec=3)
|
||||
|
||||
await self._listen(self.continuation)
|
||||
@@ -169,10 +172,10 @@ class LiveChatAsync:
|
||||
parameter for next chat data
|
||||
'''
|
||||
try:
|
||||
async with httpx.AsyncClient(http2=True) as client:
|
||||
async with self._client as client:
|
||||
while(continuation and self._is_alive):
|
||||
continuation = await self._check_pause(continuation)
|
||||
contents = await self._get_contents(continuation, client, headers)
|
||||
contents = await self._get_contents(continuation, client, headers) #Q#
|
||||
metadata, chatdata = self._parser.parse(contents)
|
||||
continuation = metadata.get('continuation')
|
||||
if continuation:
|
||||
@@ -214,9 +217,10 @@ class LiveChatAsync:
|
||||
'''
|
||||
self._pauser.put_nowait(None)
|
||||
if not self._is_replay:
|
||||
async with httpx.AsyncClient(http2=True) as client:
|
||||
continuation = await liveparam.getparam(self._video_id,
|
||||
channel_id=util.get_channelid_async(client, self.video_id),
|
||||
async with self._client as client:
|
||||
channel_id = await util.get_channelid_async(client, self.video_id)
|
||||
continuation = liveparam.getparam(self._video_id,
|
||||
channel_id,
|
||||
past_sec=3)
|
||||
|
||||
return continuation
|
||||
@@ -338,6 +342,8 @@ class LiveChatAsync:
|
||||
self._logger.debug(f'[{self._video_id}] cancelled:{sender}')
|
||||
|
||||
def terminate(self):
|
||||
if not self.is_alive():
|
||||
return
|
||||
if self._pauser.empty():
|
||||
self._pauser.put_nowait(None)
|
||||
self._is_alive = False
|
||||
|
||||
@@ -78,6 +78,7 @@ class LiveChat:
|
||||
def __init__(self, video_id,
|
||||
seektime=-1,
|
||||
processor=DefaultProcessor(),
|
||||
client = httpx.Client(http2=True),
|
||||
buffer=None,
|
||||
interruptable=True,
|
||||
callback=None,
|
||||
@@ -88,6 +89,7 @@ class LiveChat:
|
||||
logger=config.logger(__name__),
|
||||
replay_continuation=None
|
||||
):
|
||||
self._client = client
|
||||
self._video_id = util.extract_video_id(video_id)
|
||||
self.seektime = seektime
|
||||
if isinstance(processor, tuple):
|
||||
@@ -150,7 +152,7 @@ class LiveChat:
|
||||
if not self.continuation:
|
||||
self.continuation = liveparam.getparam(
|
||||
self._video_id,
|
||||
channel_id=util.get_channelid(httpx.Client(http2=True), self._video_id),
|
||||
channel_id=util.get_channelid(self._client, self._video_id),
|
||||
past_sec=3)
|
||||
self._listen(self.continuation)
|
||||
|
||||
@@ -164,7 +166,7 @@ class LiveChat:
|
||||
parameter for next chat data
|
||||
'''
|
||||
try:
|
||||
with httpx.Client(http2=True) as client:
|
||||
with self._client as client:
|
||||
while(continuation and self._is_alive):
|
||||
continuation = self._check_pause(continuation)
|
||||
contents = self._get_contents(continuation, client, headers)
|
||||
@@ -224,7 +226,8 @@ class LiveChat:
|
||||
-------
|
||||
'continuationContents' which includes metadata & chat data.
|
||||
'''
|
||||
livechat_json = self._get_livechat_json(continuation, client, replay=self._is_replay, offset_ms=self._last_offset_ms)
|
||||
livechat_json = self._get_livechat_json(
|
||||
continuation, client, replay=self._is_replay, offset_ms=self._last_offset_ms)
|
||||
contents, dat = self._parser.get_contents(livechat_json)
|
||||
if self._dat == '' and dat:
|
||||
self._dat = dat
|
||||
@@ -235,8 +238,8 @@ class LiveChat:
|
||||
self._fetch_url = config._smr
|
||||
continuation = arcparam.getparam(
|
||||
self._video_id, self.seektime, self._topchat_only, util.get_channelid(client, self._video_id))
|
||||
livechat_json = (self._get_livechat_json(
|
||||
continuation, client, replay=True, offset_ms=self.seektime * 1000))
|
||||
livechat_json = self._get_livechat_json(
|
||||
continuation, client, replay=True, offset_ms=self.seektime * 1000)
|
||||
reload_continuation = self._parser.reload_continuation(
|
||||
self._parser.get_contents(livechat_json)[0])
|
||||
if reload_continuation:
|
||||
@@ -331,6 +334,8 @@ class LiveChat:
|
||||
self._logger.debug(f'[{self._video_id}] cancelled:{sender}')
|
||||
|
||||
def terminate(self):
|
||||
if not self.is_alive():
|
||||
return
|
||||
if self._pauser.empty():
|
||||
self._pauser.put_nowait(None)
|
||||
self._is_alive = False
|
||||
|
||||
@@ -7,6 +7,7 @@ from .renderer.paidmessage import LiveChatPaidMessageRenderer
|
||||
from .renderer.paidsticker import LiveChatPaidStickerRenderer
|
||||
from .renderer.legacypaid import LiveChatLegacyPaidMessageRenderer
|
||||
from .renderer.membership import LiveChatMembershipItemRenderer
|
||||
from .renderer.donation import LiveChatDonationAnnouncementRenderer
|
||||
from .. chat_processor import ChatProcessor
|
||||
from ... import config
|
||||
|
||||
@@ -124,7 +125,8 @@ class DefaultProcessor(ChatProcessor):
|
||||
"liveChatPaidMessageRenderer": LiveChatPaidMessageRenderer(),
|
||||
"liveChatPaidStickerRenderer": LiveChatPaidStickerRenderer(),
|
||||
"liveChatLegacyPaidMessageRenderer": LiveChatLegacyPaidMessageRenderer(),
|
||||
"liveChatMembershipItemRenderer": LiveChatMembershipItemRenderer()
|
||||
"liveChatMembershipItemRenderer": LiveChatMembershipItemRenderer(),
|
||||
"liveChatDonationAnnouncementRenderer": LiveChatDonationAnnouncementRenderer(),
|
||||
}
|
||||
|
||||
def process(self, chat_components: list):
|
||||
|
||||
6
pytchat/processors/default/renderer/donation.py
Normal file
6
pytchat/processors/default/renderer/donation.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from .base import BaseRenderer
|
||||
|
||||
|
||||
class LiveChatDonationAnnouncementRenderer(BaseRenderer):
|
||||
def settype(self):
|
||||
self.chat.type = "donation"
|
||||
@@ -82,16 +82,17 @@ class RingQueue:
|
||||
|
||||
class SpeedCalculator(ChatProcessor, RingQueue):
|
||||
"""
|
||||
チャットの勢いを計算する。
|
||||
Calculate the momentum of the chat.
|
||||
|
||||
一定期間のチャットデータのうち、最初のチャットの投稿時刻と
|
||||
最後のチャットの投稿時刻の差を、チャット数で割り返し
|
||||
1分あたりの速度に換算する。
|
||||
Divide the difference between the time of the first chat and
|
||||
the time of the last chat in the chat data over a period of
|
||||
time by the number of chats and convert it to speed per minute.
|
||||
|
||||
Parameter
|
||||
----------
|
||||
capacity : int
|
||||
RingQueueに格納するチャット勢い算出用データの最大数
|
||||
Maximum number of data for calculating chat momentum
|
||||
to be stored in RingQueue.
|
||||
"""
|
||||
|
||||
def __init__(self, capacity=10):
|
||||
@@ -111,17 +112,17 @@ class SpeedCalculator(ChatProcessor, RingQueue):
|
||||
|
||||
def _calc_speed(self):
|
||||
"""
|
||||
RingQueue内のチャット勢い算出用データリストを元に、
|
||||
チャット速度を計算して返す
|
||||
Calculates the chat speed based on the data list for calculating
|
||||
the chat momentum in RingQueue.
|
||||
|
||||
Return
|
||||
---------------------------
|
||||
チャット速度(1分間で換算したチャット数)
|
||||
Chat speed (number of chats converted per minute)
|
||||
"""
|
||||
try:
|
||||
# キュー内の総チャット数
|
||||
# Total number of chats in the queue
|
||||
total = sum(item['chat_count'] for item in self.items)
|
||||
# キュー内の最初と最後のチャットの時間差
|
||||
# Interval between the first and last chats in the queue
|
||||
duration = (self.items[self.last_pos]['endtime'] - self.items[self.first_pos]['starttime'])
|
||||
if duration != 0:
|
||||
return int(total * 60 / duration)
|
||||
@@ -131,19 +132,12 @@ class SpeedCalculator(ChatProcessor, RingQueue):
|
||||
|
||||
def _put_chatdata(self, actions):
|
||||
"""
|
||||
チャットデータからタイムスタンプを読み取り、勢い測定用のデータを組み立て、
|
||||
RingQueueに投入する。
|
||||
200円以上のスパチャはtickerとmessageの2つのデータが生成されるが、
|
||||
tickerの方は時刻データの場所が異なることを利用し、勢いの集計から除外している。
|
||||
Parameter
|
||||
---------
|
||||
actions : List[dict]
|
||||
チャットデータ(addChatItemAction) のリスト
|
||||
List of addChatItemActions
|
||||
"""
|
||||
def _put_emptydata():
|
||||
'''
|
||||
チャットデータがない場合に空のデータをキューに投入する。
|
||||
'''
|
||||
timestamp_now = int(time.time())
|
||||
self.put({
|
||||
'chat_count': 0,
|
||||
@@ -152,9 +146,6 @@ class SpeedCalculator(ChatProcessor, RingQueue):
|
||||
})
|
||||
|
||||
def _get_timestamp(action: dict):
|
||||
"""
|
||||
チャットデータから時刻データを取り出す。
|
||||
"""
|
||||
try:
|
||||
item = action['addChatItemAction']['item']
|
||||
timestamp = int(item[list(item.keys())[0]]['timestampUsec'])
|
||||
@@ -166,32 +157,24 @@ class SpeedCalculator(ChatProcessor, RingQueue):
|
||||
_put_emptydata()
|
||||
return
|
||||
|
||||
# actions内の時刻データを持つチャットデータの数
|
||||
counter = 0
|
||||
# actions内の最初のチャットデータの時刻
|
||||
starttime = None
|
||||
# actions内の最後のチャットデータの時刻
|
||||
endtime = None
|
||||
|
||||
for action in actions:
|
||||
# チャットデータからtimestampUsecを読み取る
|
||||
# Get timestampUsec from chatdata
|
||||
gettime = _get_timestamp(action)
|
||||
|
||||
# 時刻のないデータだった場合は次の行のデータで読み取り試行
|
||||
if gettime is None:
|
||||
continue
|
||||
|
||||
# 最初に有効な時刻を持つデータのtimestampをstarttimeに設定
|
||||
if starttime is None:
|
||||
starttime = gettime
|
||||
|
||||
# 最後のtimestampを設定(途中で時刻のないデータの場合もあるので上書きしていく)
|
||||
endtime = gettime
|
||||
|
||||
# チャットの数をインクリメント
|
||||
counter += 1
|
||||
|
||||
# チャット速度用のデータをRingQueueに送る
|
||||
if starttime is None or endtime is None:
|
||||
_put_emptydata()
|
||||
return
|
||||
|
||||
@@ -1 +1 @@
|
||||
httpx[http2]==0.16.1
|
||||
httpx[http2]
|
||||
1
setup.py
1
setup.py
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
],
|
||||
description="a python library for fetching youtube live chat.",
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
from pytchat.processors.superchat.calculator import SuperchatCalculator
|
||||
|
||||
get_item = SuperchatCalculator()._get_item
|
||||
|
||||
dict_test = {
|
||||
'root':{
|
||||
'node0' : 'value0',
|
||||
'node1' : 'value1',
|
||||
'node2' : {
|
||||
'node2-0' : 'value2-0'
|
||||
},
|
||||
|
||||
'node3' : [
|
||||
{'node3-0' : 'value3-0'},
|
||||
{'node3-1' :
|
||||
{'node3-1-0' : 'value3-1-0'}
|
||||
}
|
||||
],
|
||||
'node4' : [],
|
||||
'node5' : [
|
||||
[
|
||||
{'node5-1-0' : 'value5-1-0'},
|
||||
{'node5-1-1' : 'value5-1-1'},
|
||||
],
|
||||
{'node5-0' : 'value5-0'},
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
items_test0 = [
|
||||
'root',
|
||||
'node1'
|
||||
]
|
||||
|
||||
|
||||
items_test_not_found0 = [
|
||||
'root',
|
||||
'other_data'
|
||||
]
|
||||
|
||||
|
||||
items_test_nest = [
|
||||
'root',
|
||||
'node2',
|
||||
'node2-0'
|
||||
]
|
||||
|
||||
items_test_list0 = [
|
||||
'root',
|
||||
'node3',
|
||||
1,
|
||||
'node3-1'
|
||||
]
|
||||
|
||||
items_test_list1 = [
|
||||
'root',
|
||||
'node3',
|
||||
1,
|
||||
'node3-1',
|
||||
'node3-1-0'
|
||||
]
|
||||
|
||||
items_test_list2 = [
|
||||
'root',
|
||||
'node4',
|
||||
None
|
||||
]
|
||||
|
||||
items_test_list3 = [
|
||||
'root',
|
||||
'node4'
|
||||
]
|
||||
|
||||
items_test_list_nest = [
|
||||
'root',
|
||||
'node5',
|
||||
0,
|
||||
1,
|
||||
'node5-1-1'
|
||||
]
|
||||
|
||||
items_test_list_nest_not_found1 = [
|
||||
'root',
|
||||
'node5',
|
||||
0,
|
||||
1,
|
||||
'node5-1-1',
|
||||
'nodez'
|
||||
]
|
||||
|
||||
items_test_not_found1 = [
|
||||
'root',
|
||||
'node3',
|
||||
2,
|
||||
'node3-1',
|
||||
'node3-1-0'
|
||||
]
|
||||
|
||||
items_test_not_found2 = [
|
||||
'root',
|
||||
'node3',
|
||||
2,
|
||||
'node3-1',
|
||||
'node3-1-0',
|
||||
'nodex'
|
||||
]
|
||||
def test_get_items_0():
|
||||
assert get_item(dict_test, items_test0) == 'value1'
|
||||
|
||||
def test_get_items_1():
|
||||
assert get_item(dict_test, items_test_not_found0) is None
|
||||
|
||||
def test_get_items_2():
|
||||
assert get_item(dict_test, items_test_nest) == 'value2-0'
|
||||
|
||||
def test_get_items_3():
|
||||
assert get_item(
|
||||
dict_test, items_test_list0) == {'node3-1-0' : 'value3-1-0'}
|
||||
|
||||
def test_get_items_4():
|
||||
assert get_item(dict_test, items_test_list1) == 'value3-1-0'
|
||||
|
||||
def test_get_items_5():
|
||||
assert get_item(dict_test, items_test_not_found1) == None
|
||||
|
||||
def test_get_items_6():
|
||||
assert get_item(dict_test, items_test_not_found2) == None
|
||||
|
||||
def test_get_items_7():
|
||||
assert get_item(dict_test, items_test_list2) == None
|
||||
|
||||
def test_get_items_8():
|
||||
assert get_item(dict_test, items_test_list_nest) == 'value5-1-1'
|
||||
|
||||
def test_get_items_9():
|
||||
assert get_item(dict_test, items_test_list_nest_not_found1) == None
|
||||
|
||||
def test_get_items_10():
|
||||
assert get_item(dict_test, items_test_list3) == []
|
||||
@@ -67,6 +67,5 @@ def test_process_2():
|
||||
'chatdata': load_chatdata(r"tests/testdata/calculator/replay_end.json")
|
||||
}
|
||||
assert False
|
||||
SuperchatCalculator().process([chat_component])
|
||||
except ChatParseException:
|
||||
assert True
|
||||
|
||||
24
tests/test_compatible_processor2.py
Normal file
24
tests/test_compatible_processor2.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import pytchat
|
||||
from pytchat.processors.compatible.processor import CompatibleProcessor
|
||||
|
||||
|
||||
root_keys = ('kind', 'etag', 'nextPageToken', 'pollingIntervalMillis', 'pageInfo', 'items')
|
||||
item_keys = ('kind', 'etag', 'id', 'snippet', 'authorDetails')
|
||||
snippet_keys = ('type', 'liveChatId', 'authorChannelId', 'publishedAt', 'hasDisplayContent', 'displayMessage', 'textMessageDetails')
|
||||
author_details_keys = ('channelId', 'channelUrl', 'displayName', 'profileImageUrl', 'isVerified', 'isChatOwner', 'isChatSponsor', 'isChatModerator')
|
||||
|
||||
def test_compatible_processor():
|
||||
stream = pytchat.create("Hj-wnLIYKjw", seektime = 6000, processor=CompatibleProcessor())
|
||||
while stream.is_alive():
|
||||
chat = stream.get()
|
||||
for key in chat.keys():
|
||||
assert key in root_keys
|
||||
for key in chat["items"][0].keys():
|
||||
assert key in item_keys
|
||||
for key in chat["items"][0]["snippet"].keys():
|
||||
assert key in snippet_keys
|
||||
for key in chat["items"][0]["authorDetails"].keys():
|
||||
assert key in author_details_keys
|
||||
break
|
||||
|
||||
|
||||
94
tests/test_default_processor2.py
Normal file
94
tests/test_default_processor2.py
Normal file
@@ -0,0 +1,94 @@
|
||||
import asyncio
|
||||
import pytchat
|
||||
from concurrent.futures import CancelledError
|
||||
from pytchat.core_multithread.livechat import LiveChat
|
||||
from pytchat.core_async.livechat import LiveChatAsync
|
||||
|
||||
cases = [
|
||||
{
|
||||
"video_id":"1X7oL0hDnMg", "seektime":1620,
|
||||
"result1":{'textMessage': 84},
|
||||
"result2":{'': 83, 'MODERATOR': 1}
|
||||
},
|
||||
{
|
||||
"video_id":"Hj-wnLIYKjw", "seektime":420,
|
||||
"result1":{'superChat': 1, 'newSponsor': 6, 'textMessage': 63, 'donation': 1},
|
||||
"result2":{'': 59, 'MEMBER': 12}
|
||||
},
|
||||
{
|
||||
"video_id":"S8dmq5YIUoc", "seektime":3,
|
||||
"result1":{'textMessage': 86},
|
||||
"result2":{'': 62, 'MEMBER': 21, 'OWNER': 2, 'VERIFIED': 1}
|
||||
},{
|
||||
"video_id":"yLrstz80MKs", "seektime":30,
|
||||
"result1":{'superSticker': 8, 'superChat': 2, 'textMessage': 67},
|
||||
"result2":{'': 73, 'MEMBER': 4}
|
||||
}
|
||||
]
|
||||
|
||||
def test_archived_stream():
|
||||
for case in cases:
|
||||
stream = pytchat.create(video_id=case["video_id"], seektime=case["seektime"])
|
||||
while stream.is_alive():
|
||||
chat = stream.get()
|
||||
agg1 = {}
|
||||
agg2 = {}
|
||||
for c in chat.items:
|
||||
if c.type in agg1:
|
||||
agg1[c.type] += 1
|
||||
else:
|
||||
agg1[c.type] = 1
|
||||
if c.author.type in agg2:
|
||||
agg2[c.author.type] += 1
|
||||
else:
|
||||
agg2[c.author.type] = 1
|
||||
break
|
||||
assert agg1 == case["result1"]
|
||||
assert agg2 == case["result2"]
|
||||
|
||||
|
||||
def test_archived_stream_multithread():
|
||||
for case in cases:
|
||||
stream = LiveChat(video_id=case["video_id"], seektime=case["seektime"])
|
||||
while stream.is_alive():
|
||||
chat = stream.get()
|
||||
agg1 = {}
|
||||
agg2 = {}
|
||||
for c in chat.items:
|
||||
if c.type in agg1:
|
||||
agg1[c.type] += 1
|
||||
else:
|
||||
agg1[c.type] = 1
|
||||
if c.author.type in agg2:
|
||||
agg2[c.author.type] += 1
|
||||
else:
|
||||
agg2[c.author.type] = 1
|
||||
break
|
||||
assert agg1 == case["result1"]
|
||||
assert agg2 == case["result2"]
|
||||
|
||||
def test_async_live_stream():
|
||||
async def test_loop():
|
||||
for case in cases:
|
||||
stream = LiveChatAsync(video_id=case["video_id"], seektime=case["seektime"])
|
||||
while stream.is_alive():
|
||||
chat = await stream.get()
|
||||
agg1 = {}
|
||||
agg2 = {}
|
||||
for c in chat.items:
|
||||
if c.type in agg1:
|
||||
agg1[c.type] += 1
|
||||
else:
|
||||
agg1[c.type] = 1
|
||||
if c.author.type in agg2:
|
||||
agg2[c.author.type] += 1
|
||||
else:
|
||||
agg2[c.author.type] = 1
|
||||
break
|
||||
assert agg1 == case["result1"]
|
||||
assert agg2 == case["result2"]
|
||||
loop = asyncio.get_event_loop()
|
||||
try:
|
||||
loop.run_until_complete(test_loop())
|
||||
except CancelledError:
|
||||
assert True
|
||||
16
tests/test_speed_calculator2.py
Normal file
16
tests/test_speed_calculator2.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import json
|
||||
import pytchat
|
||||
from pytchat.parser.live import Parser
|
||||
from pytchat.processors.speed.calculator import SpeedCalculator
|
||||
|
||||
parser = Parser(is_replay=False)
|
||||
|
||||
|
||||
def test_speed_1():
|
||||
stream = pytchat.create("Hj-wnLIYKjw", seektime = 6000,processor=SpeedCalculator())
|
||||
while stream.is_alive():
|
||||
speed = stream.get()
|
||||
assert speed > 100
|
||||
break
|
||||
test_speed_1()
|
||||
|
||||
Reference in New Issue
Block a user