diff --git a/android/app/libs/libmtorrentserver.aar b/android/app/libs/libmtorrentserver.aar index 18ec1766..8c9eeadd 100644 Binary files a/android/app/libs/libmtorrentserver.aar and b/android/app/libs/libmtorrentserver.aar differ diff --git a/go/go.mod b/go/go.mod index e24894fb..3863aa06 100644 --- a/go/go.mod +++ b/go/go.mod @@ -3,27 +3,27 @@ module server go 1.25.0 require ( - github.com/anacrolix/torrent v1.58.1 + github.com/anacrolix/torrent v1.61.0 github.com/rs/cors v1.11.1 ) require ( github.com/RoaringBitmap/roaring v1.9.4 // indirect - github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect github.com/alecthomas/atomic v0.1.0-alpha2 // indirect - github.com/anacrolix/chansync v0.6.0 // indirect - github.com/anacrolix/dht/v2 v2.22.1 // indirect + github.com/anacrolix/btree v0.0.0-20251201064447-d86c3fa41bd8 // indirect + github.com/anacrolix/chansync v0.7.0 // indirect + github.com/anacrolix/dht/v2 v2.23.0 // indirect github.com/anacrolix/envpprof v1.4.0 // indirect - github.com/anacrolix/generics v0.0.3 // indirect + github.com/anacrolix/generics v0.1.1-0.20251125230353-15d98d46693b // indirect github.com/anacrolix/go-libutp v1.3.2 // indirect - github.com/anacrolix/log v0.16.0 // indirect + github.com/anacrolix/log v0.17.1-0.20251118025802-918f1157b7bb // indirect github.com/anacrolix/missinggo v1.3.0 // indirect github.com/anacrolix/missinggo/perf v1.0.0 // indirect - github.com/anacrolix/missinggo/v2 v2.8.0 // indirect + github.com/anacrolix/missinggo/v2 v2.10.0 // indirect github.com/anacrolix/mmsg v1.1.1 // indirect github.com/anacrolix/multiless v0.4.0 // indirect github.com/anacrolix/stm v0.5.0 // indirect - github.com/anacrolix/sync v0.5.4 // indirect + github.com/anacrolix/sync v0.5.5-0.20251119100342-d78dd1f686f1 // indirect github.com/anacrolix/upnp v0.1.4 // indirect github.com/anacrolix/utp v0.2.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect @@ -39,6 +39,7 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/google/btree v1.1.3 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/huandu/xstrings v1.5.0 // indirect @@ -71,20 +72,21 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/tidwall/btree v1.8.0 // indirect + github.com/tidwall/btree v1.8.1 // indirect github.com/wlynxg/anet v0.0.5 // indirect go.etcd.io/bbolt v1.4.2 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.37.0 // indirect - go.opentelemetry.io/otel/metric v1.37.0 // indirect - go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect golang.org/x/crypto v0.50.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect + golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect golang.org/x/mobile v0.0.0-20260410095206-2cfb76559b7b // indirect golang.org/x/net v0.53.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.43.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/text v0.36.0 // indirect + golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.44.0 // indirect lukechampine.com/blake3 v1.4.1 // indirect modernc.org/libc v1.66.3 // indirect diff --git a/go/go.sum b/go/go.sum index b75cbc95..4794c4f1 100644 --- a/go/go.sum +++ b/go/go.sum @@ -14,8 +14,6 @@ github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 h1:byYvvbfSo3+9efR4IeReh77gVs4PnNDR3AMOE9NJ7a0= -github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0/go.mod h1:q37NoqncT41qKc048STsifIt69LfUJ8SrWWcz/yam5k= github.com/alecthomas/assert/v2 v2.0.0-alpha3 h1:pcHeMvQ3OMstAWgaeaXIAL8uzB9xMm2zlxt+/4ml8lk= github.com/alecthomas/assert/v2 v2.0.0-alpha3/go.mod h1:+zD0lmDXTeQj7TgDgCt0ePWxb0hMC1G+PGTsTCv1B9o= github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8= @@ -26,28 +24,31 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/anacrolix/chansync v0.6.0 h1:/aQVvZ1yLRhmqEYrr9dC92JwzNBQ/SNnFi4uk+fTkQY= -github.com/anacrolix/chansync v0.6.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= -github.com/anacrolix/dht/v2 v2.22.1 h1:mgsljPXyA/EWA7uUDSNjx7wf6gsfhppjVIp9auVeR6w= -github.com/anacrolix/dht/v2 v2.22.1/go.mod h1:seXRz6HLw8zEnxlysf9ye2eQbrKUmch6PyOHpe/Nb/U= +github.com/anacrolix/btree v0.0.0-20251201064447-d86c3fa41bd8 h1:c02PsmoaChabVqAFm7pqPI1UIkDdDAjUaWa6ZmfxybQ= +github.com/anacrolix/btree v0.0.0-20251201064447-d86c3fa41bd8/go.mod h1:7stWJ39LeusmMI8mjJuhFNRqep//vx0AsaySRoK9or0= +github.com/anacrolix/chansync v0.7.0 h1:wgwxbsJRmOqNjil4INpxHrDp4rlqQhECxR8/WBP4Et0= +github.com/anacrolix/chansync v0.7.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= +github.com/anacrolix/dht/v2 v2.23.0 h1:EuD17ykTTEkAMPLjBsS5QjGOwuBgLTdQhds6zPAjeVY= +github.com/anacrolix/dht/v2 v2.23.0/go.mod h1:seXRz6HLw8zEnxlysf9ye2eQbrKUmch6PyOHpe/Nb/U= github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= github.com/anacrolix/envpprof v1.4.0 h1:QHeIcrgHcRChhnxR8l6rlaLlRQx9zd7Q2NII6Zbt83w= github.com/anacrolix/envpprof v1.4.0/go.mod h1:7QIG4CaX1uexQ3tqd5+BRa/9e2D02Wcertl6Yh0jCB0= github.com/anacrolix/generics v0.0.0-20230113004304-d6428d516633/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8= -github.com/anacrolix/generics v0.0.3 h1:wMkQgQzq0obSy1tMkxDu7Ife7PsegOBWHDRaSW31EnM= -github.com/anacrolix/generics v0.0.3/go.mod h1:MN3ve08Z3zSV/rTuX/ouI4lNdlfTxgdafQJiLzyNRB8= +github.com/anacrolix/generics v0.1.1-0.20251125230353-15d98d46693b h1:Kuvx/A/TTJuT9x8mn7DeGx2KW9tWn1LI8bira67xdT0= +github.com/anacrolix/generics v0.1.1-0.20251125230353-15d98d46693b/go.mod h1:NGehhfeXJPBujPx0s6cstSj8B+TERsTY32Xckfx5ftc= github.com/anacrolix/go-libutp v1.3.2 h1:WswiaxTIogchbkzNgGHuHRfbrYLpv4o290mlvcx+++M= github.com/anacrolix/go-libutp v1.3.2/go.mod h1:fCUiEnXJSe3jsPG554A200Qv+45ZzIIyGEvE56SHmyA= github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= github.com/anacrolix/log v0.14.2/go.mod h1:1OmJESOtxQGNMlUO5rcv96Vpp9mfMqXXbe2RdinFLdY= -github.com/anacrolix/log v0.16.0 h1:DSuyb5kAJwl3Y0X1TRcStVrTS9ST9b0BHW+7neE4Xho= -github.com/anacrolix/log v0.16.0/go.mod h1:m0poRtlr41mriZlXBQ9SOVZ8yZBkLjOkDhd5Li5pITA= -github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62 h1:P04VG6Td13FHMgS5ZBcJX23NPC/fiC4cp9bXwYujdYM= +github.com/anacrolix/log v0.17.1-0.20251118025802-918f1157b7bb h1:nGNLCQbxFQZz7/9PXLGQ9GmavI/W+eX66pSwVeUwugU= +github.com/anacrolix/log v0.17.1-0.20251118025802-918f1157b7bb/go.mod h1:YjBZbwe2v3RsU7WdoBlVSPVpfKuOAno9SRQ/8tIl+hk= github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM= +github.com/anacrolix/lsan v0.1.0 h1:TbgB8fdVXgBwrNsJGHtht9+9FepNFu5H7dU8ek6XYAY= +github.com/anacrolix/lsan v0.1.0/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM= github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s= github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= @@ -58,8 +59,8 @@ github.com/anacrolix/missinggo/perf v1.0.0 h1:7ZOGYziGEBytW49+KmYGTaNfnwUqP1HBsy github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ= github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY= github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA= -github.com/anacrolix/missinggo/v2 v2.8.0 h1:6pGnVOlR6TWL9JM5Msyezij8YHU3+oHO7r82Eql/kpA= -github.com/anacrolix/missinggo/v2 v2.8.0/go.mod h1:vVO5FEziQm+NFmJesc7StpkquZk+WJFCaL0Wp//2sa0= +github.com/anacrolix/missinggo/v2 v2.10.0 h1:pg0iO4Z/UhP2MAnmGcaMtp5ZP9kyWsusENWN9aolrkY= +github.com/anacrolix/missinggo/v2 v2.10.0/go.mod h1:nCRMW6bRCMOVcw5z9BnSYKF+kDbtenx+hQuphf4bK8Y= github.com/anacrolix/mmsg v1.0.1/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc= github.com/anacrolix/mmsg v1.1.1 h1:4ce/3I5kM7qSF6T5A8MOmDCfac3UqYlk5Bzh5XsWebM= github.com/anacrolix/mmsg v1.1.1/go.mod h1:lPCXEN1eDDQtKktdKEzdw+roswx6wWPpeXAl/WpWVDU= @@ -70,13 +71,13 @@ github.com/anacrolix/stm v0.5.0 h1:9df1KBpttF0TzLgDq51Z+TEabZKMythqgx89f1FQJt8= github.com/anacrolix/stm v0.5.0/go.mod h1:MOwrSy+jCm8Y7HYfMAwPj7qWVu7XoVvjOiYwJmpeB/M= github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk= github.com/anacrolix/sync v0.3.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= -github.com/anacrolix/sync v0.5.4 h1:yXZLIjXh/G+Rh2mYGCAPmszmF/fvEPadDy7/pPChpKM= -github.com/anacrolix/sync v0.5.4/go.mod h1:21cUWerw9eiu/3T3kyoChu37AVO+YFue1/H15qqubS0= +github.com/anacrolix/sync v0.5.5-0.20251119100342-d78dd1f686f1 h1:oLCfNgEOR3/Z98mSwmwTM1pcqCDb/1zIjxCNn7dzVaE= +github.com/anacrolix/sync v0.5.5-0.20251119100342-d78dd1f686f1/go.mod h1:21cUWerw9eiu/3T3kyoChu37AVO+YFue1/H15qqubS0= github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= -github.com/anacrolix/torrent v1.58.1 h1:6FP+KH57b1gyT2CpVL9fEqf9MGJEgh3xw1VA8rI0pW8= -github.com/anacrolix/torrent v1.58.1/go.mod h1:/7ZdLuHNKgtCE1gjYJCfbtG9JodBcDaF5ip5EUWRtk8= +github.com/anacrolix/torrent v1.61.0 h1:vxo+B4SwnoP5AQWbhvnTYIaTgPSX+llYUVuQVsN4Jg8= +github.com/anacrolix/torrent v1.61.0/go.mod h1:yKUKuZSSDdyOsCbuH+rDOpswl/g546gICapdrU7aUmQ= github.com/anacrolix/upnp v0.1.4 h1:+2t2KA6QOhm/49zeNyeVwDu1ZYS9dB9wfxyVvh/wk7U= github.com/anacrolix/upnp v0.1.4/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984+9fIic= github.com/anacrolix/utp v0.2.0 h1:65Cdmr6q9WSw2KsM+rtJFu7rqDzLl2bdysf4KlNPcFI= @@ -299,8 +300,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 h1:18kd+8ZUlt/ARXhljq+14TwAoKa61q6dX8jtwOf6DH8= @@ -328,10 +329,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tidwall/btree v1.8.0 h1:kHHy8hSBauQUe0KPHMFLOt0olAj1nDnkHPJhr8+HFkM= -github.com/tidwall/btree v1.8.0/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA= +github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= @@ -346,27 +347,25 @@ go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= +golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -377,8 +376,8 @@ golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -396,8 +395,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -411,8 +408,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -436,8 +432,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -453,11 +447,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -468,8 +461,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/go/server.go b/go/server.go index f5322bc5..32454476 100644 --- a/go/server.go +++ b/go/server.go @@ -33,14 +33,20 @@ var ( ) const ( - maxJSONBodyBytes int64 = 1 << 20 - maxTorrentUploadSize int64 = 100 << 20 - minStreamReadahead = 1 << 20 - baseStreamReadahead = 4 << 20 - maxStreamReadahead = 32 << 20 - seekBoostThreshold = 4 << 20 - seekBoostWindow = 32 << 20 - seekBoostReadahead = 16 << 20 + maxJSONBodyBytes int64 = 1 << 20 + maxTorrentUploadSize int64 = 100 << 20 + minStreamReadahead = 1 << 20 + baseStreamReadahead = 4 << 20 + maxStreamReadahead = 32 << 20 + seekBoostThreshold = 4 << 20 + seekBoostWindow = 32 << 20 + seekBoostReadahead = 16 << 20 + httpConnectTimeout = 5 * time.Second + httpKeepAlive = 30 * time.Second + httpIdleConnTimeout = 90 * time.Second + httpTLSHandshakeTimeout = 5 * time.Second + httpResponseHeaderTimeout = 15 * time.Second + httpClientTimeout = 45 * time.Second ) // WorkerPool manages a pool of goroutines for concurrent tasks @@ -140,6 +146,12 @@ func Start(config *Config) (int, error) { torrentcliCfg = torrent.NewDefaultClientConfig() torrentcliCfg.DataDir = sanitizedConfig.Path + torrentHTTPTransport := newTorrentHTTPTransport() + torrentcliCfg.WebTransport = torrentHTTPTransport + torrentcliCfg.MetainfoSourcesClient = &http.Client{ + Transport: torrentHTTPTransport, + Timeout: httpClientTimeout, + } // Performance optimizations torrentcliCfg.DisableUTP = false @@ -147,10 +159,13 @@ func Start(config *Config) (int, error) { torrentcliCfg.NoDefaultPortForwarding = false torrentcliCfg.DisablePEX = false torrentcliCfg.AcceptPeerConnections = true + torrentcliCfg.DropDuplicatePeerIds = true torrentcliCfg.EstablishedConnsPerTorrent = 80 torrentcliCfg.HalfOpenConnsPerTorrent = 25 torrentcliCfg.TorrentPeersHighWater = 200 torrentcliCfg.TorrentPeersLowWater = 50 + torrentcliCfg.HandshakesTimeout = 8 * time.Second + torrentcliCfg.PieceHashersPerTorrent = pieceHashersPerTorrent() torrentcliCfg.Seed = false log.Printf("[INFO] Download directory: %s", torrentcliCfg.DataDir) @@ -230,6 +245,35 @@ func configureCORS() *cors.Cors { }) } +func pieceHashersPerTorrent() int { + workers := runtime.NumCPU() / 2 + if workers < 2 { + workers = 2 + } + if workers > 8 { + workers = 8 + } + return workers +} + +func newTorrentHTTPTransport() *http.Transport { + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: httpConnectTimeout, + KeepAlive: httpKeepAlive, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 128, + MaxIdleConnsPerHost: 32, + MaxConnsPerHost: 64, + IdleConnTimeout: httpIdleConnTimeout, + TLSHandshakeTimeout: httpTLSHandshakeTimeout, + ResponseHeaderTimeout: httpResponseHeaderTimeout, + ExpectContinueTimeout: time.Second, + } +} + func setupGracefulShutdown(server *http.Server) { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) @@ -573,7 +617,7 @@ func getInfoWithTimeout(t *torrent.Torrent, timeout time.Duration) error { } } -func initMagnet(w http.ResponseWriter, magnet string, alldn, alltr []string) *torrent.Torrent { +func initMagnet(w http.ResponseWriter, magnet string, alldn, alltr, sources, webseeds []string) *torrent.Torrent { var sb strings.Builder sb.WriteString(magnet) @@ -599,10 +643,70 @@ func initMagnet(w http.ResponseWriter, magnet string, alldn, alltr []string) *to // Cache metadata cacheTorrentMetadata(t) + applyTorrentFallbacks(t, sources, webseeds) return t } +func normalizeHTTPURLs(values []string) []string { + if len(values) == 0 { + return nil + } + + urls := make([]string, 0, len(values)) + seen := make(map[string]struct{}, len(values)) + for _, value := range values { + value = strings.TrimSpace(value) + if value == "" { + continue + } + + parsedURL, err := url.Parse(value) + if err != nil { + continue + } + if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" { + continue + } + + normalizedURL := parsedURL.String() + if _, exists := seen[normalizedURL]; exists { + continue + } + seen[normalizedURL] = struct{}{} + urls = append(urls, normalizedURL) + } + + return urls +} + +func applyTorrentFallbacks(t *torrent.Torrent, sources, webseeds []string) { + if t == nil { + return + } + + normalizedSources := normalizeHTTPURLs(sources) + if len(normalizedSources) > 0 { + t.AddSources(normalizedSources) + } + + normalizedWebseeds := normalizeHTTPURLs(webseeds) + if len(normalizedWebseeds) > 0 { + t.AddWebSeeds(normalizedWebseeds) + } +} + +func collectTorrentFallbacks(values url.Values) ([]string, []string) { + sources := append([]string{}, values["source"]...) + sources = append(sources, values["xs"]...) + sources = append(sources, values["as"]...) + + webseeds := append([]string{}, values["webseed"]...) + webseeds = append(webseeds, values["ws"]...) + + return sources, webseeds +} + func getTorrent(w http.ResponseWriter, infoHash string) *torrent.Torrent { if len(infoHash) != 40 { httpJSONError(w, "InfoHash not valid", http.StatusBadRequest) @@ -750,7 +854,7 @@ func addMagnet(w http.ResponseWriter, r *http.Request) { return } - t := initMagnet(w, amBody.Magnet, []string{}, []string{}) + t := initMagnet(w, amBody.Magnet, []string{}, []string{}, amBody.Sources, amBody.Webseeds) if t == nil { return } @@ -824,6 +928,7 @@ func streamTorrent(w http.ResponseWriter, r *http.Request) { tFile.SetPriority(torrent.PiecePriorityHigh) fileReader := tFile.NewReader() + fileReader.SetContext(r.Context()) streamReader := configureStreamReader(fileReader, tFile.Length(), r.Header.Get("Range") != "") defer streamReader.Close() @@ -1176,6 +1281,7 @@ func AddTorrent(w http.ResponseWriter, r *http.Request) { // Cache metadata cacheTorrentMetadata(torrent) + applyTorrentFallbacks(torrent, r.MultipartForm.Value["source"], r.MultipartForm.Value["webseed"]) w.Header().Set("Content-Type", "text/plain") w.Write([]byte(torrent.InfoHash().HexString())) @@ -1191,6 +1297,7 @@ func playTorrent(w http.ResponseWriter, r *http.Request) { magnet, magOk := r.URL.Query()["magnet"] displayName := r.URL.Query()["dn"] trackers := r.URL.Query()["tr"] + sources, webseeds := collectTorrentFallbacks(r.URL.Query()) files, fOk := r.URL.Query()["file"] if !magOk && !ihOk { @@ -1200,11 +1307,12 @@ func playTorrent(w http.ResponseWriter, r *http.Request) { var t *torrent.Torrent if magOk && !ihOk { - t = initMagnet(w, magnet[0], displayName, trackers) + t = initMagnet(w, magnet[0], displayName, trackers, sources, webseeds) } if ihOk && !magOk { t = getTorrent(w, infoHash[0]) + applyTorrentFallbacks(t, sources, webseeds) } if t == nil { @@ -1244,6 +1352,8 @@ type addMagnetBody struct { Magnet string AllFiles bool Files []string + Sources []string + Webseeds []string } type addMagnetRes struct { diff --git a/go/server_test.go b/go/server_test.go index 742eb74a..1270efea 100644 --- a/go/server_test.go +++ b/go/server_test.go @@ -5,6 +5,7 @@ import ( "context" "io" "net/http/httptest" + "net/url" "strings" "testing" @@ -15,6 +16,7 @@ type fakeTorrentReader struct { pos int64 responsive bool readaheadFunc torrent.ReadaheadFunc + ctx context.Context } func (f *fakeTorrentReader) Read(p []byte) (int, error) { @@ -38,6 +40,10 @@ func (f *fakeTorrentReader) ReadContext(_ context.Context, p []byte) (int, error return f.Read(p) } +func (f *fakeTorrentReader) SetContext(ctx context.Context) { + f.ctx = ctx +} + func (f *fakeTorrentReader) SetReadahead(_ int64) {} func (f *fakeTorrentReader) SetReadaheadFunc(fn torrent.ReadaheadFunc) { @@ -117,6 +123,43 @@ func TestIsAllowedOriginOnlyAcceptsLocalOrigins(t *testing.T) { } } +func TestNewTorrentHTTPTransportIsTunedForFallbacks(t *testing.T) { + transport := newTorrentHTTPTransport() + if transport == nil { + t.Fatal("expected a transport instance") + } + if !transport.ForceAttemptHTTP2 { + t.Fatal("expected HTTP/2 to be enabled") + } + if transport.MaxIdleConnsPerHost < 16 { + t.Fatalf("expected connection pooling per host, got %d", transport.MaxIdleConnsPerHost) + } + if transport.ResponseHeaderTimeout != httpResponseHeaderTimeout { + t.Fatalf("expected response header timeout %s, got %s", httpResponseHeaderTimeout, transport.ResponseHeaderTimeout) + } +} + +func TestCollectTorrentFallbacksIncludesLegacyMagnetParams(t *testing.T) { + values, err := url.ParseQuery( + "magnet=magnet%3A%3Fxt%3Durn%3Abtih%3Aabc123%26dn%3DExample&xs=https%3A%2F%2Fmeta.example%2Fmovie.torrent&as=https%3A%2F%2Fbackup.example%2Fmovie.torrent&ws=https%3A%2F%2Fcdn.example%2Fmovie.mkv&source=https%3A%2F%2Fexplicit.example%2Fmovie.torrent&webseed=https%3A%2F%2Fexplicit-cdn.example%2Fmovie.mkv", + ) + if err != nil { + t.Fatalf("ParseQuery failed: %v", err) + } + + sources, webseeds := collectTorrentFallbacks(values) + + if len(sources) != 3 { + t.Fatalf("expected 3 sources, got %d: %#v", len(sources), sources) + } + if len(webseeds) != 2 { + t.Fatalf("expected 2 webseeds, got %d: %#v", len(webseeds), webseeds) + } + if values.Get("magnet") != "magnet:?xt=urn:btih:abc123&dn=Example" { + t.Fatalf("expected magnet payload to stay intact, got %q", values.Get("magnet")) + } +} + func TestConfigureStreamReaderBoostsReadaheadAfterLargeSeek(t *testing.T) { fakeReader := &fakeTorrentReader{} streamReader := configureStreamReader(fakeReader, 1<<30, true) @@ -128,6 +171,12 @@ func TestConfigureStreamReaderBoostsReadaheadAfterLargeSeek(t *testing.T) { t.Fatal("expected stream reader to install a readahead function") } + ctx := context.Background() + fakeReader.SetContext(ctx) + if fakeReader.ctx != ctx { + t.Fatal("expected reader context to be stored") + } + baseReadahead := fakeReader.readaheadFunc(torrent.ReadaheadContext{}) if _, err := streamReader.Seek(32<<20, io.SeekStart); err != nil { t.Fatalf("seek failed: %v", err) diff --git a/ios/Frameworks/Libmtorrentserver.xcframework/Info.plist b/ios/Frameworks/Libmtorrentserver.xcframework/Info.plist index 37272fd9..24afd6a3 100644 --- a/ios/Frameworks/Libmtorrentserver.xcframework/Info.plist +++ b/ios/Frameworks/Libmtorrentserver.xcframework/Info.plist @@ -4,6 +4,20 @@ AvailableLibraries + + BinaryPath + Libmtorrentserver.framework/Libmtorrentserver + LibraryIdentifier + ios-arm64 + LibraryPath + Libmtorrentserver.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + BinaryPath Libmtorrentserver.framework/Libmtorrentserver @@ -21,20 +35,6 @@ SupportedPlatformVariant simulator - - BinaryPath - Libmtorrentserver.framework/Libmtorrentserver - LibraryIdentifier - ios-arm64 - LibraryPath - Libmtorrentserver.framework - SupportedArchitectures - - arm64 - - SupportedPlatform - ios - CFBundlePackageType XFWK diff --git a/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64/Libmtorrentserver.framework/Info.plist b/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64/Libmtorrentserver.framework/Info.plist index 94f6cf96..55869d95 100644 --- a/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64/Libmtorrentserver.framework/Info.plist +++ b/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64/Libmtorrentserver.framework/Info.plist @@ -9,9 +9,9 @@ MinimumOSVersion 100.0 CFBundleShortVersionString - 0.0.1776077599 + 0.0.1776080429 CFBundleVersion - 0.0.1776077599 + 0.0.1776080429 CFBundlePackageType FMWK diff --git a/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64/Libmtorrentserver.framework/Libmtorrentserver b/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64/Libmtorrentserver.framework/Libmtorrentserver index e6bfadad..960acacf 100644 Binary files a/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64/Libmtorrentserver.framework/Libmtorrentserver and b/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64/Libmtorrentserver.framework/Libmtorrentserver differ diff --git a/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64_x86_64-simulator/Libmtorrentserver.framework/Info.plist b/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64_x86_64-simulator/Libmtorrentserver.framework/Info.plist index 94f6cf96..55869d95 100644 --- a/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64_x86_64-simulator/Libmtorrentserver.framework/Info.plist +++ b/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64_x86_64-simulator/Libmtorrentserver.framework/Info.plist @@ -9,9 +9,9 @@ MinimumOSVersion 100.0 CFBundleShortVersionString - 0.0.1776077599 + 0.0.1776080429 CFBundleVersion - 0.0.1776077599 + 0.0.1776080429 CFBundlePackageType FMWK diff --git a/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64_x86_64-simulator/Libmtorrentserver.framework/Libmtorrentserver b/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64_x86_64-simulator/Libmtorrentserver.framework/Libmtorrentserver index 944d7ae5..ed3fe899 100644 Binary files a/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64_x86_64-simulator/Libmtorrentserver.framework/Libmtorrentserver and b/ios/Frameworks/Libmtorrentserver.xcframework/ios-arm64_x86_64-simulator/Libmtorrentserver.framework/Libmtorrentserver differ diff --git a/lib/services/torrent_server.dart b/lib/services/torrent_server.dart index 065e2372..ffacd92c 100644 --- a/lib/services/torrent_server.dart +++ b/lib/services/torrent_server.dart @@ -11,6 +11,56 @@ import 'package:mangayomi/services/http/m_client.dart'; import 'package:mangayomi/utils/extensions/string_extensions.dart'; import 'package:mangayomi/ffi/torrent_server_ffi.dart' as libmtorrentserver_ffi; +String _buildQueryString(Map> parameters) { + final segments = []; + parameters.forEach((key, values) { + for (final value in values) { + segments.add( + '${Uri.encodeQueryComponent(key)}=${Uri.encodeQueryComponent(value)}', + ); + } + }); + return segments.join('&'); +} + +List _normalizeHttpUrls(Iterable values) { + final normalized = []; + final seen = {}; + for (final value in values) { + final trimmed = value.trim(); + if (trimmed.isEmpty) continue; + final uri = Uri.tryParse(trimmed); + if (uri == null) continue; + if (uri.scheme != 'http' && uri.scheme != 'https') continue; + final normalizedValue = uri.toString(); + if (seen.add(normalizedValue)) { + normalized.add(normalizedValue); + } + } + return normalized; +} + +(List, List) _extractMagnetFallbacks(String magnetUrl) { + final uri = Uri.tryParse(magnetUrl); + if (uri == null || uri.scheme != 'magnet') { + return (const [], const []); + } + + final sources = _normalizeHttpUrls([ + ...uri.queryParametersAll['xs'] ?? const [], + ...uri.queryParametersAll['as'] ?? const [], + ]); + final webseeds = _normalizeHttpUrls(uri.queryParametersAll['ws'] ?? const []); + return (sources, webseeds); +} + +List _mergeTorrentFallbacks( + Iterable base, + Iterable discovered, +) { + return _normalizeHttpUrls([...base, ...discovered]); +} + class MTorrentServer { final http = MClient.init(); Future removeTorrent(String? inforHash) async { @@ -41,7 +91,12 @@ class MTorrentServer { } } - Future getInfohash(String url, bool isFilePath) async { + Future getInfohash( + String url, + bool isFilePath, { + List sources = const [], + List webseeds = const [], + }) async { try { final torrentByte = isFilePath ? File(url).readAsBytesSync() @@ -54,6 +109,10 @@ class MTorrentServer { request.files.add( MultipartFile.fromBytes('file', torrentByte, filename: 'file.torrent'), ); + request.fields.addAll({ + for (final source in sources) 'source': source, + for (final webseed in webseeds) 'webseed': webseed, + }); final response = await http.send(request); return await response.stream.bytesToString(); } catch (e) { @@ -63,8 +122,10 @@ class MTorrentServer { Future<(List