diff --git a/Cargo.lock b/Cargo.lock index c44fe02..3643963 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,10 +3,10 @@ version = 3 [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aes" @@ -35,10 +35,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" [[package]] -name = "anstream" -version = "0.6.15" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -51,49 +66,49 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "arg_enum_proc_macro" @@ -113,10 +128,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "autocfg" -version = "1.3.0" +name = "audiotags" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "44e797ce0164cf599c71f2c3849b56301d96a3dc033544588e875686b050ed39" +dependencies = [ + "audiotags-macro", + "id3", + "metaflac", + "mp4ameta", + "readme-rustdocifier", + "thiserror", +] + +[[package]] +name = "audiotags-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa9b2312fc01f7291f3b7b0f52ed08b1c0177c96a2e696ab55695cc4d06889" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "av1-grain" @@ -134,9 +169,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" dependencies = [ "arrayvec", ] @@ -160,16 +195,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "bitstream-io" -version = "2.5.0" +name = "bitflags" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" [[package]] name = "built" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4" +checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b" [[package]] name = "bumpalo" @@ -179,9 +220,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" [[package]] name = "byteorder" @@ -197,9 +238,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "cc" -version = "1.1.13" +version = "1.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" dependencies = [ "jobserver", "libc", @@ -222,6 +263,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "cipher" version = "0.4.4" @@ -234,9 +289,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -244,9 +299,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -256,9 +311,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -280,9 +335,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" @@ -295,10 +350,16 @@ dependencies = [ ] [[package]] -name = "cpufeatures" -version = "0.2.13" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -390,12 +451,11 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "exr" -version = "1.72.0" +version = "1.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" dependencies = [ "bit_field", - "flume", "half", "lebe", "miniz_oxide", @@ -406,32 +466,23 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" dependencies = [ "simd-adler32", ] [[package]] name = "flate2" -version = "1.0.31" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", ] -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "spin", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -475,9 +526,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heck" @@ -498,10 +549,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] -name = "image" -version = "0.25.2" +name = "iana-time-zone" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id3" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55f4e785f2c700217ee82a1c727c720449421742abd5fcb2f1df04e1244760e9" +dependencies = [ + "bitflags 2.6.0", + "byteorder", + "flate2", +] + +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" dependencies = [ "bytemuck", "byteorder-lite", @@ -522,9 +607,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" dependencies = [ "byteorder-lite", "quick-error", @@ -532,15 +617,15 @@ dependencies = [ [[package]] name = "imgref" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -602,6 +687,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -616,29 +710,18 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.156" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libfuzzer-sys" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" dependencies = [ "arbitrary", "cc", - "once_cell", -] - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", ] [[package]] @@ -663,6 +746,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" dependencies = [ "cfg-if", + "rayon", ] [[package]] @@ -671,6 +755,16 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "metaflac" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f083edae4a21f5acb1fda8220d1c14fa31f725bfd4e21005a14c2d8944db9b" +dependencies = [ + "byteorder", + "hex", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -679,25 +773,44 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", "simd-adler32", ] +[[package]] +name = "mp4ameta" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb23d62e8eb5299a3f79657c70ea9269eac8f6239a76952689bcd06a74057e81" +dependencies = [ + "lazy_static", + "mp4ameta_proc", +] + +[[package]] +name = "mp4ameta_proc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07dcca13d1740c0a665f77104803360da0bdb3323ecce2e93fa2c959a6d52806" + [[package]] name = "ncmmiao" -version = "1.1.3" +version = "2.2.4" dependencies = [ "aes", + "audiotags", "base64", + "chrono", "clap", "colored", "env_logger", "hex", "image", + "lazy_static", "log", "serde", "serde_derive", @@ -779,9 +892,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "paste" @@ -791,17 +904,17 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "png" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", @@ -819,27 +932,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "profiling" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", "syn", @@ -862,9 +975,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -936,15 +1049,16 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd" +checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" dependencies = [ "avif-serialize", "imgref", "loop9", "quick-error", "rav1e", + "rayon", "rgb", ] @@ -969,10 +1083,16 @@ dependencies = [ ] [[package]] -name = "regex" -version = "1.10.6" +name = "readme-rustdocifier" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "08ad765b21a08b1a8e5cdce052719188a23772bcbefb3c439f0baaf62c56ceac" + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -982,9 +1102,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -993,18 +1113,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rgb" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" -dependencies = [ - "bytemuck", -] +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" [[package]] name = "ryu" @@ -1021,26 +1138,20 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "serde" -version = "1.0.208" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -1049,9 +1160,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -1061,9 +1172,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -1095,15 +1206,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "strsim" version = "0.11.1" @@ -1112,9 +1214,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.74" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -1142,18 +1244,18 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" dependencies = [ "proc-macro2", "quote", @@ -1194,9 +1296,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -1213,9 +1315,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "utf8parse" @@ -1264,9 +1366,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -1275,9 +1377,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -1290,9 +1392,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1300,9 +1402,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -1313,9 +1415,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "weezl" @@ -1332,6 +1434,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1341,15 +1452,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.59.0" @@ -1482,9 +1584,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index a45b108..91ed966 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,21 @@ [package] name = "ncmmiao" -version = "1.1.3" +version = "2.2.4" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] aes = "0.8.3" +audiotags = "0.5.0" base64 = "0.22.*" +chrono = "0.4.38" clap = { version = "4.5.9", features = ["derive"] } colored = "2.1.0" env_logger = "0.11.1" hex = "0.4.3" image = "0.25.*" +lazy_static = "1.5.0" log = "0.4.20" serde = { version = "1.0.195", features = ["derive"] } serde_derive = "1.0.195" @@ -21,3 +24,11 @@ walkdir = "2.4.0" [badges] maintenance = { status = "actively-developed" } + +[profile.release] +#缩小编译后体积 +strip = true +# strip = "debuginfo" #仅移除debug信息 +lto = true #启用链接时间优化 +panic = "abort" #panic时直接abort +opt-level = "z" #优化级别 diff --git a/README.md b/README.md index 94dcd57..d7bbd3e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # NcmMiao :tada: [![build](https://github.com/Lkhsss/NcmMiao/actions/workflows/build.yml/badge.svg?event=push)](https://github.com/Lkhsss/NcmMiao/actions/workflows/build.yml) -一个使用Rust语言编写的ncm文件解密工具。 +一个使用Rust语言编写的ncm文件解密工具(第一!😆)。 ### 功能及特点 - 支持单一文件,多文件夹递归批量解密。 - 完善的日志功能 - Colorful - 编译文件小,解密快 + - [New!]支持自动添加封面! ## 编译 ``` @@ -17,20 +18,25 @@ cargo build -r ## 使用 支持单一文件,多文件夹递归批量解密。 ``` -cargo run -r <文件或文件夹路径1> <文件或文件夹路径2> <文件或文件夹路径3> ... +ncmmiao [OPTIONS] +Options: + -w, --workers 最大线程数 约束逻辑在主函数 + -i, --input <输入文件/文件夹> 需要解密的文件夹或文件 + -o, --output <输出文件夹> [默认: NcmmiaoOutput] ``` -> 注意!如果没有指定任何文件夹或者文件,那么程序将自动读取工作目录下的CloudMusic和input文件夹下的文件。如果都没有将自动退出。 -输出文件夹在output。等我想写了再写命令行解析(bushi。 +~~输出文件夹在output。等我想写了再写命令行解析(bushi。~~ 写了写了 --- # TODO :construction: - [x] 多线程支持 + - [x] 自动添加封面 - [ ] 解密进度条 - - [ ] 命令行解析 - - [ ] 自定义输出文件夹 - - [ ] 计时功能 + - [x] 命令行解析 + - [x] 自定义输出文件夹 + - [x] 计时功能 + - [ ] 自动覆盖开关 --- @@ -55,10 +61,21 @@ cargo run -r <文件或文件夹路径1> <文件或文件夹路径2> <文件或 ## [1.1.4] - 2024-7-14 ### Features :sparkles: - 增加多线程支持! - > 目前固定4线程,还没写命令行参数。可以源代码修改线程数 + > ~~目前固定4线程,还没写命令行参数。可以源代码修改线程数~~ 已于2.1.4版本修复~ ### Fixed - 优化代码结构 +## [2.2.4] - 2024-11-17 +### Features :sparkles: +- :ambulance:完整的多线程支持!可自定线程数! +- :ambulance:自动添加封面,解密文件不再需要musictag! +- :sparkles: 完整的命令行参数支持! +- :sparkles: 计时功能! +### Fixed +- :arrow_up: 升级依赖 +- 优化保存文件逻辑,保存时间缩短到毫秒 +### Refactoring +- :hammer: 重构代码! --- # 附 - ncm文件结构 diff --git a/src/clap.rs b/src/clap.rs new file mode 100644 index 0000000..fbe7d3b --- /dev/null +++ b/src/clap.rs @@ -0,0 +1,17 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(name = "ncmmiao")] +#[command(author = "lkhsss")] +#[command(version,about = "一个解密ncm文件的神秘程序", long_about = None)] +pub struct Cli { + /// 最大线程数 约束逻辑在主函数 + #[arg(short, long)] + pub workers: Option, + /// 需要解密的文件夹或文件 + #[arg(short, long, name = "输入文件/文件夹")] + pub input: Vec, + + #[arg(short, long, name = "输出文件夹", default_value = "NcmmiaoOutput")] + pub output: Option, +} diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..c5ba71e --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,47 @@ +use chrono; +use env_logger::Builder; +use std::io::Write; + +pub struct Logger {} + +impl Logger { + pub fn new() { + let mut builder = Builder::new(); + if cfg!(debug_assertions) { + builder.filter(None, log::LevelFilter::Debug); + } else { + builder.filter(None, log::LevelFilter::Info); + } + builder.format(move |buf, record| { + writeln!( + buf, + "[{} {}] {}", + format!("{}", chrono::Local::now().format("%H:%M:%S")), + match record.level() { + log::Level::Error => { + let style = buf.default_level_style(log::Level::Error); + format!("{style}Error{style:#}") + } + log::Level::Warn => { + let style = buf.default_level_style(log::Level::Warn); + format!("{style}Warn{style:#}") + } + log::Level::Info => { + let style = buf.default_level_style(log::Level::Info); + format!("{style}Info{style:#}") + } + log::Level::Debug => { + let style = buf.default_level_style(log::Level::Debug); + format!("{style}Warn{style:#}") + } + log::Level::Trace => { + let style = buf.default_level_style(log::Level::Trace); + format!("{style}Trace{style:#}") + } + }, + record.args(), + ) + }); + builder.init(); //初始化logger + } +} diff --git a/src/main.rs b/src/main.rs index 282cb9a..e095077 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,58 +1,45 @@ -use env_logger::Builder; -use hex::decode; +use ::clap::Parser; +#[allow(unused_imports)] use log::{error, info, warn}; -use std::env; use std::path::Path; -// use std::time::SystemTime; use walkdir::WalkDir; //遍历目录 +mod clap; +mod logger; mod ncmdump; mod threadpool; -use ncmdump::{dump, Key, Ncmfile}; +use ncmdump::Ncmfile; mod test; fn main() { + let timer = ncmdump::TimeCompare::new(); + // 初始化日志系统 + logger::Logger::new(); + + let cli = clap::Cli::parse(); + // 最大线程数 - let max_workers = 4; - - let mut builder = Builder::new(); - builder.filter(None, log::LevelFilter::Info); - builder.init(); //初始化logger - - let keys: Key = Key { - core: decode("687A4852416D736F356B496E62617857").unwrap(), - meta: decode("2331346C6A6B5F215C5D2630553C2728").unwrap(), - }; - - let args: Vec = env::args().collect(); - let args = if args.len() == 1 { - warn!("未指定文件夹,将使用默认文件夹。"); - - let mut args_temp = Vec::new(); - if Path::new("CloudMusic").exists() { - warn!("CloudMusic文件夹存在,将自动使用。"); - args_temp.push(String::from("CloudMusic")); - }; - if Path::new("input").exists() { - warn!("input文件夹存在,将自动使用。"); - args_temp.push(String::from("input")); - }; - if args_temp.is_empty() { - //TODO 增加软件介绍 - error!("没有参数\n没有CloudMusic或者input文件夹存在与于工作目录"); - panic!("没有参数\n没有CloudMusic或者input文件夹存在与于工作目录"); + let max_workers = match cli.workers { + Some(n) => { + if n >= 1 { + n + } else { + 1 + } } - args_temp - } else { - args[1..].to_vec() + None => 4, }; + let input = cli.input; + + let outputdir = cli.output.unwrap(); + let mut undumpfile = Vec::new(); // 该列表将存入文件的路径 - for arg in &args { + for arg in input { //解析传入的每一个路径:文件or文件夹 - let path = Path::new(arg); + let path = Path::new(&arg); if path.is_file() { // 当后缀符合为ncm时才加入列表 @@ -82,17 +69,27 @@ fn main() { } } } - // let filepaths = undumpfile; - // let count = undumpfile.len(); - // let mut time = 0usize; + let taskcount = undumpfile.len(); + if taskcount == 0 { + error!("没有找到有效文件") + } else { + // 初始化线程池 + let pool = threadpool::Pool::new(max_workers); - // 初始化线程池 - let pool = threadpool::Pool::new(max_workers); - for filepath in undumpfile { - let tkey = keys.clone(); - pool.execute(move || { - let mut ncmfile = Ncmfile::new(filepath.as_str()).unwrap(); - dump(&mut ncmfile, &tkey, Path::new("output")).unwrap(); - }); + for filepath in undumpfile { + let output = outputdir.clone(); + pool.execute(move || { + Ncmfile::new(filepath.as_str()) + .unwrap() + .dump(Path::new(&output)) + .unwrap(); + }); + } + } + let timecount = timer.compare(); + if timecount > 2000 { + info!("解密{}个文件,共计用时{}秒", taskcount, timecount / 1000) + } else { + info!("解密{}个文件,共计用时{}毫秒", taskcount, timecount) } } diff --git a/src/ncmdump.rs b/src/ncmdump.rs index d7410da..8235a50 100644 --- a/src/ncmdump.rs +++ b/src/ncmdump.rs @@ -1,25 +1,38 @@ -#[warn(dead_code)] use aes::cipher::generic_array::typenum::U16; use aes::cipher::{generic_array::GenericArray, BlockDecrypt, KeyInit}; use aes::Aes128; -use base64; +use audiotags::{MimeType, Picture, Tag}; +use base64::{self, Engine}; use colored::*; +use hex::decode; +use lazy_static::lazy_static; +#[allow(unused_imports)] use log::{debug, error, info, trace, warn}; use serde_derive::{Deserialize, Serialize}; use serde_json::{self, Value}; use std::fs::{self, File}; -use std::io::{BufReader, Error, ErrorKind, Read, Seek, SeekFrom, Write}; -use std::path::Path; +use std::io::{BufReader, BufWriter, Error, ErrorKind, Read, Seek, SeekFrom, Write}; +use std::path::{Path, PathBuf}; use std::str::from_utf8; +use std::vec; + +use std::time::{SystemTime, UNIX_EPOCH}; + +lazy_static! { + // 解密需要的密钥 + static ref KEY_CORE: Vec = decode("687A4852416D736F356B496E62617857").unwrap(); + static ref KEY_META: Vec = decode("2331346C6A6B5F215C5D2630553C2728").unwrap(); +} #[derive(Debug)] +#[allow(unused_variables)] pub struct Ncmfile { /// 文件对象 pub file: File, - /// 歌曲名称,不带文件后缀 - pub name: String, - /// 文件名称,带后缀名 + /// 文件名称,不带文件后缀 pub filename: String, + /// 文件名称,带后缀名 + pub fullfilename: String, /// 文件大小 pub size: u64, /// 游标 @@ -29,16 +42,16 @@ impl Ncmfile { pub fn new(filepath: &str) -> Result { let file = File::open(filepath)?; let path = Path::new(filepath); - let filename = path.file_name().unwrap().to_str().unwrap().to_string(); + let fullfilename = path.file_name().unwrap().to_str().unwrap().to_string(); let size = file.metadata().unwrap().len(); - let name = match Path::new(&filepath).file_stem() { + let filename = match Path::new(&filepath).file_stem() { Some(f) => f.to_str().unwrap().to_string(), None => panic!("获取文件名失败"), }; Ok(Ncmfile { file, - name, filename, + fullfilename, size, position: 0, }) @@ -68,6 +81,7 @@ impl Ncmfile { /// /// - offset 开始位置 /// - length 想要读取的长度 + #[allow(dead_code)] pub fn seekread_from(&mut self, offset: u64, length: u64) -> Result, std::io::Error> { if self.position + length > self.size { return Err(Error::new( @@ -83,6 +97,7 @@ impl Ncmfile { Ok(buf[..].to_vec()) } } + #[allow(dead_code)] pub fn seekread_to_end(&mut self) -> Result, std::io::Error> { let mut reader = BufReader::new(&self.file); reader.seek(SeekFrom::Start(self.position))?; @@ -132,10 +147,248 @@ impl Ncmfile { } key } + + /// 解密函数 + #[allow(unused_assignments)] + pub fn dump(&mut self, outputdir: &Path) -> Result<(), MyError> { + info!("开始解密[{}]文件", self.fullfilename.yellow()); + // 获取magic header 。应为CTENFDAM + let magic_header = match self.seekread(8) { + Ok(header) => header, + Err(_e) => { + error!("读取magic header失败"); + return Err(MyError::MagicHeaderError); + } + }; + + // 判断是否为ncm格式的文件 + match from_utf8(&magic_header) { + Ok(header) => { + if header != "CTENFDAM" { + // 传播错误至dump + return Err(MyError::MagicHeaderError); + } else { + trace!("[{}]为ncm格式文件", self.fullfilename.yellow()); + debug!("magic header: {}", header); + } + } + // 传播错误至dump + Err(_e) => return Err(MyError::MagicHeaderError), + } + + // 跳过2字节 + trace!("跳过2字节"); + match self.skip(2) { + Ok(_) => (), + Err(_e) => return Err(MyError::FileSkipError), + }; + + trace!("获取RC4密钥长度"); + //小端模式读取RC4密钥长度 正常情况下应为128 + let key_length = u32::from_le_bytes(self.seekread(4).unwrap().try_into().unwrap()) as u64; + debug!("RC4密钥长度为:{}", key_length); + + //读取密钥 开头应为 neteasecloudmusic + trace!("读取RC4密钥"); + let mut key_data = self.seekread(key_length).unwrap(); + //aes128解密 + let key_data = &aes128_to_slice(&KEY_CORE, Self::parse_key(&mut key_data[..])); //先把密钥按照字节进行0x64异或 + // RC4密钥 + let key_data = unpad(&key_data[..])[17..].to_vec(); + + //读取meta信息的数据大小 + trace!("获取meta信息数据大小"); + let meta_length = u32::from_le_bytes(self.seekread(4).unwrap().try_into().unwrap()) as u64; + + // 读取meta信息 + trace!("读取meta信息"); + let meta_data = { + let mut meta_data = self.seekread(meta_length).unwrap(); //读取源数据 + //字节对0x63进行异或。 + for i in 0..meta_data.len() { + meta_data[i] ^= 0x63; + } + // base64解密 + let mut decode_data = Vec::::new(); + let _ = &base64::engine::general_purpose::STANDARD + .decode_vec(&mut meta_data[22..], &mut decode_data) + .unwrap(); + // aes128解密 + let aes_data = aes128_to_slice(&KEY_META, &decode_data); + // unpadding + let json_data = String::from_utf8(unpad(&aes_data)[6..].to_vec()).unwrap(); + debug!("json_data: {}", json_data); + let data: Value = serde_json::from_str(&json_data[..]).unwrap(); //解析json数据 + data + }; + + // 跳过4个字节的校验码 + trace!("读取校验码"); + let _crc32 = u32::from_le_bytes(self.seekread(4).unwrap().try_into().unwrap()) as u64; + + // 跳过5个字节 + trace!("跳过5个字节"); + self.skip(5).unwrap(); + + // 获取图片数据的大小 + trace!("获取图片数据的大小"); + let image_data_length = + u32::from_le_bytes(self.seekread(4).unwrap().try_into().unwrap()) as u64; + + // 读取图片,并写入文件当中 + trace!("暂不需要保存图片,跳过{}字节", image_data_length); + let image_data = self.seekread(image_data_length).unwrap(); //读取图片数据 + // let _ = self.skip(image_data_length); //暂不需要保存图片,直接跳过这些字节就好 + //保存图片 + // trace!("保存图片"); + // let mut file = File::create(format!("TEST.jpg",)).unwrap(); + // file.write_all(&image_data).unwrap(); + + trace!("组成密码盒"); + let key_box = { + let key_length = key_data.len(); + let key_data = Vec::from(key_data); + let mut key_box = (0..=255).collect::>(); + let mut temp = 0; + let mut last_byte = 0; + let mut key_offset = 0; + + for i in 0..=255 { + let swap = key_box[i as usize] as u64; + temp = (swap + last_byte as u64 + key_data[key_offset as usize] as u64) & 0xFF; + key_offset += 1; + if key_offset >= key_length { + key_offset = 0; + } + key_box[i as usize] = key_box[temp as usize]; + key_box[temp as usize] = swap as u8; + last_byte = temp; + } + let key_box = key_box.clone(); + key_box + }; + + /* let mut s_box = { + let key_length = key_data.len(); + let key_box = Vec::from(key_data); + let mut s = (0..=255).collect::>(); + let mut j = 0; + + for i in 0..=255 { + j = (j as usize + s[i] as usize + key_box[i % key_length] as usize) & 0xFF; + + //记录 s[j]的值 + let temp = &s.get(j as usize).unwrap().to_owned(); + s[j as usize] = s[i]; + s[i] = temp.to_owned(); + } + + s + }; */ + + // let key_box = key_box[0..(key_box.len()-key_box[key_box.len() as usize-1] as usize)].to_vec(); + + //解密音乐数据 + trace!("解密音乐数据"); + let mut music_data: Vec = Vec::new(); + loop { + let mut chunk = self.seekread_no_error(0x8000); + + let chunk_length = chunk.len(); + if chunk_length != 0 { + for i in 1..chunk_length + 1 { + let j = i & 0xFF; + + chunk[i - 1] ^= key_box[(key_box[j] as usize + + key_box[(key_box[j as usize] as usize + j) & 0xff] as usize) + & 0xff] + // chunk[i - 1] ^= key_box[(key_box[j] + key_box[(key_box[j as usize] as usize + j as usize) & 0xFF]) & 0xFF]; + } + //向music_data中最追加chunk + music_data.append(&mut chunk); + } else { + break; + } + } + + //组成流密钥 + /* let mut stream = Vec::new(); + for i in 0..256 { + stream.push( + s_box[(s_box[i] as usize + s_box[(i + s_box[i] as usize) & 0xFF] as usize) & 0xFF], + ) + } */ + + // 解密音乐数据 + /* loop { + let chunk = self.seekread_no_error(256); //每次读取256个字节 + if chunk.len() != 0 { + for (count, &i) in chunk[..].iter().enumerate() { + music_data.push(i ^ stream[count]) + } + } else { + break; + } + } */ + // debug!("music_data:{:?}", music_data); + // debug!("长度:{}", stream.len()); + + //退出循环,写入文件 + + //处理文件路径 + trace!("拼接文件路径"); + let path = { + let filename = format!( + "{}.{}", + self.filename, + meta_data.get("format").unwrap().as_str().unwrap() + ); + + // let filename = standardize_filename(filename); + debug!("文件名:{}", filename.yellow()); + //链级创建输出目录 + fs::create_dir_all(outputdir).unwrap(); + outputdir.join(filename) + }; + + debug!("文件路径: {:?}", path); + self.save(&path, music_data); + + { + // 保存封面 + let mut tag = Tag::new().read_from_path(&path).unwrap(); + let cover = Picture { + mime_type: MimeType::Jpeg, + data: &image_data, + }; + tag.set_album_cover(cover); //添加封面 + let _ = tag.write_to_path(&path.to_str().unwrap()); //保存 + } + + info!( + "[{}]文件已保存到: {}", + self.filename.yellow(), + path.to_str().unwrap().bright_cyan() + ); + info!( + "[{}]{}", + self.fullfilename.yellow(), + "解密成功".bright_green() + ); + Ok(()) + } + fn save(&mut self, path: &PathBuf, data: Vec) { + let music_file = File::create(path).unwrap(); + let mut writer = BufWriter::new(music_file); + let _ = writer.write_all(&data); + // 关闭文件 + writer.flush().unwrap(); + } } /// 存储元数据的结构体 #[derive(Serialize, Deserialize, Debug)] +#[allow(unused_variables, dead_code)] struct Metadata { //编号 #[serde(rename = "musicId", skip)] //没用过,跳过 @@ -183,253 +436,12 @@ struct Metadata { // 存储各种密钥的结构体 #[derive(Clone)] +#[allow(dead_code)] pub struct Key { pub core: Vec, pub meta: Vec, } -/// 解密函数 -pub fn dump(ncmfile: &mut Ncmfile, keys: &Key, outputdir: &Path) -> Result<(), MyError> { - info!("开始解密[{}]文件", ncmfile.filename.yellow()); - // 获取magic header 。应为CTENFDAM - trace!("获取 magic header"); - let magic_header = match ncmfile.seekread(8) { - Ok(header) => header, - Err(_e) => { - error!("读取magic header失败"); - return Err(MyError::MagicHeaderError); - } - }; - // 判断是否为ncm格式的文件 - match from_utf8(&magic_header) { - Ok(header) => { - if header != "CTENFDAM" { - // 传播错误至dump - return Err(MyError::MagicHeaderError); - } else { - trace!("[{}]为ncm格式文件", ncmfile.filename.yellow()); - } - } - // 传播错误至dump - Err(_e) => return Err(MyError::MagicHeaderError), - } - - // 跳过2字节 - trace!("跳过2字节"); - match ncmfile.skip(2) { - Ok(_) => (), - Err(_e) => return Err(MyError::FileSkipError), - }; - - trace!("获取RC4密钥长度"); - //小端模式读取RC4密钥长度 正常情况下应为128 - let key_length = u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64; - debug!("RC4密钥长度为:{}", key_length); - - //读取密钥 开头应为 neteasecloudmusic - trace!("读取RC4密钥"); - let mut key_data = ncmfile.seekread(key_length).unwrap(); - //aes128解密 - let key_data = &aes128_to_slice(&keys.core, Ncmfile::parse_key(&mut key_data[..])); //先把密钥按照字节进行0x64异或 - // RC4密钥 - let key_data = unpad(&key_data[..])[17..].to_vec(); - - //读取meta信息的数据大小 - trace!("获取meta信息数据大小"); - let meta_length = u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64; - - // 读取meta信息 - trace!("读取meta信息"); - let meta_data = { - let mut meta_data = ncmfile.seekread(meta_length).unwrap(); //读取源数据 - //字节对0x63进行异或。 - for i in 0..meta_data.len() { - meta_data[i] ^= 0x63; - } - // base64解密 - let decode_data = &base64::decode(&meta_data[22..]).unwrap()[..]; - // aes128解密 - let aes_data = aes128_to_slice(&keys.meta, decode_data); - // unpadding - let json_data = String::from_utf8(unpad(&aes_data)[6..].to_vec()).unwrap(); - debug!("json_data: {}", json_data); - let data: Value = serde_json::from_str(&json_data[..]).unwrap(); //解析json数据 - data - }; - - // 跳过4个字节的校验码 - trace!("读取校验码"); - let crc32 = u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64; - - // 跳过5个字节 - trace!("跳过5个字节"); - ncmfile.skip(5).unwrap(); - - // 获取图片数据的大小 - trace!("获取图片数据的大小"); - let image_data_length = - u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64; - - // 读取图片,并写入文件当中 - trace!("暂不需要保存图片,跳过{}字节", image_data_length); - let image_data = ncmfile.seekread(image_data_length).unwrap(); //读取图片数据 - // let _ = ncmfile.skip(image_data_length); //暂不需要保存图片,直接跳过这些字节就好 - - //保存图片 - // trace!("保存图片"); - // let mut file = File::create(format!("TEST.jpg",)).unwrap(); - // file.write_all(&image_data).unwrap(); - trace!("组成密码盒"); - let key_box = { - let key_length = key_data.len(); - let key_data = Vec::from(key_data); - let mut key_box = (0..=255).collect::>(); - let mut temp = 0; - let mut last_byte = 0; - let mut key_offset = 0; - - for i in 0..=255 { - let swap = key_box[i as usize] as u64; - temp = (swap + last_byte as u64 + key_data[key_offset as usize] as u64) & 0xFF; - key_offset += 1; - if key_offset >= key_length { - key_offset = 0; - } - key_box[i as usize] = key_box[temp as usize]; - key_box[temp as usize] = swap as u8; - last_byte = temp; - } - let key_box = key_box.clone(); - key_box - }; - - /* let mut s_box = { - let key_length = key_data.len(); - let key_box = Vec::from(key_data); - let mut s = (0..=255).collect::>(); - let mut j = 0; - - for i in 0..=255 { - j = (j as usize + s[i] as usize + key_box[i % key_length] as usize) & 0xFF; - - //记录 s[j]的值 - let temp = &s.get(j as usize).unwrap().to_owned(); - s[j as usize] = s[i]; - s[i] = temp.to_owned(); - } - - s - }; */ - - // let key_box = key_box[0..(key_box.len()-key_box[key_box.len() as usize-1] as usize)].to_vec(); - - //解密音乐数据 - trace!("解密音乐数据"); - let mut music_data: Vec = Vec::new(); - loop { - let mut chunk = ncmfile.seekread_no_error(0x8000); - - let chunk_length = chunk.len(); - if chunk_length != 0 { - for i in 1..chunk_length + 1 { - let j = i & 0xFF; - - chunk[i - 1] ^= key_box[(key_box[j] as usize - + key_box[(key_box[j as usize] as usize + j) & 0xff] as usize) - & 0xff] - // chunk[i - 1] ^= key_box[(key_box[j] + key_box[(key_box[j as usize] as usize + j as usize) & 0xFF]) & 0xFF]; - } - //向music_data中最追加chunk - music_data.append(&mut chunk); - } else { - break; - } - } - - //组成流密钥 - /* let mut stream = Vec::new(); - for i in 0..256 { - stream.push( - s_box[(s_box[i] as usize + s_box[(i + s_box[i] as usize) & 0xFF] as usize) & 0xFF], - ) - } */ - - // 解密音乐数据 - /* loop { - let chunk = ncmfile.seekread_no_error(256); //每次读取256个字节 - if chunk.len() != 0 { - for (count, &i) in chunk[..].iter().enumerate() { - music_data.push(i ^ stream[count]) - } - } else { - break; - } - } */ - // debug!("music_data:{:?}", music_data); - // debug!("长度:{}", stream.len()); - - //退出循环,写入文件 - - //处理文件路径 - trace!("拼接文件路径"); - let path = { - let mut artists = String::new(); - let mut music_artist = Vec::new(); - if let Some(i) = meta_data.get("artist") { - for names in i.as_array().unwrap() { - music_artist.push(names[0].as_str().unwrap()); - } - } - - for artist in music_artist { - artists.push_str(&artist); - artists.push(','); - } - // 移除最后一个字符 - artists.pop(); - debug!("艺术家名称:{}", artists.yellow()); - - let filename = format!( - "{} - {}.{}", - meta_data.get("musicName").unwrap().as_str().unwrap(), - artists, - meta_data.get("format").unwrap().as_str().unwrap() - ); - // .replace("\"", """) - // .replace("?", "?") - // .replace(":", ":"); - - let filename = standardize_filename(filename); - debug!("文件名:{}", filename.yellow()); - //链级创建输出目录 - fs::create_dir_all(outputdir).unwrap(); - outputdir.join(filename) - }; - - debug!("文件路径: {:?}", path); - // 创建文件 - trace!("创建文件"); - let mut music_file = File::create(&path).unwrap(); - trace!("保存文件"); - music_file.write_all(&music_data).unwrap(); - // 关闭文件 - music_file.sync_all().unwrap(); - music_file.flush().unwrap(); - - info!( - "[{}]文件已保存到: {}", - ncmfile.name.yellow(), - path.to_str().unwrap().bright_cyan() - ); - info!( - "{}{}{}", - "[".bright_green(), - ncmfile.filename.yellow(), - "]解密成功".bright_green() - ); - Ok(()) -} - // fn read_meta(file: &mut File, meta_length: u32) -> Result, Error> {} fn convert_to_generic_arrays(input: &[u8]) -> Vec> { @@ -451,6 +463,7 @@ fn convert_to_generic_arrays(input: &[u8]) -> Vec> { /// !!!未对齐数据!!! /// TODO /// 解密NCM文件的rc4密钥前记得按字节对0x64进行异或 +#[allow(dead_code)] fn aes128(key: &[u8], blocks: &[u8]) -> String { trace!("进行AES128解密"); let key = GenericArray::from_slice(key); @@ -501,16 +514,17 @@ fn aes128_to_slice(key: &[u8], blocks: &[u8]) -> Vec { /// 符号一一对应: /// - \ / * ? " : < > | /// - _ _ * ? " : ⟨ ⟩ _ -fn standardize_filename(old_filename: String) -> String { +fn standardize_filename(old_fullfilename: String) -> String { trace!("格式化文件名"); - let mut new_filename = String::from(old_filename); - // debug!("规范文件名:{}", new_filename); + let mut new_fullfilename = String::from(old_fullfilename); + // debug!("规范文件名:{}", new_fullfilename); let standard = ["\\", "/", "*", "?", "\"", ":", "<", ">", "|"]; let resolution = ["_", "_", "*", "?", """, ":", "⟨", "⟩", "_"]; for i in 0..standard.len() { - new_filename = new_filename.replace(&standard[i].to_string(), &resolution[i].to_string()); + new_fullfilename = + new_fullfilename.replace(&standard[i].to_string(), &resolution[i].to_string()); } - new_filename + new_fullfilename } /// 使用PKCS5Padding标准,去掉填充信息 @@ -519,12 +533,13 @@ fn unpad(data: &[u8]) -> Vec { } #[derive(Debug)] +#[allow(dead_code)] pub enum MyError { MagicHeaderError, FileReadError, FileSkipError, FileWriteError, - FilenameError, + FullFilenameError, FileNotFoundError, } @@ -536,8 +551,29 @@ impl std::fmt::Display for MyError { Self::MagicHeaderError => write!(f, "文件不为NCM格式"), Self::FileReadError => write!(f, "文件读取错误"), Self::FileWriteError => write!(f, "文件写入错误"), - Self::FilenameError => write!(f, "文件名不符合规范"), + Self::FullFilenameError => write!(f, "文件名不符合规范"), _ => write!(f, "未知错误"), } } } + +#[allow(dead_code)] +pub struct TimeCompare(u128); + +impl TimeCompare { + pub fn new() -> Self { + Self( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis(), + ) + } + pub fn compare(&self) -> u128 { + let time = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis(); + time - self.0 + } +} diff --git a/src/test.rs b/src/test.rs index 208a53b..eebaf95 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,14 +1,14 @@ #[cfg(test)] mod tests { use crate::*; - + #[test] #[ignore = "测验成功"] - fn it_works() { + fn threadpool() { let p = threadpool::Pool::new(4); p.execute(|| println!("do new job1")); p.execute(|| println!("do new job2")); p.execute(|| println!("do new job3")); p.execute(|| println!("do new job4")); } -} \ No newline at end of file +} diff --git a/src/threadpool.rs b/src/threadpool.rs index 424258c..41475ba 100644 --- a/src/threadpool.rs +++ b/src/threadpool.rs @@ -1,7 +1,6 @@ +use log::debug; use std::sync::{mpsc, Arc, Mutex}; use std::thread::{self, JoinHandle}; -use log::{info,debug}; -use serde::de; type Job = Box; enum Message { @@ -47,8 +46,8 @@ impl Pool { pub fn new(max_workers: usize) -> Pool { if max_workers == 0 { panic!("最大线程数不能小于零!") - }else { - debug!("将开启{}线程",max_workers); + } else { + debug!("将开启{}线程", max_workers); } let (tx, rx) = mpsc::channel(); @@ -86,5 +85,3 @@ impl Drop for Pool { } } } - -