commit e03d746ce6ff2aae37fdba2226726aea25af6825 Author: blank X Date: Sat Feb 13 15:01:33 2021 +0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4c8e0b5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1184 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bumpalo" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cc" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "encoding_rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" + +[[package]] +name = "futures-macro" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" + +[[package]] +name = "futures-task" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", + "tracing-futures", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "hentaihavenrs" +version = "0.1.0" +dependencies = [ + "clap", + "quick-xml", + "reqwest", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "http" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "httparse" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" + +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + +[[package]] +name = "hyper" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project 1.0.5", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +dependencies = [ + "futures-util", + "hyper", + "log", + "rustls", + "tokio", + "tokio-rustls", + "webpki", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +dependencies = [ + "socket2", + "winapi", +] + +[[package]] +name = "native-tls" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + +[[package]] +name = "openssl" +version = "0.10.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" +dependencies = [ + "pin-project-internal 0.4.27", +] + +[[package]] +name = "pin-project" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +dependencies = [ + "pin-project-internal 1.0.5", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quick-xml" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26aab6b48e2590e4a64d1ed808749ba06257882b461d01ca71baeb747074a6dd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd281b1030aa675fb90aa994d07187645bb3c8fc756ca766e7c3070b439de9de" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls", + "serde", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if", + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "tinyvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "pin-project-lite", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb7cb2f00c5ae8df755b252306272cd1790d39728363936e01827e11f0b017b" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project 0.4.27", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" +dependencies = [ + "cfg-if", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" + +[[package]] +name = "web-sys" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fcf134e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "hentaihavenrs" +version = "0.1.0" +authors = ["blank X "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +lto = true + +[dependencies] +tokio = { version = "1.1", features = ["rt"] } +reqwest = { version = "0.11", features = ["multipart", "rustls-tls"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +quick-xml = "0.20" +clap = { version = "2.33", default-features = false } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..211185a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 blank X + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/commands/download.rs b/src/commands/download.rs new file mode 100644 index 0000000..5754f85 --- /dev/null +++ b/src/commands/download.rs @@ -0,0 +1,104 @@ +use crate::utils; + +use std::io; +use std::fs::{rename, create_dir}; +use std::path::PathBuf; +use std::process::{exit, Command}; +use clap::ArgMatches; + +const MAX_DOWNLOAD_ATTEMPTS: i32 = 5; + +pub async fn download(arg_m: &ArgMatches<'_>) { + let print_only = arg_m.is_present("print"); + let id = arg_m.value_of("id").unwrap(); + let client = utils::create_client(); + let mut return_fail = false; + let hentai_info = utils::get_hentai(client.clone(), id).await; + match hentai_info { + Ok(hentai_info) => { + match hentai_info { + Some(hentai_info) => { + let tcloned_client = client.clone(); + let iter = (1..=hentai_info.episode_urls.len()).zip(hentai_info.episode_urls); + for (episode_number, episode_url) in iter { + let mut filename = PathBuf::from(&hentai_info.slug); + filename.push(&format!("{}.mkv", episode_number)); + if filename.exists() { + continue; + } + let download_url = match utils::get_url(tcloned_client.clone(), &episode_url).await { + Ok(Some(i)) => i, + Ok(None) => { + eprintln!("Failed to get {}: get_url returned None", filename.display()); + continue; + }, + Err(err) => { + eprintln!("Failed to get {}: {}", filename.display(), err); + continue; + } + }; + if print_only { + println!("{}", download_url); + continue; + } + match create_dir(&hentai_info.slug) { + Ok(_) => (), + Err(err) => { + if err.kind() != io::ErrorKind::AlreadyExists { + eprintln!("Failed to create parent directory due to {}", err); + exit(1); + } + } + }; + let mut tmp_filename = filename.clone(); + tmp_filename.set_extension("tmp"); + let mut fail_dl = true; + for i in 0..MAX_DOWNLOAD_ATTEMPTS { + eprintln!("Downloading {} (attempt {})", filename.display(), i); + let mut command = Command::new("ffmpeg"); + let command = command.args(&["-v", "warning", "-stats", "-nostdin", "-y", "-i"]); + let mut command = command.arg(&download_url.video); + if let Some(ref captions) = download_url.captions { + command = command.args(&["-i", &captions]); + } + match command.args(&["-c", "copy", "-f", "matroska"]).arg(&tmp_filename).spawn() { + Ok(mut child) => { + match child.wait() { + Ok(exit_status) => { + if exit_status.success() { + fail_dl = false; + match rename(&tmp_filename, &filename) { + Ok(_) => (), + Err(err) => eprintln!("Failed to rename {} to {} due to {}", tmp_filename.display(), filename.display(), err) + }; + break; + } + eprintln!("ffmpeg exited with {:?}", exit_status.code()); + }, + Err(err) => eprintln!("Failed to wait on ffmpeg process due to {}", err) + }; + }, + Err(err) => eprintln!("Failed to spawn ffmpeg process due to {}", err) + }; + } + if fail_dl { + eprintln!("Failed to download {}", filename.display()); + return_fail = true; + } + } + }, + None => { + eprintln!("Failed to get {}: does not exist", id); + return_fail = true; + } + }; + }, + Err(err) => { + eprintln!("Failed to get {}: {}", id, err); + return_fail = true; + } + }; + if return_fail { + exit(1); + } +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..249189a --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,6 @@ +mod view; +mod search; +mod download; +pub use view::view; +pub use search::search; +pub use download::download; diff --git a/src/commands/search.rs b/src/commands/search.rs new file mode 100644 index 0000000..84abcde --- /dev/null +++ b/src/commands/search.rs @@ -0,0 +1,17 @@ +use crate::utils; + +use std::process::exit; +use clap::ArgMatches; + +pub async fn search(arg_m: &ArgMatches<'_>) { + let query = arg_m.values_of("query").unwrap_or_default().collect::>().join(" "); + let query = query.trim(); + let results = utils::search(utils::create_client(), query).await.unwrap(); + if results.is_empty() { + eprintln!("No results found"); + exit(1); + } + for i in results { + println!("{}", i); + } +} diff --git a/src/commands/view.rs b/src/commands/view.rs new file mode 100644 index 0000000..845b7b7 --- /dev/null +++ b/src/commands/view.rs @@ -0,0 +1,64 @@ +use crate::utils; + +use std::process::exit; +use clap::ArgMatches; +extern crate tokio; + +pub async fn view(arg_m: &ArgMatches<'_>) { + let client = utils::create_client(); + let handles = arg_m.values_of("id").unwrap().map(|id| { + let cloned_client = client.clone(); + let id = id.to_string(); + let cid = id.clone(); + (tokio::spawn(async move { + utils::get_hentai(cloned_client, &cid).await + }), id) + }).collect::>(); + let mut fail = false; + let mut one_done = false; + for handle in handles { + let (handle, id) = handle; + let hentai = match handle.await { + Ok(hentai) => hentai, + Err(err) => { + if one_done { + eprintln!(""); + } + eprintln!("ID: {}\nError: {}", id, err); + fail = true; + one_done = true; + continue; + } + }; + match hentai { + Ok(hentai) => { + match hentai { + Some(hentai) => { + if one_done { + println!(""); + } + println!("ID: {}\n{}", id, &hentai); + }, + None => { + if one_done { + eprintln!(""); + } + eprintln!("ID: {}\nError: does not exist", id); + fail = true; + } + }; + }, + Err(err) => { + if one_done { + eprintln!(""); + } + eprintln!("ID: {}\nError: {}", id, err); + fail = true; + } + }; + one_done = true; + } + if fail { + exit(1); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8ee19bf --- /dev/null +++ b/src/main.rs @@ -0,0 +1,57 @@ +mod commands; +mod structs; +mod utils; +use clap::{App, AppSettings, Arg, SubCommand}; +extern crate tokio; + +fn main() { + let matches = App::new("hentaihavenrs") + .about("hentaihaven.tv downloader in rust") + .version(env!("CARGO_PKG_VERSION")) + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("search") + .arg( + Arg::with_name("query") + .takes_value(true) + .multiple(true) + .help("Search query") + ) + ) + .subcommand( + SubCommand::with_name("view") + .aliases(&["info", "show"]) + .arg( + Arg::with_name("id") + .takes_value(true) + .multiple(true) + .required(true) + ) + ) + .subcommand( + SubCommand::with_name("download") + .alias("dl") + .arg( + Arg::with_name("print") + .long("print") + .short("p") + .help("Print the URL to download only") + ).arg( + Arg::with_name("id") + .takes_value(true) + .required(true) + ) + ) + .get_matches(); + + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + match matches.subcommand() { + ("search", Some(sub_m)) => runtime.block_on(commands::search(sub_m)), + ("view", Some(sub_m)) => runtime.block_on(commands::view(sub_m)), + ("download", Some(sub_m)) => runtime.block_on(commands::download(sub_m)), + _ => panic!("AppSettings::SubcommandRequiredElseHelp do your job please") + }; +} diff --git a/src/structs.rs b/src/structs.rs new file mode 100644 index 0000000..07cbf73 --- /dev/null +++ b/src/structs.rs @@ -0,0 +1,122 @@ +use std::fmt; +use serde::Deserialize; +extern crate reqwest; +extern crate quick_xml; +extern crate serde_json; + +#[derive(Deserialize, Debug)] +pub struct SearchResult { + pub id: i32, + pub title: RenderedTitle +} + +#[derive(Deserialize, Debug)] +pub struct RenderedTitle { + pub rendered: String +} + +impl fmt::Display for SearchResult { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(&format!("{}: {}", self.id, &self.title.rendered)) + } +} + +#[derive(Debug)] +pub struct HentaiInfo { + pub slug: String, + pub title: String, + pub views: usize, + pub genres: Vec, + pub censored: bool, + pub episode_urls: Vec, + pub summary: String +} + +impl fmt::Display for HentaiInfo { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "Title: {}\nViews: {}\nCensored: {}\nGenres: {}\nEpisodes: {}\nSummary:\n{}", + &self.title, + self.views, + match self.censored { + true => "Yes", + false => "No" + }, + &self.genres.join(", "), + self.episode_urls.len(), + &self.summary + ) + } +} + +#[derive(Debug)] +pub struct HentaiVideo { + pub captions: Option, + pub video: String +} + +impl fmt::Display for HentaiVideo { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut text = self.video.clone(); + if let Some(subtitles) = &self.captions { + text.push(';'); + text.push_str(&subtitles); + } + formatter.write_str(&text) + } +} + +#[derive(Deserialize, Debug)] +pub struct RawHentaiVideo { + pub data: RawHentaiVideoData +} + +#[derive(Deserialize, Debug)] +pub struct RawHentaiVideoData { + pub captions: RawHentaiVideoSrc, + pub sources: Vec +} + +#[derive(Deserialize, Debug)] +pub struct RawHentaiVideoSrc { + pub src: String +} + +#[derive(Debug)] +pub enum Error { + Reqwest(reqwest::Error), + QuickXML(quick_xml::Error), + SerdeJSON(serde_json::Error), +} + +impl From for Error { + #[inline] + fn from(error: reqwest::Error) -> Error { + Error::Reqwest(error) + } +} + +impl From for Error { + #[inline] + fn from(error: quick_xml::Error) -> Error { + Error::QuickXML(error) + } +} + +impl From for Error { + #[inline] + fn from(error: serde_json::Error) -> Error { + Error::SerdeJSON(error) + } +} + +impl fmt::Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str( + &match self { + Error::Reqwest(err) => format!("reqwest error: {}", err), + Error::QuickXML(err) => format!("quick-xml error: {}", err), + Error::SerdeJSON(err) => format!("serde_json error: {}", err), + } + ) + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..bcef1d2 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,305 @@ +use crate::structs; + +use quick_xml::Reader; +use quick_xml::events::Event; +extern crate reqwest; +extern crate serde_json; + +pub async fn search(client: reqwest::Client, query: &str) -> Result, structs::Error> { + let text = &client.get("https://hentaihaven.xxx/wp-json/wp/v2/wp-manga") + .query(&[("search", &query)]) + .send() + .await? + .text() + .await?; + let text = text.trim_start_matches("\u{feff}"); + Ok(serde_json::from_str(&text)?) +} + +pub async fn get_hentai(client: reqwest::Client, id: &str) -> Result, structs::Error> { + let url = match id.contains(|c: char| !c.is_digit(10)) { + true => format!("https://hentaihaven.xxx/watch/{}", &id), + false => format!("https://hentaihaven.xxx/?post_type=wp-manga&p={}", &id) + }; + let resp = client.get(&url) + .send() + .await?; + if resp.status() != 200 { + return Ok(None); + } + let slug = resp.url().path().trim_end_matches('/').rsplitn(2, '/').nth(0).unwrap().to_string(); + let text = resp.text().await?.replace(" ", " "); + let mut reader = Reader::from_str(&text); + reader.check_end_names(false); + let mut buf = Vec::new(); + let mut is_inside_a = false; + let mut is_inside_summary = false; + let mut is_inside_nav_links = false; + let mut is_inside_post_title = false; + let mut is_inside_chapter_list = false; + let mut is_inside_summary_heading = false; + let mut is_inside_summary_content = false; + let mut to_read_rank = false; + let mut to_read_genres = false; + let mut rank = 0; + let mut title = String::new(); + let mut genres = Vec::new(); + let mut censored = true; + let mut episode_urls = Vec::new(); + let mut summary = String::new(); + loop { + match reader.read_event(&mut buf) { + Ok(Event::Start(ref e)) => { + if e.name() == b"div" { + let class = e.attributes() + .find(|i| { + match i.as_ref() { + Ok(i) => i.key == b"class", + Err(_) => false + } + }); + if let Some(class) = class { + match class.unwrap().unescape_and_decode_value(&reader) { + Ok(class_name) => { + match class_name.as_str() { + "summary-heading" => is_inside_summary_heading = true, + "summary-content" => is_inside_summary_content = true, + "post-title" => is_inside_post_title = true, + "nav-links" => is_inside_nav_links = true, + "listing-chapters_wrap" => is_inside_chapter_list = true, + "summary__content show-more" => is_inside_summary = true, + _ => () + }; + }, + Err(_) => () + }; + } + } else if e.name() == b"a" { + is_inside_a = true; + if is_inside_nav_links { + let class = e.attributes() + .find(|i| { + match i.as_ref() { + Ok(i) => i.key == b"class", + Err(_) => false + } + }); + if let Some(class) = class { + match class.unwrap().unescape_and_decode_value(&reader) { + Ok(class_name) => { + if class_name.to_lowercase().split_whitespace().any(|i| i == "uncensored") { + censored = false; + is_inside_nav_links = false; + } + }, + Err(_) => () + }; + } + } else if is_inside_chapter_list { + let href = e.attributes() + .find(|i| { + match i.as_ref() { + Ok(i) => i.key == b"href", + Err(_) => false + } + }); + if let Some(href) = href { + match href.unwrap().unescape_and_decode_value(&reader) { + Ok(href) => episode_urls.push(href), + Err(_) => () + }; + } + } + } + }, + Ok(Event::Text(e)) => { + let text = match e.unescape_and_decode(&reader) { + Ok(text) => text, + Err(_) => continue + }; + if is_inside_summary_heading { + match text.trim() { + "Rank" => to_read_rank = true, + "Genre(s)" => to_read_genres = true, + _ => () + }; + } else if is_inside_summary_content { + if to_read_rank { + match text.trim().splitn(2, " ").nth(0).unwrap().parse::() { + Ok(i) => rank = i, + Err(_) => () + }; + to_read_rank = false; + } else if to_read_genres && is_inside_a { + genres.push(text.to_string()); + } + } else if is_inside_post_title { + title.push_str(&text); + } else if is_inside_summary { + summary.push_str(&text); + } + }, + Ok(Event::End(ref e)) => { + if e.name() == b"div" { + if is_inside_summary_heading { + is_inside_summary_heading = false; + } else if is_inside_summary_content { + is_inside_summary_content = false; + to_read_genres = false; + } else if is_inside_post_title { + is_inside_post_title = false; + title = title.trim().to_string(); + } else if is_inside_nav_links { + is_inside_nav_links = false; + } else if is_inside_chapter_list { + is_inside_chapter_list = false; + } else if is_inside_summary { + break; + } + } else if e.name() == b"a" { + is_inside_a = false; + } + }, + Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err), + Ok(Event::Eof) => break, + _ => () + }; + buf.clear(); + } + episode_urls.reverse(); + summary = summary.trim().to_string(); + Ok(Some(structs::HentaiInfo { + slug: slug, + title: title, + views: rank, + genres: genres, + censored: censored, + episode_urls: episode_urls, + summary: summary + })) +} + +pub async fn get_url(client: reqwest::Client, url: &str) -> Result, structs::Error> { + let resp = client.get(url) + .send() + .await?; + if resp.status() != 200 { + return Ok(None); + } + let text = resp.text().await?; + let mut reader = Reader::from_str(&text); + reader.check_end_names(false); + let mut buf = Vec::new(); + let mut iframe_url = None; + loop { + match reader.read_event(&mut buf) { + Ok(Event::Start(ref e)) if e.name() == b"iframe" => { + let src = e.attributes() + .find(|i| { + match i.as_ref() { + Ok(i) => i.key == b"src", + Err(_) => false + } + }); + if let Some(src) = src { + match src.unwrap().unescape_and_decode_value(&reader) { + Ok(src) => { + iframe_url = Some(src); + break + }, + Err(_) => () + }; + } + }, + Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err), + Ok(Event::Eof) => break, + _ => () + }; + buf.clear(); + } + let iframe_url = match iframe_url { + Some(tmp) => tmp, + None => return Ok(None) + }; + parse_iframe(client, &iframe_url).await +} + +async fn parse_iframe(client: reqwest::Client, url: &str) -> Result, structs::Error> { + let resp = client.get(url) + .send() + .await?; + if resp.status() != 200 { + return Ok(None); + } + let text = resp.text().await?; + let mut reader = Reader::from_str(&text); + reader.check_end_names(false); + let mut buf = Vec::new(); + let mut form = reqwest::multipart::Form::new(); + let mut form_modified = false; + loop { + match reader.read_event(&mut buf) { + Ok(Event::Text(e)) => { + let text = match reader.decode(e.escaped()) { + Ok(text) => text, + Err(_) => continue + }; + for i in text.split('\n') { + let i = i.trim(); + if !i.starts_with("data.append('") { + continue; + } + let mut i = i.trim_start_matches("data.append('").trim_end_matches("');").splitn(2, "', '"); + let key = match i.next() { + Some(i) => i, + None => continue + }; + let value = match i.next() { + Some(i) => i, + None => continue + }; + form = form.text(key.to_string(), value.to_string()); + form_modified = true; + } + }, + Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err), + Ok(Event::Eof) => break, + _ => () + }; + buf.clear(); + } + if !form_modified { + return Ok(None); + } + let text = client.post("https://hentaihaven.xxx/wp-admin/admin-ajax.php") + .multipart(form) + .send() + .await? + .text() + .await?; + let text = text.trim_start_matches("\u{feff}"); + let raw_data: structs::RawHentaiVideo = serde_json::from_str(&text)?; + let raw_data = raw_data.data; + let captions = match client.get(&raw_data.captions.src).send().await?.status().as_u16() { + 200 => Some(raw_data.captions.src), + _ => None + }; + let video_url = match raw_data.sources.get(0) { + Some(i) => i.src.clone(), + None => return Ok(None) + }; + Ok(Some(structs::HentaiVideo { + captions: captions, + video: video_url + })) +} + +pub fn create_client() -> reqwest::Client { + // cloudflare you can go fuck yourself + reqwest::ClientBuilder::new() + .use_rustls_tls() + .http1_title_case_headers() + .user_agent("Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0") + .build() + .unwrap() +}