diff --git a/.github/update.log b/.github/update.log index 8d353792ff..ea7189dbe5 100644 --- a/.github/update.log +++ b/.github/update.log @@ -918,3 +918,4 @@ Update On Sat Feb 15 19:30:59 CET 2025 Update On Sun Feb 16 19:32:22 CET 2025 Update On Mon Feb 17 19:35:15 CET 2025 Update On Tue Feb 18 19:34:22 CET 2025 +Update On Wed Feb 19 19:34:28 CET 2025 diff --git a/brook/ping/ping.json b/brook/ping/ping.json index 22fcc96f7f..90a4d7edfe 100644 --- a/brook/ping/ping.json +++ b/brook/ping/ping.json @@ -1,7 +1,7 @@ { "version": "20250202", - "text": "Zhi - A Zero-Trust End-to-End Encrypted Instant Messaging App", - "link": "https://www.txthinking.com/zhi.html", - "text_zh": "生成自用的中国域名直连模块", + "text": "Tip: 如何生成自用的中国域名直连模块", + "link": "https://www.txthinking.com/talks/articles/china-list.article", + "text_zh": "Tip: 如何生成自用的中国域名直连模块", "link_zh": "https://www.txthinking.com/talks/articles/china-list.article" } diff --git a/clash-meta-android/.github/workflows/build-debug.yaml b/clash-meta-android/.github/workflows/build-debug.yaml index 0474715b1e..24262d6ee0 100644 --- a/clash-meta-android/.github/workflows/build-debug.yaml +++ b/clash-meta-android/.github/workflows/build-debug.yaml @@ -29,7 +29,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - uses: actions/cache@v4 with: diff --git a/clash-meta-android/.github/workflows/build-pre-release.yaml b/clash-meta-android/.github/workflows/build-pre-release.yaml index e0e04cf66e..c712bb59fc 100644 --- a/clash-meta-android/.github/workflows/build-pre-release.yaml +++ b/clash-meta-android/.github/workflows/build-pre-release.yaml @@ -28,7 +28,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - uses: actions/cache@v4 with: diff --git a/clash-meta-android/.github/workflows/build-release.yaml b/clash-meta-android/.github/workflows/build-release.yaml index 04ccd5fd6f..ca6d9bc711 100644 --- a/clash-meta-android/.github/workflows/build-release.yaml +++ b/clash-meta-android/.github/workflows/build-release.yaml @@ -31,7 +31,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - uses: actions/cache@v4 with: diff --git a/clash-meta-android/.github/workflows/update-dependencies.yaml b/clash-meta-android/.github/workflows/update-dependencies.yaml index 2624f5e10a..76655d4d73 100644 --- a/clash-meta-android/.github/workflows/update-dependencies.yaml +++ b/clash-meta-android/.github/workflows/update-dependencies.yaml @@ -24,7 +24,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - uses: actions/cache@v4 with: diff --git a/clash-meta-android/core/src/foss/golang/clash/go.mod b/clash-meta-android/core/src/foss/golang/clash/go.mod index ee9a209247..1eb79004b5 100644 --- a/clash-meta-android/core/src/foss/golang/clash/go.mod +++ b/clash-meta-android/core/src/foss/golang/clash/go.mod @@ -23,6 +23,7 @@ require ( github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 github.com/metacubex/randv2 v0.2.0 + github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 @@ -40,7 +41,6 @@ require ( github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/sing v0.5.1 github.com/sagernet/sing-mux v0.2.1 github.com/sagernet/sing-shadowtls v0.1.5 diff --git a/clash-meta-android/core/src/foss/golang/clash/go.sum b/clash-meta-android/core/src/foss/golang/clash/go.sum index 82fa5d50b6..7e485eacb1 100644 --- a/clash-meta-android/core/src/foss/golang/clash/go.sum +++ b/clash-meta-android/core/src/foss/golang/clash/go.sum @@ -109,6 +109,8 @@ github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/j github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA= @@ -168,8 +170,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo= github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE= github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo= diff --git a/clash-meta-android/core/src/foss/golang/clash/listener/anytls/server.go b/clash-meta-android/core/src/foss/golang/clash/listener/anytls/server.go index f89f2277a2..31a7c55ae9 100644 --- a/clash-meta-android/core/src/foss/golang/clash/listener/anytls/server.go +++ b/clash-meta-android/core/src/foss/golang/clash/listener/anytls/server.go @@ -135,6 +135,8 @@ func (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) { defer conn.Close() b := buf.NewPacket() + defer b.Release() + _, err := b.ReadOnceFrom(conn) if err != nil { return @@ -177,6 +179,6 @@ func (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) { Destination: destination, }) }, &l.padding) - session.Run(true) + session.Run() session.Close() } diff --git a/clash-meta-android/core/src/foss/golang/clash/listener/reality/reality.go b/clash-meta-android/core/src/foss/golang/clash/listener/reality/reality.go index 16245e2bb7..f629f2b4fe 100644 --- a/clash-meta-android/core/src/foss/golang/clash/listener/reality/reality.go +++ b/clash-meta-android/core/src/foss/golang/clash/listener/reality/reality.go @@ -12,7 +12,7 @@ import ( "github.com/metacubex/mihomo/listener/inner" "github.com/metacubex/mihomo/ntp" - "github.com/sagernet/reality" + "github.com/metacubex/reality" ) type Conn = reality.Conn diff --git a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/client.go b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/client.go index fd573ebe79..19776df97c 100644 --- a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/client.go +++ b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/client.go @@ -71,6 +71,8 @@ func (c *Client) CreateOutboundTLSConnection(ctx context.Context) (net.Conn, err } b := buf.NewPacket() + defer b.Release() + b.Write(c.passwordSha256) var paddingLen int if pad := c.padding.Load().GenerateRecordPayloadSizes(0); len(pad) > 0 { diff --git a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/pipe/deadline.go b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/pipe/deadline.go new file mode 100644 index 0000000000..29c4ec0ab7 --- /dev/null +++ b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/pipe/deadline.go @@ -0,0 +1,74 @@ +package pipe + +import ( + "sync" + "time" +) + +// PipeDeadline is an abstraction for handling timeouts. +type PipeDeadline struct { + mu sync.Mutex // Guards timer and cancel + timer *time.Timer + cancel chan struct{} // Must be non-nil +} + +func MakePipeDeadline() PipeDeadline { + return PipeDeadline{cancel: make(chan struct{})} +} + +// Set sets the point in time when the deadline will time out. +// A timeout event is signaled by closing the channel returned by waiter. +// Once a timeout has occurred, the deadline can be refreshed by specifying a +// t value in the future. +// +// A zero value for t prevents timeout. +func (d *PipeDeadline) Set(t time.Time) { + d.mu.Lock() + defer d.mu.Unlock() + + if d.timer != nil && !d.timer.Stop() { + <-d.cancel // Wait for the timer callback to finish and close cancel + } + d.timer = nil + + // Time is zero, then there is no deadline. + closed := isClosedChan(d.cancel) + if t.IsZero() { + if closed { + d.cancel = make(chan struct{}) + } + return + } + + // Time in the future, setup a timer to cancel in the future. + if dur := time.Until(t); dur > 0 { + if closed { + d.cancel = make(chan struct{}) + } + d.timer = time.AfterFunc(dur, func() { + close(d.cancel) + }) + return + } + + // Time in the past, so close immediately. + if !closed { + close(d.cancel) + } +} + +// Wait returns a channel that is closed when the deadline is exceeded. +func (d *PipeDeadline) Wait() chan struct{} { + d.mu.Lock() + defer d.mu.Unlock() + return d.cancel +} + +func isClosedChan(c <-chan struct{}) bool { + select { + case <-c: + return true + default: + return false + } +} diff --git a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/pipe/io_pipe.go b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/pipe/io_pipe.go new file mode 100644 index 0000000000..5d0fd252ec --- /dev/null +++ b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/pipe/io_pipe.go @@ -0,0 +1,232 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pipe adapter to connect code expecting an io.Reader +// with code expecting an io.Writer. + +package pipe + +import ( + "io" + "os" + "sync" + "time" +) + +// onceError is an object that will only store an error once. +type onceError struct { + sync.Mutex // guards following + err error +} + +func (a *onceError) Store(err error) { + a.Lock() + defer a.Unlock() + if a.err != nil { + return + } + a.err = err +} +func (a *onceError) Load() error { + a.Lock() + defer a.Unlock() + return a.err +} + +// A pipe is the shared pipe structure underlying PipeReader and PipeWriter. +type pipe struct { + wrMu sync.Mutex // Serializes Write operations + wrCh chan []byte + rdCh chan int + + once sync.Once // Protects closing done + done chan struct{} + rerr onceError + werr onceError + + readDeadline PipeDeadline + writeDeadline PipeDeadline +} + +func (p *pipe) read(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.readCloseError() + case <-p.readDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + default: + } + + select { + case bw := <-p.wrCh: + nr := copy(b, bw) + p.rdCh <- nr + return nr, nil + case <-p.done: + return 0, p.readCloseError() + case <-p.readDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + } +} + +func (p *pipe) closeRead(err error) error { + if err == nil { + err = io.ErrClosedPipe + } + p.rerr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +func (p *pipe) write(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.writeCloseError() + case <-p.writeDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + default: + p.wrMu.Lock() + defer p.wrMu.Unlock() + } + + for once := true; once || len(b) > 0; once = false { + select { + case p.wrCh <- b: + nw := <-p.rdCh + b = b[nw:] + n += nw + case <-p.done: + return n, p.writeCloseError() + case <-p.writeDeadline.Wait(): + return n, os.ErrDeadlineExceeded + } + } + return n, nil +} + +func (p *pipe) closeWrite(err error) error { + if err == nil { + err = io.EOF + } + p.werr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +// readCloseError is considered internal to the pipe type. +func (p *pipe) readCloseError() error { + rerr := p.rerr.Load() + if werr := p.werr.Load(); rerr == nil && werr != nil { + return werr + } + return io.ErrClosedPipe +} + +// writeCloseError is considered internal to the pipe type. +func (p *pipe) writeCloseError() error { + werr := p.werr.Load() + if rerr := p.rerr.Load(); werr == nil && rerr != nil { + return rerr + } + return io.ErrClosedPipe +} + +// A PipeReader is the read half of a pipe. +type PipeReader struct{ pipe } + +// Read implements the standard Read interface: +// it reads data from the pipe, blocking until a writer +// arrives or the write end is closed. +// If the write end is closed with an error, that error is +// returned as err; otherwise err is EOF. +func (r *PipeReader) Read(data []byte) (n int, err error) { + return r.pipe.read(data) +} + +// Close closes the reader; subsequent writes to the +// write half of the pipe will return the error [ErrClosedPipe]. +func (r *PipeReader) Close() error { + return r.CloseWithError(nil) +} + +// CloseWithError closes the reader; subsequent writes +// to the write half of the pipe will return the error err. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (r *PipeReader) CloseWithError(err error) error { + return r.pipe.closeRead(err) +} + +// A PipeWriter is the write half of a pipe. +type PipeWriter struct{ r PipeReader } + +// Write implements the standard Write interface: +// it writes data to the pipe, blocking until one or more readers +// have consumed all the data or the read end is closed. +// If the read end is closed with an error, that err is +// returned as err; otherwise err is [ErrClosedPipe]. +func (w *PipeWriter) Write(data []byte) (n int, err error) { + return w.r.pipe.write(data) +} + +// Close closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and EOF. +func (w *PipeWriter) Close() error { + return w.CloseWithError(nil) +} + +// CloseWithError closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and the error err, +// or EOF if err is nil. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (w *PipeWriter) CloseWithError(err error) error { + return w.r.pipe.closeWrite(err) +} + +// Pipe creates a synchronous in-memory pipe. +// It can be used to connect code expecting an [io.Reader] +// with code expecting an [io.Writer]. +// +// Reads and Writes on the pipe are matched one to one +// except when multiple Reads are needed to consume a single Write. +// That is, each Write to the [PipeWriter] blocks until it has satisfied +// one or more Reads from the [PipeReader] that fully consume +// the written data. +// The data is copied directly from the Write to the corresponding +// Read (or Reads); there is no internal buffering. +// +// It is safe to call Read and Write in parallel with each other or with Close. +// Parallel calls to Read and parallel calls to Write are also safe: +// the individual calls will be gated sequentially. +// +// Added SetReadDeadline and SetWriteDeadline methods based on `io.Pipe`. +func Pipe() (*PipeReader, *PipeWriter) { + pw := &PipeWriter{r: PipeReader{pipe: pipe{ + wrCh: make(chan []byte), + rdCh: make(chan int), + done: make(chan struct{}), + readDeadline: MakePipeDeadline(), + writeDeadline: MakePipeDeadline(), + }}} + return &pw.r, pw +} + +func (p *PipeReader) SetReadDeadline(t time.Time) error { + if isClosedChan(p.done) { + return io.ErrClosedPipe + } + p.readDeadline.Set(t) + return nil +} + +func (p *PipeWriter) SetWriteDeadline(t time.Time) error { + if isClosedChan(p.r.done) { + return io.ErrClosedPipe + } + p.r.writeDeadline.Set(t) + return nil +} diff --git a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/client.go b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/client.go index 869a4b317a..2312a6ff56 100644 --- a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/client.go +++ b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/client.go @@ -123,7 +123,7 @@ func (c *Client) createSession(ctx context.Context) (*Session, error) { c.idleSession.Remove(math.MaxUint64 - session.seq) c.idleSessionLock.Unlock() } - session.Run(false) + session.Run() return session, nil } diff --git a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/session.go b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/session.go index ce4d580f20..963533ead1 100644 --- a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/session.go +++ b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/session.go @@ -36,10 +36,11 @@ type Session struct { padding *atomic.TypedValue[*padding.PaddingFactory] // client - isClient bool - buffering bool - buffer []byte - pktCounter atomic.Uint32 + isClient bool + sendPadding bool + buffering bool + buffer []byte + pktCounter atomic.Uint32 // server onNewStream func(stream *Stream) @@ -47,9 +48,10 @@ type Session struct { func NewClientSession(conn net.Conn, _padding *atomic.TypedValue[*padding.PaddingFactory]) *Session { s := &Session{ - conn: conn, - isClient: true, - padding: _padding, + conn: conn, + isClient: true, + sendPadding: true, + padding: _padding, } s.die = make(chan struct{}) s.streams = make(map[uint32]*Stream) @@ -60,7 +62,6 @@ func NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding s := &Session{ conn: conn, onNewStream: onNewStream, - isClient: false, padding: _padding, } s.die = make(chan struct{}) @@ -68,8 +69,8 @@ func NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding return s } -func (s *Session) Run(isServer bool) { - if isServer { +func (s *Session) Run() { + if !s.isClient { s.recvLoop() return } @@ -257,19 +258,18 @@ func (s *Session) recvLoop() error { } case cmdUpdatePaddingScheme: if hdr.Length() > 0 { - buffer := pool.Get(int(hdr.Length())) - if _, err := io.ReadFull(s.conn, buffer); err != nil { - pool.Put(buffer) + // `rawScheme` Do not use buffer to prevent subsequent misuse + rawScheme := make([]byte, int(hdr.Length())) + if _, err := io.ReadFull(s.conn, rawScheme); err != nil { return err } if s.isClient { - if padding.UpdatePaddingScheme(buffer, s.padding) { - log.Infoln("[Update padding succeed] %x\n", md5.Sum(buffer)) + if padding.UpdatePaddingScheme(rawScheme, s.padding) { + log.Infoln("[Update padding succeed] %x\n", md5.Sum(rawScheme)) } else { - log.Warnln("[Update padding failed] %x\n", md5.Sum(buffer)) + log.Warnln("[Update padding failed] %x\n", md5.Sum(rawScheme)) } } - pool.Put(buffer) } default: // I don't know what command it is (can't have data) @@ -319,7 +319,7 @@ func (s *Session) writeConn(b []byte) (n int, err error) { } // calulate & send padding - if s.isClient { + if s.sendPadding { pkt := s.pktCounter.Add(1) paddingF := s.padding.Load() if pkt < paddingF.Stop { @@ -333,7 +333,6 @@ func (s *Session) writeConn(b []byte) (n int, err error) { continue } } - // logrus.Debugln(pkt, "write", l, "len", remainPayloadLen, "remain", remainPayloadLen-l) if remainPayloadLen > l { // this packet is all payload _, err = s.conn.Write(b[:l]) if err != nil { @@ -371,7 +370,12 @@ func (s *Session) writeConn(b []byte) (n int, err error) { // maybe still remain payload to write if len(b) == 0 { return + } else { + n2, err := s.conn.Write(b) + return n + n2, err } + } else { + s.sendPadding = false } } diff --git a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/stream.go b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/stream.go index 140396e486..9f21ff04b3 100644 --- a/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/stream.go +++ b/clash-meta-android/core/src/foss/golang/clash/transport/anytls/session/stream.go @@ -6,6 +6,8 @@ import ( "os" "sync" "time" + + "github.com/metacubex/mihomo/transport/anytls/pipe" ) // Stream implements net.Conn @@ -14,8 +16,9 @@ type Stream struct { sess *Session - pipeR *io.PipeReader - pipeW *io.PipeWriter + pipeR *pipe.PipeReader + pipeW *pipe.PipeWriter + writeDeadline pipe.PipeDeadline dieOnce sync.Once dieHook func() @@ -26,7 +29,8 @@ func newStream(id uint32, sess *Session) *Stream { s := new(Stream) s.id = id s.sess = sess - s.pipeR, s.pipeW = io.Pipe() + s.pipeR, s.pipeW = pipe.Pipe() + s.writeDeadline = pipe.MakePipeDeadline() return s } @@ -37,6 +41,11 @@ func (s *Stream) Read(b []byte) (n int, err error) { // Write implements net.Conn func (s *Stream) Write(b []byte) (n int, err error) { + select { + case <-s.writeDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + default: + } f := newFrame(cmdPSH, s.id) f.data = b n, err = s.sess.writeFrame(f) @@ -67,15 +76,17 @@ func (s *Stream) sessionClose() (once bool) { } func (s *Stream) SetReadDeadline(t time.Time) error { - return os.ErrNotExist + return s.pipeR.SetReadDeadline(t) } func (s *Stream) SetWriteDeadline(t time.Time) error { - return os.ErrNotExist + s.writeDeadline.Set(t) + return nil } func (s *Stream) SetDeadline(t time.Time) error { - return os.ErrNotExist + s.SetWriteDeadline(t) + return s.SetReadDeadline(t) } // LocalAddr satisfies net.Conn interface diff --git a/clash-meta-android/core/src/foss/golang/go.mod b/clash-meta-android/core/src/foss/golang/go.mod index 077d9280b0..696fc5c479 100644 --- a/clash-meta-android/core/src/foss/golang/go.mod +++ b/clash-meta-android/core/src/foss/golang/go.mod @@ -52,6 +52,7 @@ require ( github.com/metacubex/mihomo v1.7.0 // indirect github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 // indirect github.com/metacubex/randv2 v0.2.0 // indirect + github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 // indirect github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 // indirect github.com/metacubex/sing-shadowsocks v0.2.8 // indirect github.com/metacubex/sing-shadowsocks2 v0.2.2 // indirect @@ -76,7 +77,6 @@ require ( github.com/sagernet/fswatch v0.1.1 // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect - github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/sing v0.5.1 // indirect github.com/sagernet/sing-mux v0.2.1 // indirect github.com/sagernet/sing-shadowtls v0.1.5 // indirect diff --git a/clash-meta-android/core/src/foss/golang/go.sum b/clash-meta-android/core/src/foss/golang/go.sum index 5d8e453726..09c3105c28 100644 --- a/clash-meta-android/core/src/foss/golang/go.sum +++ b/clash-meta-android/core/src/foss/golang/go.sum @@ -109,6 +109,8 @@ github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/j github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA= @@ -167,8 +169,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo= github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE= github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo= diff --git a/clash-meta-android/core/src/main/golang/go.mod b/clash-meta-android/core/src/main/golang/go.mod index 96f64f6ec2..f3f69112f0 100644 --- a/clash-meta-android/core/src/main/golang/go.mod +++ b/clash-meta-android/core/src/main/golang/go.mod @@ -60,6 +60,7 @@ require ( github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a // indirect github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 // indirect github.com/metacubex/randv2 v0.2.0 // indirect + github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 // indirect github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 // indirect github.com/metacubex/sing-shadowsocks v0.2.8 // indirect github.com/metacubex/sing-shadowsocks2 v0.2.2 // indirect @@ -84,7 +85,6 @@ require ( github.com/sagernet/fswatch v0.1.1 // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect - github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/sing v0.5.1 // indirect github.com/sagernet/sing-mux v0.2.1 // indirect github.com/sagernet/sing-shadowtls v0.1.5 // indirect diff --git a/clash-meta-android/core/src/main/golang/go.sum b/clash-meta-android/core/src/main/golang/go.sum index 4a1acb93d2..e3627af5f9 100644 --- a/clash-meta-android/core/src/main/golang/go.sum +++ b/clash-meta-android/core/src/main/golang/go.sum @@ -110,6 +110,8 @@ github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/j github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA= @@ -168,8 +170,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo= github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE= github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo= diff --git a/clash-meta-android/core/src/main/java/com/github/kr328/clash/core/model/ConfigurationOverride.kt b/clash-meta-android/core/src/main/java/com/github/kr328/clash/core/model/ConfigurationOverride.kt index 3587a1d87a..7d3b222db6 100644 --- a/clash-meta-android/core/src/main/java/com/github/kr328/clash/core/model/ConfigurationOverride.kt +++ b/clash-meta-android/core/src/main/java/com/github/kr328/clash/core/model/ConfigurationOverride.kt @@ -179,8 +179,8 @@ data class ConfigurationOverride( @SerialName("enable") var enable: Boolean? = null, - @SerialName("sniffing") - var sniffing: List? = null, + @SerialName("sniff") + var sniff: Sniff = Sniff(), @SerialName("force-dns-mapping") var forceDnsMapping: Boolean? = null, @@ -197,8 +197,11 @@ data class ConfigurationOverride( @SerialName("skip-domain") var skipDomain: List? = null, - @SerialName("port-whitelist") - var portWhitelist: List? = null, + @SerialName("skip-src-address") + var skipSrcAddress: List? = null, + + @SerialName("skip-dst-address") + var skipDstAddress: List? = null, ) @Serializable @@ -222,6 +225,27 @@ data class ConfigurationOverride( var allowPrivateNetwork: Boolean? = null, ) + @Serializable + data class Sniff( + @SerialName("HTTP") + var http: ProtocolConig = ProtocolConig(), + + @SerialName("TLS") + var tls: ProtocolConig = ProtocolConig(), + + @SerialName("QUIC") + var quic: ProtocolConig = ProtocolConig(), + ) + + @Serializable + data class ProtocolConig( + @SerialName("ports") + var ports: List? = null, + + @SerialName("override-destination") + var overrideDestination: Boolean? = null, + ) + override fun writeToParcel(parcel: Parcel, flags: Int) { Parcelizer.encodeToParcel(serializer(), parcel, this) } diff --git a/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/MetaFeatureSettingsDesign.kt b/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/MetaFeatureSettingsDesign.kt index 0d674ca9f9..b24a1bb90b 100644 --- a/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/MetaFeatureSettingsDesign.kt +++ b/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/MetaFeatureSettingsDesign.kt @@ -137,13 +137,53 @@ class MetaFeatureSettingsDesign( } editableTextList( - value = configuration.sniffer::sniffing, + value = configuration.sniffer.sniff.http::ports, adapter = TextAdapter.String, - title = R.string.sniffing, + title = R.string.sniff_http_ports, placeholder = R.string.dont_modify, configure = snifferDependencies::add, ) + selectableList( + value = configuration.sniffer.sniff.http::overrideDestination, + values = booleanValues, + valuesText = booleanValuesText, + title = R.string.sniff_http_override_destination, + configure = snifferDependencies::add, + ) + + editableTextList( + value = configuration.sniffer.sniff.tls::ports, + adapter = TextAdapter.String, + title = R.string.sniff_tls_ports, + placeholder = R.string.dont_modify, + configure = snifferDependencies::add, + ) + + selectableList( + value = configuration.sniffer.sniff.tls::overrideDestination, + values = booleanValues, + valuesText = booleanValuesText, + title = R.string.sniff_tls_override_destination, + configure = snifferDependencies::add, + ) + + editableTextList( + value = configuration.sniffer.sniff.quic::ports, + adapter = TextAdapter.String, + title = R.string.sniff_quic_ports, + placeholder = R.string.dont_modify, + configure = snifferDependencies::add, + ) + + selectableList( + value = configuration.sniffer.sniff.quic::overrideDestination, + values = booleanValues, + valuesText = booleanValuesText, + title = R.string.sniff_quic_override_destination, + configure = snifferDependencies::add, + ) + selectableList( value = configuration.sniffer::forceDnsMapping, values = booleanValues, @@ -185,9 +225,17 @@ class MetaFeatureSettingsDesign( ) editableTextList( - value = configuration.sniffer::portWhitelist, + value = configuration.sniffer::skipSrcAddress, adapter = TextAdapter.String, - title = R.string.port_whitelist, + title = R.string.skip_src_address, + placeholder = R.string.dont_modify, + configure = snifferDependencies::add, + ) + + editableTextList( + value = configuration.sniffer::skipDstAddress, + adapter = TextAdapter.String, + title = R.string.skip_dst_address, placeholder = R.string.dont_modify, configure = snifferDependencies::add, ) diff --git a/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/ProvidersDesign.kt b/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/ProvidersDesign.kt index b193783c14..a717bc287f 100644 --- a/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/ProvidersDesign.kt +++ b/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/ProvidersDesign.kt @@ -55,8 +55,9 @@ class ProvidersDesign( fun requestUpdateAll() { adapter.states.filter { !it.updating }.forEachIndexed { index, state -> state.updating = true - - requests.trySend(Request.Update(index, state.provider)) + if (state.provider.vehicleType != Provider.VehicleType.Inline) { + requests.trySend(Request.Update(index, state.provider)) + } } } } \ No newline at end of file diff --git a/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/adapter/ProviderAdapter.kt b/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/adapter/ProviderAdapter.kt index bb7b1eb794..b076827a75 100644 --- a/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/adapter/ProviderAdapter.kt +++ b/clash-meta-android/design/src/main/java/com/github/kr328/clash/design/adapter/ProviderAdapter.kt @@ -55,10 +55,17 @@ class ProviderAdapter( holder.binding.provider = state.provider holder.binding.state = state - holder.binding.update = View.OnClickListener { - state.updating = true - - requestUpdate(position, state.provider) + if (state.provider.vehicleType == Provider.VehicleType.Inline) { + holder.binding.endView.visibility = View.GONE + holder.binding.elapsedView.visibility = View.GONE + holder.binding.divider.visibility = View.GONE + } else { + holder.binding.endView.visibility = View.VISIBLE + holder.binding.elapsedView.visibility = View.VISIBLE + holder.binding.update = View.OnClickListener { + state.updating = true + requestUpdate(position, state.provider) + } } } diff --git a/clash-meta-android/design/src/main/res/layout/adapter_provider.xml b/clash-meta-android/design/src/main/res/layout/adapter_provider.xml index 12b99fe9bb..4625574f6e 100644 --- a/clash-meta-android/design/src/main/res/layout/adapter_provider.xml +++ b/clash-meta-android/design/src/main/res/layout/adapter_provider.xml @@ -58,6 +58,7 @@ android:text="@{IntervalKt.elapsedIntervalString(currentTime.value - state.updatedAt, context)}" /> Fake-IP 至 域名映射 黑名单 白名单 - OFF - Strict - Always + 关闭 + 严格 + 强制开启 排序 布局 单列 @@ -220,40 +220,39 @@ 允许 Ipv6 通过 VpnService 代理 Ipv6 流量 Clash Meta Wiki - Meta Features - Unified Delay - Geodata Mode - TCP Concurrent - Find Process Mode - Sniffer Setting - Sniffer - Sniffer Mode - Force Domain - Skip Domain - Disable Sniffer - Load Sniffer From Config - Override Sniffer Config - GeoX Url Setting - GeoIp Url - MMDB Url - Geosite Url - Prefer h3 - Port Whitelist - Geo Files + Meta 特性 + 统一延迟 + Geodata 模式 + TCP 并发 + 查找进程模式 + 嗅探设置 + 嗅探器 + 嗅探 + 强制 DNS 映射 + 解析纯 IP 连接 + 覆盖目标地址 + 强制解析域名 + 跳过域名 + 跳过源 IP + 跳过目标 IP + GeoX 链接设置 + GeoIp 链接 + MMDB 链接 + Geosite 链接 + H3 优先 + Geo 文件 导入 GeoIP 数据库 - Press to import... 导入 GeoSite 数据库 导入 Country 数据库 + 导入 ASN 数据库 + 点击导入... 导入失败 数据库类型错误 只支持 %1$s 格式的 Geo 数据库 %1$s 已导入 更新配置 %s 成功 更新配置 %1$s 失败:%2$s - External Control + 外部控制 Clash.Meta 服务已启动 Clash.Meta 服务已停止 - Force DNS Mapping - Parse Pure IP - Override Destination - \ No newline at end of file + diff --git a/clash-meta-android/design/src/main/res/values/strings.xml b/clash-meta-android/design/src/main/res/values/strings.xml index 85f8a6ba52..2840f209c9 100644 --- a/clash-meta-android/design/src/main/res/values/strings.xml +++ b/clash-meta-android/design/src/main/res/values/strings.xml @@ -305,13 +305,19 @@ Sniffer Setting Sniffer - Sniffer Mode + Force DNS Mapping + Parse Pure IP + Override Destination Force Domain Skip Domain - Port Whitelist - Disable Sniffer - Load Sniffer From Config - Override Sniffer Config + Skip Src Address + Skip Dst Address + Sniff HTTP Ports + Sniff TLS Ports + Sniff QUIC Ports + Sniff HTTP Override Destination + Sniff TLS Override Destination + Sniff QUIC Override Destination GeoX Url Setting GeoIp Url @@ -322,10 +328,10 @@ https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/release/geosite.dat Geo Files Import GeoIP Database - Press to import... Import GeoSite Database Import Country Database Import ASN Database + Press to import... Import failed Unknown Database format Only %1$s are supported @@ -335,7 +341,4 @@ External Control Clash.Meta service started Clash.Meta service stopped - Force DNS Mapping - Parse Pure IP - Override Destination diff --git a/clash-meta/go.mod b/clash-meta/go.mod index ee9a209247..1eb79004b5 100644 --- a/clash-meta/go.mod +++ b/clash-meta/go.mod @@ -23,6 +23,7 @@ require ( github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 github.com/metacubex/randv2 v0.2.0 + github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 @@ -40,7 +41,6 @@ require ( github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/sing v0.5.1 github.com/sagernet/sing-mux v0.2.1 github.com/sagernet/sing-shadowtls v0.1.5 diff --git a/clash-meta/go.sum b/clash-meta/go.sum index 82fa5d50b6..7e485eacb1 100644 --- a/clash-meta/go.sum +++ b/clash-meta/go.sum @@ -109,6 +109,8 @@ github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/j github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA= @@ -168,8 +170,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo= github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE= github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo= diff --git a/clash-meta/listener/anytls/server.go b/clash-meta/listener/anytls/server.go index f89f2277a2..31a7c55ae9 100644 --- a/clash-meta/listener/anytls/server.go +++ b/clash-meta/listener/anytls/server.go @@ -135,6 +135,8 @@ func (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) { defer conn.Close() b := buf.NewPacket() + defer b.Release() + _, err := b.ReadOnceFrom(conn) if err != nil { return @@ -177,6 +179,6 @@ func (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) { Destination: destination, }) }, &l.padding) - session.Run(true) + session.Run() session.Close() } diff --git a/clash-meta/listener/reality/reality.go b/clash-meta/listener/reality/reality.go index 16245e2bb7..f629f2b4fe 100644 --- a/clash-meta/listener/reality/reality.go +++ b/clash-meta/listener/reality/reality.go @@ -12,7 +12,7 @@ import ( "github.com/metacubex/mihomo/listener/inner" "github.com/metacubex/mihomo/ntp" - "github.com/sagernet/reality" + "github.com/metacubex/reality" ) type Conn = reality.Conn diff --git a/clash-meta/transport/anytls/client.go b/clash-meta/transport/anytls/client.go index fd573ebe79..19776df97c 100644 --- a/clash-meta/transport/anytls/client.go +++ b/clash-meta/transport/anytls/client.go @@ -71,6 +71,8 @@ func (c *Client) CreateOutboundTLSConnection(ctx context.Context) (net.Conn, err } b := buf.NewPacket() + defer b.Release() + b.Write(c.passwordSha256) var paddingLen int if pad := c.padding.Load().GenerateRecordPayloadSizes(0); len(pad) > 0 { diff --git a/clash-meta/transport/anytls/pipe/deadline.go b/clash-meta/transport/anytls/pipe/deadline.go new file mode 100644 index 0000000000..29c4ec0ab7 --- /dev/null +++ b/clash-meta/transport/anytls/pipe/deadline.go @@ -0,0 +1,74 @@ +package pipe + +import ( + "sync" + "time" +) + +// PipeDeadline is an abstraction for handling timeouts. +type PipeDeadline struct { + mu sync.Mutex // Guards timer and cancel + timer *time.Timer + cancel chan struct{} // Must be non-nil +} + +func MakePipeDeadline() PipeDeadline { + return PipeDeadline{cancel: make(chan struct{})} +} + +// Set sets the point in time when the deadline will time out. +// A timeout event is signaled by closing the channel returned by waiter. +// Once a timeout has occurred, the deadline can be refreshed by specifying a +// t value in the future. +// +// A zero value for t prevents timeout. +func (d *PipeDeadline) Set(t time.Time) { + d.mu.Lock() + defer d.mu.Unlock() + + if d.timer != nil && !d.timer.Stop() { + <-d.cancel // Wait for the timer callback to finish and close cancel + } + d.timer = nil + + // Time is zero, then there is no deadline. + closed := isClosedChan(d.cancel) + if t.IsZero() { + if closed { + d.cancel = make(chan struct{}) + } + return + } + + // Time in the future, setup a timer to cancel in the future. + if dur := time.Until(t); dur > 0 { + if closed { + d.cancel = make(chan struct{}) + } + d.timer = time.AfterFunc(dur, func() { + close(d.cancel) + }) + return + } + + // Time in the past, so close immediately. + if !closed { + close(d.cancel) + } +} + +// Wait returns a channel that is closed when the deadline is exceeded. +func (d *PipeDeadline) Wait() chan struct{} { + d.mu.Lock() + defer d.mu.Unlock() + return d.cancel +} + +func isClosedChan(c <-chan struct{}) bool { + select { + case <-c: + return true + default: + return false + } +} diff --git a/clash-meta/transport/anytls/pipe/io_pipe.go b/clash-meta/transport/anytls/pipe/io_pipe.go new file mode 100644 index 0000000000..5d0fd252ec --- /dev/null +++ b/clash-meta/transport/anytls/pipe/io_pipe.go @@ -0,0 +1,232 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pipe adapter to connect code expecting an io.Reader +// with code expecting an io.Writer. + +package pipe + +import ( + "io" + "os" + "sync" + "time" +) + +// onceError is an object that will only store an error once. +type onceError struct { + sync.Mutex // guards following + err error +} + +func (a *onceError) Store(err error) { + a.Lock() + defer a.Unlock() + if a.err != nil { + return + } + a.err = err +} +func (a *onceError) Load() error { + a.Lock() + defer a.Unlock() + return a.err +} + +// A pipe is the shared pipe structure underlying PipeReader and PipeWriter. +type pipe struct { + wrMu sync.Mutex // Serializes Write operations + wrCh chan []byte + rdCh chan int + + once sync.Once // Protects closing done + done chan struct{} + rerr onceError + werr onceError + + readDeadline PipeDeadline + writeDeadline PipeDeadline +} + +func (p *pipe) read(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.readCloseError() + case <-p.readDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + default: + } + + select { + case bw := <-p.wrCh: + nr := copy(b, bw) + p.rdCh <- nr + return nr, nil + case <-p.done: + return 0, p.readCloseError() + case <-p.readDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + } +} + +func (p *pipe) closeRead(err error) error { + if err == nil { + err = io.ErrClosedPipe + } + p.rerr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +func (p *pipe) write(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.writeCloseError() + case <-p.writeDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + default: + p.wrMu.Lock() + defer p.wrMu.Unlock() + } + + for once := true; once || len(b) > 0; once = false { + select { + case p.wrCh <- b: + nw := <-p.rdCh + b = b[nw:] + n += nw + case <-p.done: + return n, p.writeCloseError() + case <-p.writeDeadline.Wait(): + return n, os.ErrDeadlineExceeded + } + } + return n, nil +} + +func (p *pipe) closeWrite(err error) error { + if err == nil { + err = io.EOF + } + p.werr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +// readCloseError is considered internal to the pipe type. +func (p *pipe) readCloseError() error { + rerr := p.rerr.Load() + if werr := p.werr.Load(); rerr == nil && werr != nil { + return werr + } + return io.ErrClosedPipe +} + +// writeCloseError is considered internal to the pipe type. +func (p *pipe) writeCloseError() error { + werr := p.werr.Load() + if rerr := p.rerr.Load(); werr == nil && rerr != nil { + return rerr + } + return io.ErrClosedPipe +} + +// A PipeReader is the read half of a pipe. +type PipeReader struct{ pipe } + +// Read implements the standard Read interface: +// it reads data from the pipe, blocking until a writer +// arrives or the write end is closed. +// If the write end is closed with an error, that error is +// returned as err; otherwise err is EOF. +func (r *PipeReader) Read(data []byte) (n int, err error) { + return r.pipe.read(data) +} + +// Close closes the reader; subsequent writes to the +// write half of the pipe will return the error [ErrClosedPipe]. +func (r *PipeReader) Close() error { + return r.CloseWithError(nil) +} + +// CloseWithError closes the reader; subsequent writes +// to the write half of the pipe will return the error err. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (r *PipeReader) CloseWithError(err error) error { + return r.pipe.closeRead(err) +} + +// A PipeWriter is the write half of a pipe. +type PipeWriter struct{ r PipeReader } + +// Write implements the standard Write interface: +// it writes data to the pipe, blocking until one or more readers +// have consumed all the data or the read end is closed. +// If the read end is closed with an error, that err is +// returned as err; otherwise err is [ErrClosedPipe]. +func (w *PipeWriter) Write(data []byte) (n int, err error) { + return w.r.pipe.write(data) +} + +// Close closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and EOF. +func (w *PipeWriter) Close() error { + return w.CloseWithError(nil) +} + +// CloseWithError closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and the error err, +// or EOF if err is nil. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (w *PipeWriter) CloseWithError(err error) error { + return w.r.pipe.closeWrite(err) +} + +// Pipe creates a synchronous in-memory pipe. +// It can be used to connect code expecting an [io.Reader] +// with code expecting an [io.Writer]. +// +// Reads and Writes on the pipe are matched one to one +// except when multiple Reads are needed to consume a single Write. +// That is, each Write to the [PipeWriter] blocks until it has satisfied +// one or more Reads from the [PipeReader] that fully consume +// the written data. +// The data is copied directly from the Write to the corresponding +// Read (or Reads); there is no internal buffering. +// +// It is safe to call Read and Write in parallel with each other or with Close. +// Parallel calls to Read and parallel calls to Write are also safe: +// the individual calls will be gated sequentially. +// +// Added SetReadDeadline and SetWriteDeadline methods based on `io.Pipe`. +func Pipe() (*PipeReader, *PipeWriter) { + pw := &PipeWriter{r: PipeReader{pipe: pipe{ + wrCh: make(chan []byte), + rdCh: make(chan int), + done: make(chan struct{}), + readDeadline: MakePipeDeadline(), + writeDeadline: MakePipeDeadline(), + }}} + return &pw.r, pw +} + +func (p *PipeReader) SetReadDeadline(t time.Time) error { + if isClosedChan(p.done) { + return io.ErrClosedPipe + } + p.readDeadline.Set(t) + return nil +} + +func (p *PipeWriter) SetWriteDeadline(t time.Time) error { + if isClosedChan(p.r.done) { + return io.ErrClosedPipe + } + p.r.writeDeadline.Set(t) + return nil +} diff --git a/clash-meta/transport/anytls/session/client.go b/clash-meta/transport/anytls/session/client.go index 869a4b317a..2312a6ff56 100644 --- a/clash-meta/transport/anytls/session/client.go +++ b/clash-meta/transport/anytls/session/client.go @@ -123,7 +123,7 @@ func (c *Client) createSession(ctx context.Context) (*Session, error) { c.idleSession.Remove(math.MaxUint64 - session.seq) c.idleSessionLock.Unlock() } - session.Run(false) + session.Run() return session, nil } diff --git a/clash-meta/transport/anytls/session/session.go b/clash-meta/transport/anytls/session/session.go index ce4d580f20..963533ead1 100644 --- a/clash-meta/transport/anytls/session/session.go +++ b/clash-meta/transport/anytls/session/session.go @@ -36,10 +36,11 @@ type Session struct { padding *atomic.TypedValue[*padding.PaddingFactory] // client - isClient bool - buffering bool - buffer []byte - pktCounter atomic.Uint32 + isClient bool + sendPadding bool + buffering bool + buffer []byte + pktCounter atomic.Uint32 // server onNewStream func(stream *Stream) @@ -47,9 +48,10 @@ type Session struct { func NewClientSession(conn net.Conn, _padding *atomic.TypedValue[*padding.PaddingFactory]) *Session { s := &Session{ - conn: conn, - isClient: true, - padding: _padding, + conn: conn, + isClient: true, + sendPadding: true, + padding: _padding, } s.die = make(chan struct{}) s.streams = make(map[uint32]*Stream) @@ -60,7 +62,6 @@ func NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding s := &Session{ conn: conn, onNewStream: onNewStream, - isClient: false, padding: _padding, } s.die = make(chan struct{}) @@ -68,8 +69,8 @@ func NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding return s } -func (s *Session) Run(isServer bool) { - if isServer { +func (s *Session) Run() { + if !s.isClient { s.recvLoop() return } @@ -257,19 +258,18 @@ func (s *Session) recvLoop() error { } case cmdUpdatePaddingScheme: if hdr.Length() > 0 { - buffer := pool.Get(int(hdr.Length())) - if _, err := io.ReadFull(s.conn, buffer); err != nil { - pool.Put(buffer) + // `rawScheme` Do not use buffer to prevent subsequent misuse + rawScheme := make([]byte, int(hdr.Length())) + if _, err := io.ReadFull(s.conn, rawScheme); err != nil { return err } if s.isClient { - if padding.UpdatePaddingScheme(buffer, s.padding) { - log.Infoln("[Update padding succeed] %x\n", md5.Sum(buffer)) + if padding.UpdatePaddingScheme(rawScheme, s.padding) { + log.Infoln("[Update padding succeed] %x\n", md5.Sum(rawScheme)) } else { - log.Warnln("[Update padding failed] %x\n", md5.Sum(buffer)) + log.Warnln("[Update padding failed] %x\n", md5.Sum(rawScheme)) } } - pool.Put(buffer) } default: // I don't know what command it is (can't have data) @@ -319,7 +319,7 @@ func (s *Session) writeConn(b []byte) (n int, err error) { } // calulate & send padding - if s.isClient { + if s.sendPadding { pkt := s.pktCounter.Add(1) paddingF := s.padding.Load() if pkt < paddingF.Stop { @@ -333,7 +333,6 @@ func (s *Session) writeConn(b []byte) (n int, err error) { continue } } - // logrus.Debugln(pkt, "write", l, "len", remainPayloadLen, "remain", remainPayloadLen-l) if remainPayloadLen > l { // this packet is all payload _, err = s.conn.Write(b[:l]) if err != nil { @@ -371,7 +370,12 @@ func (s *Session) writeConn(b []byte) (n int, err error) { // maybe still remain payload to write if len(b) == 0 { return + } else { + n2, err := s.conn.Write(b) + return n + n2, err } + } else { + s.sendPadding = false } } diff --git a/clash-meta/transport/anytls/session/stream.go b/clash-meta/transport/anytls/session/stream.go index 140396e486..9f21ff04b3 100644 --- a/clash-meta/transport/anytls/session/stream.go +++ b/clash-meta/transport/anytls/session/stream.go @@ -6,6 +6,8 @@ import ( "os" "sync" "time" + + "github.com/metacubex/mihomo/transport/anytls/pipe" ) // Stream implements net.Conn @@ -14,8 +16,9 @@ type Stream struct { sess *Session - pipeR *io.PipeReader - pipeW *io.PipeWriter + pipeR *pipe.PipeReader + pipeW *pipe.PipeWriter + writeDeadline pipe.PipeDeadline dieOnce sync.Once dieHook func() @@ -26,7 +29,8 @@ func newStream(id uint32, sess *Session) *Stream { s := new(Stream) s.id = id s.sess = sess - s.pipeR, s.pipeW = io.Pipe() + s.pipeR, s.pipeW = pipe.Pipe() + s.writeDeadline = pipe.MakePipeDeadline() return s } @@ -37,6 +41,11 @@ func (s *Stream) Read(b []byte) (n int, err error) { // Write implements net.Conn func (s *Stream) Write(b []byte) (n int, err error) { + select { + case <-s.writeDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + default: + } f := newFrame(cmdPSH, s.id) f.data = b n, err = s.sess.writeFrame(f) @@ -67,15 +76,17 @@ func (s *Stream) sessionClose() (once bool) { } func (s *Stream) SetReadDeadline(t time.Time) error { - return os.ErrNotExist + return s.pipeR.SetReadDeadline(t) } func (s *Stream) SetWriteDeadline(t time.Time) error { - return os.ErrNotExist + s.writeDeadline.Set(t) + return nil } func (s *Stream) SetDeadline(t time.Time) error { - return os.ErrNotExist + s.SetWriteDeadline(t) + return s.SetReadDeadline(t) } // LocalAddr satisfies net.Conn interface diff --git a/clash-nyanpasu/frontend/nyanpasu/package.json b/clash-nyanpasu/frontend/nyanpasu/package.json index 0a50b98d29..52bec03b89 100644 --- a/clash-nyanpasu/frontend/nyanpasu/package.json +++ b/clash-nyanpasu/frontend/nyanpasu/package.json @@ -41,7 +41,7 @@ "react-error-boundary": "5.0.0", "react-fast-marquee": "1.6.5", "react-hook-form-mui": "7.5.0", - "react-i18next": "15.4.0", + "react-i18next": "15.4.1", "react-markdown": "9.0.3", "react-split-grid": "1.0.4", "react-use": "17.6.0", diff --git a/clash-nyanpasu/frontend/ui/package.json b/clash-nyanpasu/frontend/ui/package.json index ea2746f321..ca6cd58cb9 100644 --- a/clash-nyanpasu/frontend/ui/package.json +++ b/clash-nyanpasu/frontend/ui/package.json @@ -32,7 +32,7 @@ "react": "19.0.0", "react-dom": "19.0.0", "react-error-boundary": "5.0.0", - "react-i18next": "15.4.0", + "react-i18next": "15.4.1", "react-use": "17.6.0", "tailwindcss": "4.0.6", "vite": "6.1.0", diff --git a/clash-nyanpasu/package.json b/clash-nyanpasu/package.json index 5929b9bdd5..6b8d6d854c 100644 --- a/clash-nyanpasu/package.json +++ b/clash-nyanpasu/package.json @@ -85,7 +85,7 @@ "eslint-plugin-react-compiler": "19.0.0-beta-e552027-20250112", "eslint-plugin-react-hooks": "5.1.0", "globals": "15.15.0", - "knip": "5.44.1", + "knip": "5.44.4", "lint-staged": "15.4.3", "neostandard": "0.12.1", "npm-run-all2": "7.0.2", @@ -105,7 +105,7 @@ "stylelint-order": "6.0.4", "stylelint-scss": "6.11.0", "tailwindcss": "4.0.6", - "tsx": "4.19.2", + "tsx": "4.19.3", "typescript": "5.7.3", "typescript-eslint": "8.24.1" }, diff --git a/clash-nyanpasu/pnpm-lock.yaml b/clash-nyanpasu/pnpm-lock.yaml index e2d88b29df..f935224627 100644 --- a/clash-nyanpasu/pnpm-lock.yaml +++ b/clash-nyanpasu/pnpm-lock.yaml @@ -103,8 +103,8 @@ importers: specifier: 15.15.0 version: 15.15.0 knip: - specifier: 5.44.1 - version: 5.44.1(@types/node@22.13.4)(typescript@5.7.3) + specifier: 5.44.4 + version: 5.44.4(@types/node@22.13.4)(typescript@5.7.3) lint-staged: specifier: 15.4.3 version: 15.4.3 @@ -163,8 +163,8 @@ importers: specifier: 4.0.6 version: 4.0.6 tsx: - specifier: 4.19.2 - version: 4.19.2 + specifier: 4.19.3 + version: 4.19.3 typescript: specifier: 5.7.3 version: 5.7.3 @@ -299,8 +299,8 @@ importers: specifier: 7.5.0 version: 7.5.0(c2bb2479c5b984e46a39ca4f43f854ca) react-i18next: - specifier: 15.4.0 - version: 15.4.0(i18next@24.2.2(typescript@5.7.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: 15.4.1 + version: 15.4.1(i18next@24.2.2(typescript@5.7.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react-markdown: specifier: 9.0.3 version: 9.0.3(@types/react@19.0.10)(react@19.0.0) @@ -346,7 +346,7 @@ importers: version: 1.105.0(@tanstack/react-router@1.105.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@tanstack/router-plugin': specifier: 1.105.0 - version: 1.105.0(@tanstack/react-router@1.105.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 1.105.0(@tanstack/react-router@1.105.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) '@tauri-apps/plugin-clipboard-manager': specifier: 2.2.1 version: 2.2.1 @@ -382,13 +382,13 @@ importers: version: 13.12.2 '@vitejs/plugin-legacy': specifier: 6.0.1 - version: 6.0.1(terser@5.36.0)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 6.0.1(terser@5.36.0)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) '@vitejs/plugin-react': specifier: 4.3.4 - version: 4.3.4(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 4.3.4(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) '@vitejs/plugin-react-swc': specifier: 3.8.0 - version: 3.8.0(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 3.8.0(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) change-case: specifier: 5.4.4 version: 5.4.4 @@ -427,19 +427,19 @@ importers: version: 13.12.0 vite: specifier: 6.1.0 - version: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + version: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) vite-plugin-html: specifier: 3.2.2 - version: 3.2.2(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 3.2.2(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) vite-plugin-sass-dts: specifier: 1.3.30 - version: 1.3.30(postcss@8.5.2)(prettier@3.5.1)(sass-embedded@1.85.0)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 1.3.30(postcss@8.5.2)(prettier@3.5.1)(sass-embedded@1.85.0)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) vite-plugin-svgr: specifier: 4.3.0 - version: 4.3.0(rollup@4.34.3)(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 4.3.0(rollup@4.34.3)(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) vite-tsconfig-paths: specifier: 5.1.4 - version: 5.1.4(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 5.1.4(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) zod: specifier: 3.24.2 version: 3.24.2 @@ -475,7 +475,7 @@ importers: version: 19.0.10 '@vitejs/plugin-react': specifier: 4.3.4 - version: 4.3.4(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 4.3.4(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) ahooks: specifier: 3.8.4 version: 3.8.4(react@19.0.0) @@ -495,8 +495,8 @@ importers: specifier: 5.0.0 version: 5.0.0(react@19.0.0) react-i18next: - specifier: 15.4.0 - version: 15.4.0(i18next@24.2.2(typescript@5.7.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: 15.4.1 + version: 15.4.1(i18next@24.2.2(typescript@5.7.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react-use: specifier: 17.6.0 version: 17.6.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -505,10 +505,10 @@ importers: version: 4.0.6 vite: specifier: 6.1.0 - version: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + version: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) vite-tsconfig-paths: specifier: 5.1.4 - version: 5.1.4(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 5.1.4(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) devDependencies: '@emotion/react': specifier: 11.14.0 @@ -533,7 +533,7 @@ importers: version: 5.1.0(typescript@5.7.3) vite-plugin-dts: specifier: 4.5.0 - version: 4.5.0(@types/node@22.13.4)(rollup@4.34.3)(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 4.5.0(@types/node@22.13.4)(rollup@4.34.3)(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)) scripts: dependencies: @@ -1426,23 +1426,17 @@ packages: '@emotion/weak-memoize@0.4.0': resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} - '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.24.2': resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} engines: {node: '>=18'} - cpu: [arm64] - os: [android] + cpu: [ppc64] + os: [aix] '@esbuild/android-arm64@0.24.2': resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} @@ -1450,10 +1444,10 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} engines: {node: '>=18'} - cpu: [arm] + cpu: [arm64] os: [android] '@esbuild/android-arm@0.24.2': @@ -1462,10 +1456,10 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm] os: [android] '@esbuild/android-x64@0.24.2': @@ -1474,11 +1468,11 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] + cpu: [x64] + os: [android] '@esbuild/darwin-arm64@0.24.2': resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} @@ -1486,10 +1480,10 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.24.2': @@ -1498,11 +1492,11 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] + cpu: [x64] + os: [darwin] '@esbuild/freebsd-arm64@0.24.2': resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} @@ -1510,10 +1504,10 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.24.2': @@ -1522,11 +1516,11 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} engines: {node: '>=18'} - cpu: [arm64] - os: [linux] + cpu: [x64] + os: [freebsd] '@esbuild/linux-arm64@0.24.2': resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} @@ -1534,10 +1528,10 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} engines: {node: '>=18'} - cpu: [arm] + cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.24.2': @@ -1546,10 +1540,10 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.24.2': @@ -1558,10 +1552,10 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} engines: {node: '>=18'} - cpu: [loong64] + cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.24.2': @@ -1570,10 +1564,10 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} engines: {node: '>=18'} - cpu: [mips64el] + cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.24.2': @@ -1582,10 +1576,10 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} engines: {node: '>=18'} - cpu: [ppc64] + cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.24.2': @@ -1594,10 +1588,10 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} engines: {node: '>=18'} - cpu: [riscv64] + cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.24.2': @@ -1606,10 +1600,10 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} engines: {node: '>=18'} - cpu: [s390x] + cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.24.2': @@ -1618,10 +1612,10 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} engines: {node: '>=18'} - cpu: [x64] + cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.24.2': @@ -1630,16 +1624,22 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.24.2': resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.24.2': @@ -1648,11 +1648,11 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] + cpu: [x64] + os: [netbsd] '@esbuild/openbsd-arm64@0.24.2': resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} @@ -1660,10 +1660,10 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.24.2': @@ -1672,11 +1672,11 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} engines: {node: '>=18'} cpu: [x64] - os: [sunos] + os: [openbsd] '@esbuild/sunos-x64@0.24.2': resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} @@ -1684,11 +1684,11 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} engines: {node: '>=18'} - cpu: [arm64] - os: [win32] + cpu: [x64] + os: [sunos] '@esbuild/win32-arm64@0.24.2': resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} @@ -1696,10 +1696,10 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.24.2': @@ -1708,10 +1708,10 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} engines: {node: '>=18'} - cpu: [x64] + cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.24.2': @@ -1720,6 +1720,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4565,13 +4571,13 @@ packages: resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} engines: {node: '>=0.12'} - esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} engines: {node: '>=18'} hasBin: true - esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} engines: {node: '>=18'} hasBin: true @@ -5009,9 +5015,6 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.8.0: - resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} - get-tsconfig@4.8.1: resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} @@ -5747,8 +5750,8 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - knip@5.44.1: - resolution: {integrity: sha512-YJrz7K6AA3SwXJ5LL9ZlzQX9wcihMNwKoGBAaY5kUn4rLNOZ5ehsjFV+xm8gQuIJdjsFIKcFsy0K3U3yM/mabw==} + knip@5.44.4: + resolution: {integrity: sha512-Ryn8LwWHLId8jSK1DgtT0hmg5DbzkqAtH+Gg3vZJpmSMgGHMspej9Ag+qKTm8wsPLDjVetuEz/lIsobo0XCMvQ==} engines: {node: '>=18.18.0'} hasBin: true peerDependencies: @@ -6848,8 +6851,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - react-i18next@15.4.0: - resolution: {integrity: sha512-Py6UkX3zV08RTvL6ZANRoBh9sL/ne6rQq79XlkHEdd82cZr2H9usbWpUNVadJntIZP2pu3M2rL1CN+5rQYfYFw==} + react-i18next@15.4.1: + resolution: {integrity: sha512-ahGab+IaSgZmNPYXdV1n+OYky95TGpFwnKRflX/16dY04DsYYKHtVLjeny7sBSCREEcoMbAgSkFiGLF5g5Oofw==} peerDependencies: i18next: '>= 23.2.3' react: '>= 16.8.0' @@ -7744,8 +7747,8 @@ packages: tslib@2.7.0: resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - tsx@4.19.2: - resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} + tsx@4.19.3: + resolution: {integrity: sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==} engines: {node: '>=18.0.0'} hasBin: true @@ -9468,153 +9471,156 @@ snapshots: '@emotion/weak-memoize@0.4.0': {} - '@esbuild/aix-ppc64@0.23.1': - optional: true - '@esbuild/aix-ppc64@0.24.2': optional: true - '@esbuild/android-arm64@0.23.1': + '@esbuild/aix-ppc64@0.25.0': optional: true '@esbuild/android-arm64@0.24.2': optional: true - '@esbuild/android-arm@0.23.1': + '@esbuild/android-arm64@0.25.0': optional: true '@esbuild/android-arm@0.24.2': optional: true - '@esbuild/android-x64@0.23.1': + '@esbuild/android-arm@0.25.0': optional: true '@esbuild/android-x64@0.24.2': optional: true - '@esbuild/darwin-arm64@0.23.1': + '@esbuild/android-x64@0.25.0': optional: true '@esbuild/darwin-arm64@0.24.2': optional: true - '@esbuild/darwin-x64@0.23.1': + '@esbuild/darwin-arm64@0.25.0': optional: true '@esbuild/darwin-x64@0.24.2': optional: true - '@esbuild/freebsd-arm64@0.23.1': + '@esbuild/darwin-x64@0.25.0': optional: true '@esbuild/freebsd-arm64@0.24.2': optional: true - '@esbuild/freebsd-x64@0.23.1': + '@esbuild/freebsd-arm64@0.25.0': optional: true '@esbuild/freebsd-x64@0.24.2': optional: true - '@esbuild/linux-arm64@0.23.1': + '@esbuild/freebsd-x64@0.25.0': optional: true '@esbuild/linux-arm64@0.24.2': optional: true - '@esbuild/linux-arm@0.23.1': + '@esbuild/linux-arm64@0.25.0': optional: true '@esbuild/linux-arm@0.24.2': optional: true - '@esbuild/linux-ia32@0.23.1': + '@esbuild/linux-arm@0.25.0': optional: true '@esbuild/linux-ia32@0.24.2': optional: true - '@esbuild/linux-loong64@0.23.1': + '@esbuild/linux-ia32@0.25.0': optional: true '@esbuild/linux-loong64@0.24.2': optional: true - '@esbuild/linux-mips64el@0.23.1': + '@esbuild/linux-loong64@0.25.0': optional: true '@esbuild/linux-mips64el@0.24.2': optional: true - '@esbuild/linux-ppc64@0.23.1': + '@esbuild/linux-mips64el@0.25.0': optional: true '@esbuild/linux-ppc64@0.24.2': optional: true - '@esbuild/linux-riscv64@0.23.1': + '@esbuild/linux-ppc64@0.25.0': optional: true '@esbuild/linux-riscv64@0.24.2': optional: true - '@esbuild/linux-s390x@0.23.1': + '@esbuild/linux-riscv64@0.25.0': optional: true '@esbuild/linux-s390x@0.24.2': optional: true - '@esbuild/linux-x64@0.23.1': + '@esbuild/linux-s390x@0.25.0': optional: true '@esbuild/linux-x64@0.24.2': optional: true + '@esbuild/linux-x64@0.25.0': + optional: true + '@esbuild/netbsd-arm64@0.24.2': optional: true - '@esbuild/netbsd-x64@0.23.1': + '@esbuild/netbsd-arm64@0.25.0': optional: true '@esbuild/netbsd-x64@0.24.2': optional: true - '@esbuild/openbsd-arm64@0.23.1': + '@esbuild/netbsd-x64@0.25.0': optional: true '@esbuild/openbsd-arm64@0.24.2': optional: true - '@esbuild/openbsd-x64@0.23.1': + '@esbuild/openbsd-arm64@0.25.0': optional: true '@esbuild/openbsd-x64@0.24.2': optional: true - '@esbuild/sunos-x64@0.23.1': + '@esbuild/openbsd-x64@0.25.0': optional: true '@esbuild/sunos-x64@0.24.2': optional: true - '@esbuild/win32-arm64@0.23.1': + '@esbuild/sunos-x64@0.25.0': optional: true '@esbuild/win32-arm64@0.24.2': optional: true - '@esbuild/win32-ia32@0.23.1': + '@esbuild/win32-arm64@0.25.0': optional: true '@esbuild/win32-ia32@0.24.2': optional: true - '@esbuild/win32-x64@0.23.1': + '@esbuild/win32-ia32@0.25.0': optional: true '@esbuild/win32-x64@0.24.2': optional: true + '@esbuild/win32-x64@0.25.0': + optional: true + '@eslint-community/eslint-utils@4.4.1(eslint@9.20.1(jiti@2.4.2))': dependencies: eslint: 9.20.1(jiti@2.4.2) @@ -10798,12 +10804,12 @@ snapshots: dependencies: '@tanstack/virtual-file-routes': 1.99.0 prettier: 3.5.1 - tsx: 4.19.2 + tsx: 4.19.3 zod: 3.24.2 optionalDependencies: '@tanstack/react-router': 1.105.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tanstack/router-plugin@1.105.0(@tanstack/react-router@1.105.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0))': + '@tanstack/router-plugin@1.105.0(@tanstack/react-router@1.105.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0))': dependencies: '@babel/core': 7.26.8 '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.8) @@ -10823,7 +10829,7 @@ snapshots: zod: 3.24.2 optionalDependencies: '@tanstack/react-router': 1.105.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) transitivePeerDependencies: - supports-color @@ -11330,7 +11336,7 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-legacy@6.0.1(terser@5.36.0)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0))': + '@vitejs/plugin-legacy@6.0.1(terser@5.36.0)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0))': dependencies: '@babel/core': 7.26.7 '@babel/preset-env': 7.26.7(@babel/core@7.26.7) @@ -11341,25 +11347,25 @@ snapshots: regenerator-runtime: 0.14.1 systemjs: 6.15.1 terser: 5.36.0 - vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react-swc@3.8.0(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0))': + '@vitejs/plugin-react-swc@3.8.0(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0))': dependencies: '@swc/core': 1.10.15 - vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react@4.3.4(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0))': + '@vitejs/plugin-react@4.3.4(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0))': dependencies: '@babel/core': 7.26.0 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) transitivePeerDependencies: - supports-color @@ -12703,33 +12709,6 @@ snapshots: d: 1.0.2 ext: 1.7.0 - esbuild@0.23.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 - esbuild@0.24.2: optionalDependencies: '@esbuild/aix-ppc64': 0.24.2 @@ -12758,6 +12737,34 @@ snapshots: '@esbuild/win32-ia32': 0.24.2 '@esbuild/win32-x64': 0.24.2 + esbuild@0.25.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + escalade@3.2.0: {} escape-string-regexp@1.0.5: {} @@ -13299,10 +13306,6 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.6 - get-tsconfig@4.8.0: - dependencies: - resolve-pkg-maps: 1.0.0 - get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -14026,7 +14029,7 @@ snapshots: kind-of@6.0.3: {} - knip@5.44.1(@types/node@22.13.4)(typescript@5.7.3): + knip@5.44.4(@types/node@22.13.4)(typescript@5.7.3): dependencies: '@nodelib/fs.walk': 3.0.1 '@snyk/github-codeowners': 1.1.0 @@ -15209,7 +15212,7 @@ snapshots: dependencies: react: 19.0.0 - react-i18next@15.4.0(i18next@24.2.2(typescript@5.7.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + react-i18next@15.4.1(i18next@24.2.2(typescript@5.7.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: '@babel/runtime': 7.26.0 html-parse-stringify: 3.0.1 @@ -16202,10 +16205,10 @@ snapshots: tslib@2.7.0: {} - tsx@4.19.2: + tsx@4.19.3: dependencies: - esbuild: 0.23.1 - get-tsconfig: 4.8.0 + esbuild: 0.25.0 + get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 @@ -16562,7 +16565,7 @@ snapshots: - rollup - supports-color - vite-plugin-dts@4.5.0(@types/node@22.13.4)(rollup@4.34.3)(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)): + vite-plugin-dts@4.5.0(@types/node@22.13.4)(rollup@4.34.3)(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)): dependencies: '@microsoft/api-extractor': 7.49.1(@types/node@22.13.4) '@rollup/pluginutils': 5.1.4(rollup@4.34.3) @@ -16575,13 +16578,13 @@ snapshots: magic-string: 0.30.17 typescript: 5.7.3 optionalDependencies: - vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) transitivePeerDependencies: - '@types/node' - rollup - supports-color - vite-plugin-html@3.2.2(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)): + vite-plugin-html@3.2.2(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)): dependencies: '@rollup/pluginutils': 4.2.1 colorette: 2.0.20 @@ -16595,39 +16598,39 @@ snapshots: html-minifier-terser: 6.1.0 node-html-parser: 5.4.2 pathe: 0.2.0 - vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) - vite-plugin-sass-dts@1.3.30(postcss@8.5.2)(prettier@3.5.1)(sass-embedded@1.85.0)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)): + vite-plugin-sass-dts@1.3.30(postcss@8.5.2)(prettier@3.5.1)(sass-embedded@1.85.0)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)): dependencies: postcss: 8.5.2 postcss-js: 4.0.1(postcss@8.5.2) prettier: 3.5.1 sass-embedded: 1.85.0 - vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) - vite-plugin-svgr@4.3.0(rollup@4.34.3)(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)): + vite-plugin-svgr@4.3.0(rollup@4.34.3)(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)): dependencies: '@rollup/pluginutils': 5.1.3(rollup@4.34.3) '@svgr/core': 8.1.0(typescript@5.7.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.7.3)) - vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite-tsconfig-paths@5.1.4(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0)): + vite-tsconfig-paths@5.1.4(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)): dependencies: debug: 4.3.7 globrex: 0.1.2 tsconfck: 3.0.3(typescript@5.7.3) optionalDependencies: - vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + vite: 6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0) transitivePeerDependencies: - supports-color - typescript - vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0): + vite@6.1.0(@types/node@22.13.4)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.0)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0): dependencies: esbuild: 0.24.2 postcss: 8.5.1 @@ -16642,7 +16645,7 @@ snapshots: sass-embedded: 1.85.0 stylus: 0.62.0 terser: 5.36.0 - tsx: 4.19.2 + tsx: 4.19.3 yaml: 2.7.0 void-elements@3.1.0: {} diff --git a/lede/package/lean/aic8800/Makefile b/lede/package/lean/aic8800/Makefile index 863863a53e..c294df5a38 100644 --- a/lede/package/lean/aic8800/Makefile +++ b/lede/package/lean/aic8800/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -# Copyright (c) 2023-2024 AnYun +# Copyright (c) 2023-2025 AnYun # include $(TOPDIR)/rules.mk @@ -14,15 +14,22 @@ PKG_SOURCE_URL:=https://github.com/radxa-pkg/aic8800.git PKG_SOURCE_VERSION:=8ad987876a6220fffb40a755e0454250ce2c5fdd PKG_MIRROR_HASH:=4a22e28a4341eb7516f03ea7e6c1c0523013e1aaf72c17428eb3749c8f553856 +ifeq ($(BUILD_VARIANT),sdio) MAKE_PATH:=src/SDIO/driver_fw/driver/aic8800 +PATCH_DIR:=$(CURDIR)/patches-sdio +else +MAKE_PATH:=src/USB/driver_fw/drivers/aic8800 +PATCH_DIR:=$(CURDIR)/patches-usb +endif + PKG_EXTMOD_SUBDIRS:=$(MAKE_PATH) include $(INCLUDE_DIR)/kernel.mk include $(INCLUDE_DIR)/package.mk -define KernelPackage/aic8800 +define KernelPackage/aic8800s SUBMENU:=Wireless Drivers - TITLE:=SKI WB800D80S wireless support + TITLE:=AIC8800 SDIO wireless support DEPENDS:=+kmod-mac80211 +kmod-mmc \ +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT FILES:= \ @@ -30,6 +37,21 @@ define KernelPackage/aic8800 $(PKG_BUILD_DIR)/$(MAKE_PATH)/aic8800_fdrv/aic8800_fdrv.ko AUTOLOAD:=$(call AutoProbe,aic8800_bsp aic8800_fdrv) MODPARAMS.aic8800_fdrv:=he_on=n + VARIANT:=sdio +endef + +define KernelPackage/aic8800u + SUBMENU:=Wireless Drivers + TITLE:=AIC8800 USB wireless support + DEPENDS:=+kmod-mac80211 +kmod-usb-core @USB_SUPPORT \ + +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT + FILES:= \ + $(PKG_BUILD_DIR)/$(MAKE_PATH)/aic_load_fw/aic_load_fw.ko \ + $(PKG_BUILD_DIR)/$(MAKE_PATH)/aic8800_fdrv/aic8800_fdrv.ko + AUTOLOAD:=$(call AutoProbe,aic_load_fw aic8800_fdrv) + MODPARAMS.aic8800_fdrv:=he_on=n + CONFLICTS:=kmod-aic8800s + VARIANT:=usb endef NOSTDINC_FLAGS := \ @@ -52,9 +74,15 @@ define Build/Compile modules endef -define KernelPackage/aic8800/install +define KernelPackage/aic8800s/install $(INSTALL_DIR) $(1)/lib/firmware/aic8800 $(CP) $(PKG_BUILD_DIR)/src/SDIO/driver_fw/fw/aic8800D80/* $(1)/lib/firmware/aic8800 endef -$(eval $(call KernelPackage,aic8800)) +define KernelPackage/aic8800u/install + $(INSTALL_DIR) $(1)/lib/firmware + $(CP) $(PKG_BUILD_DIR)/src/USB/driver_fw/fw/* $(1)/lib/firmware +endef + +$(eval $(call KernelPackage,aic8800s)) +$(eval $(call KernelPackage,aic8800u)) diff --git a/lede/package/lean/aic8800/patches/010-fixes-build.patch b/lede/package/lean/aic8800/patches-sdio/010-fixes-build.patch similarity index 100% rename from lede/package/lean/aic8800/patches/010-fixes-build.patch rename to lede/package/lean/aic8800/patches-sdio/010-fixes-build.patch diff --git a/lede/package/lean/aic8800/patches/020-update-path.patch b/lede/package/lean/aic8800/patches-sdio/020-update-path.patch similarity index 100% rename from lede/package/lean/aic8800/patches/020-update-path.patch rename to lede/package/lean/aic8800/patches-sdio/020-update-path.patch diff --git a/lede/package/lean/aic8800/patches/032-wireless-6.6.patch b/lede/package/lean/aic8800/patches-sdio/030-wireless-6.1.patch similarity index 86% rename from lede/package/lean/aic8800/patches/032-wireless-6.6.patch rename to lede/package/lean/aic8800/patches-sdio/030-wireless-6.1.patch index 457a1169ea..d10bcf91cc 100644 --- a/lede/package/lean/aic8800/patches/032-wireless-6.6.patch +++ b/lede/package/lean/aic8800/patches-sdio/030-wireless-6.1.patch @@ -7,7 +7,7 @@ -#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION3) +#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION3) && !defined(BUILD_OPENWRT) cfg80211_ch_switch_notify(vif->ndev, &csa->chandef, 0, 0); - #elif (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION) || defined(BUILD_OPENWRT) + #elif (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION) cfg80211_ch_switch_notify(vif->ndev, &csa->chandef, 0); @@ -5561,7 +5561,7 @@ int rwnx_cfg80211_channel_switch (struct goto end; @@ -16,5 +16,5 @@ -#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION4 +#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION4 && !defined(BUILD_OPENWRT) cfg80211_ch_switch_started_notify(dev, &csa->chandef, 0, params->count, false, 0); - #elif LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2 || defined(BUILD_OPENWRT) + #elif LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2 cfg80211_ch_switch_started_notify(dev, &csa->chandef, 0, params->count, false); diff --git a/lede/package/lean/aic8800/patches/040-disable-log.patch b/lede/package/lean/aic8800/patches-sdio/040-disable-log.patch similarity index 100% rename from lede/package/lean/aic8800/patches/040-disable-log.patch rename to lede/package/lean/aic8800/patches-sdio/040-disable-log.patch diff --git a/lede/package/lean/aic8800/patches/050-memcpy.patch b/lede/package/lean/aic8800/patches-sdio/050-memcpy.patch similarity index 100% rename from lede/package/lean/aic8800/patches/050-memcpy.patch rename to lede/package/lean/aic8800/patches-sdio/050-memcpy.patch diff --git a/lede/package/lean/aic8800/patches-usb/010-fixes-build.patch b/lede/package/lean/aic8800/patches-usb/010-fixes-build.patch new file mode 100644 index 0000000000..e1014b6328 --- /dev/null +++ b/lede/package/lean/aic8800/patches-usb/010-fixes-build.patch @@ -0,0 +1,105 @@ +--- a/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_main.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_main.c +@@ -3194,7 +3194,7 @@ static struct rwnx_vif *rwnx_interface_a + vif->ap.generation = 0; + vif->ap.mesh_pm = NL80211_MESH_POWER_ACTIVE; + vif->ap.next_mesh_pm = NL80211_MESH_POWER_ACTIVE; +- // no break ++ fallthrough; + case NL80211_IFTYPE_AP: + INIT_LIST_HEAD(&vif->ap.sta_list); + memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn)); +@@ -3649,7 +3649,7 @@ static int rwnx_cfg80211_change_iface(st + INIT_LIST_HEAD(&vif->ap.proxy_list); + vif->ap.create_path = false; + vif->ap.generation = 0; +- // no break ++ fallthrough; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + INIT_LIST_HEAD(&vif->ap.sta_list); +@@ -5795,6 +5795,7 @@ static int rwnx_cfg80211_mgmt_tx(struct + switch (RWNX_VIF_TYPE(rwnx_vif)) { + case NL80211_IFTYPE_AP_VLAN: + rwnx_vif = rwnx_vif->ap_vlan.master; ++ fallthrough; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_MESH_POINT: +@@ -6456,6 +6457,7 @@ static int rwnx_fill_station_info(struct + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + case FORMATMOD_HE_MU: + sinfo->rxrate.he_ru_alloc = rx_vect1->he.ru_size; ++ fallthrough; + case FORMATMOD_HE_SU: + case FORMATMOD_HE_ER: + sinfo->rxrate.flags = RATE_INFO_FLAGS_HE_MCS; +--- a/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_msg_tx.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_msg_tx.c +@@ -614,7 +614,7 @@ int rwnx_send_add_if(struct rwnx_hw *rwn + //case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_P2P_CLIENT: + add_if_req_param->p2p = true; +- // no break ++ fallthrough; + #endif /* CONFIG_RWNX_FULLMAC */ + case NL80211_IFTYPE_STATION: + add_if_req_param->type = MM_STA; +@@ -627,7 +627,7 @@ int rwnx_send_add_if(struct rwnx_hw *rwn + #ifdef CONFIG_RWNX_FULLMAC + case NL80211_IFTYPE_P2P_GO: + add_if_req_param->p2p = true; +- // no break ++ fallthrough; + #endif /* CONFIG_RWNX_FULLMAC */ + case NL80211_IFTYPE_AP: + add_if_req_param->type = MM_AP; +--- a/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_tdls.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_tdls.c +@@ -261,17 +261,24 @@ rwnx_add_wmm_info_ie(struct sk_buff *skb + static u8 rwnx_ac_from_wmm(int ac) + { + switch (ac) { +- default: +- WARN_ON_ONCE(1); + case 0: + return AC_BE; ++ break; + case 1: + return AC_BK; ++ break; + case 2: + return AC_VI; ++ break; + case 3: + return AC_VO; ++ break; ++ default: ++ WARN_ON_ONCE(1); ++ break; + } ++ ++ return 0; + } + + static void +--- a/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_tx.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_tx.c +@@ -328,6 +328,7 @@ u16 rwnx_select_txq(struct rwnx_vif *rwn + /* AP_VLAN interface is not used for a 4A STA, + fallback searching sta amongs all AP's clients */ + rwnx_vif = rwnx_vif->ap_vlan.master; ++ fallthrough; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + { +--- a/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_txq.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_txq.c +@@ -638,6 +638,7 @@ void rwnx_txq_vif_for_each_sta(struct rw + } + case NL80211_IFTYPE_AP_VLAN: + rwnx_vif = rwnx_vif->ap_vlan.master; ++ fallthrough; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_P2P_GO: diff --git a/lede/package/lean/aic8800/patches-usb/030-wireless-6.1.patch b/lede/package/lean/aic8800/patches-usb/030-wireless-6.1.patch new file mode 100644 index 0000000000..980de070ae --- /dev/null +++ b/lede/package/lean/aic8800/patches-usb/030-wireless-6.1.patch @@ -0,0 +1,36 @@ +--- a/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_main.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_main.c +@@ -1072,7 +1072,7 @@ static void rwnx_csa_finish(struct work_ + } else + rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); + spin_unlock_bh(&rwnx_hw->cb_lock); +-#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION3) ++#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION3) && !defined(BUILD_OPENWRT) + cfg80211_ch_switch_notify(vif->ndev, &csa->chandef, 0, 0); + #elif (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION) + cfg80211_ch_switch_notify(vif->ndev, &csa->chandef, 0); +@@ -6045,7 +6045,7 @@ int rwnx_cfg80211_channel_switch(struct + goto end; + } else { + INIT_WORK(&csa->work, rwnx_csa_finish); +-#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION4 ++#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION4 && !defined(BUILD_OPENWRT) + cfg80211_ch_switch_started_notify(dev, &csa->chandef, 0, params->count, false, 0); + #elif LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2 + cfg80211_ch_switch_started_notify(dev, &csa->chandef, 0, params->count, false); +--- a/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_rx.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_rx.c +@@ -464,8 +464,13 @@ static bool rwnx_rx_data_skb(struct rwnx + + if (amsdu) { + int count; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0) || defined(BUILD_OPENWRT) ++ ieee80211_amsdu_to_8023s(skb, &list, rwnx_vif->ndev->dev_addr, ++ RWNX_VIF_TYPE(rwnx_vif), 0, NULL, NULL, false); ++#else + ieee80211_amsdu_to_8023s(skb, &list, rwnx_vif->ndev->dev_addr, + RWNX_VIF_TYPE(rwnx_vif), 0, NULL, NULL); ++#endif + + count = skb_queue_len(&list); + if (count > ARRAY_SIZE(rwnx_hw->stats.amsdus_rx)) diff --git a/lede/package/lean/aic8800/patches-usb/040-disable-log.patch b/lede/package/lean/aic8800/patches-usb/040-disable-log.patch new file mode 100644 index 0000000000..0762c3a1ef --- /dev/null +++ b/lede/package/lean/aic8800/patches-usb/040-disable-log.patch @@ -0,0 +1,22 @@ +--- a/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_main.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic8800_fdrv/rwnx_main.c +@@ -525,7 +525,7 @@ extern uint8_t p2p_working; + struct semaphore aicwf_deinit_sem; + atomic_t aicwf_deinit_atomic; + +-int aicwf_dbg_level = LOGERROR|LOGINFO; ++int aicwf_dbg_level = LOGDATA; + module_param(aicwf_dbg_level, int, 0660); + + int testmode = 0; +--- a/src/USB/driver_fw/drivers/aic8800/aic_load_fw/aic_bluetooth_main.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic_load_fw/aic_bluetooth_main.c +@@ -20,7 +20,7 @@ int adap_test = 0; + char paringid[100]; + int n_para = 1; + int ble_scan_wakeup_reboot_time = 1000; +-int aicwf_dbg_level = LOGERROR|LOGINFO|LOGDEBUG|LOGTRACE; ++int aicwf_dbg_level = LOGDATA; + int flash_erase_len = 0x400000; + uint32_t ad_data_filter_mask = 0; + uint32_t gpio_num = 2;//default select gpiob2 for fw_wakeup_host diff --git a/lede/package/lean/aic8800/patches-usb/050-memcpy.patch b/lede/package/lean/aic8800/patches-usb/050-memcpy.patch new file mode 100644 index 0000000000..2a4ecdba98 --- /dev/null +++ b/lede/package/lean/aic8800/patches-usb/050-memcpy.patch @@ -0,0 +1,20 @@ +--- a/src/USB/driver_fw/drivers/aic8800/aic_load_fw/aicbluetooth.c ++++ b/src/USB/driver_fw/drivers/aic8800/aic_load_fw/aicbluetooth.c +@@ -8,6 +8,7 @@ + #ifdef CONFIG_USE_FW_REQUEST + #include + #endif ++#include + + //Parser state + #define INIT 0 +@@ -1227,7 +1228,8 @@ int aicbt_patch_info_unpack(struct aicbt + patch_info->info_len = head_t->len; + if(patch_info->info_len == 0) + return 0; +- memcpy(&patch_info->adid_addrinf, head_t->data, patch_info->info_len * sizeof(uint32_t) * 2); ++ unsafe_memcpy(&patch_info->adid_addrinf, head_t->data, ++ patch_info->info_len * sizeof(uint32_t) * 2, /* justification */); + } + return 0; + } diff --git a/lede/package/lean/aic8800/patches/030-wireless-5.15.patch b/lede/package/lean/aic8800/patches/030-wireless-5.15.patch deleted file mode 100644 index 038af5210f..0000000000 --- a/lede/package/lean/aic8800/patches/030-wireless-5.15.patch +++ /dev/null @@ -1,92 +0,0 @@ ---- a/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_compat.h -+++ b/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_compat.h -@@ -52,7 +52,7 @@ - #endif - - --#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 60) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 60) || defined(BUILD_OPENWRT) - #define IEEE80211_MAX_AMPDU_BUF IEEE80211_MAX_AMPDU_BUF_HE - #define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB - #define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB ---- a/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_main.c -+++ b/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_main.c -@@ -3063,7 +3063,7 @@ static struct rwnx_vif *rwnx_interface_a - } else - vif->use_4addr = false; - --#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) || defined(BUILD_OPENWRT) - if (cfg80211_register_netdevice(ndev)) - #else - if (register_netdevice(ndev)) -@@ -3336,7 +3336,7 @@ static int rwnx_cfg80211_del_iface(struc - - if (dev->reg_state == NETREG_REGISTERED) { - /* Will call rwnx_close if interface is UP */ --#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) || defined(BUILD_OPENWRT) - cfg80211_unregister_netdevice(dev); - #else - unregister_netdevice(dev); ---- a/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_mod_params.c -+++ b/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_mod_params.c -@@ -1312,7 +1312,7 @@ static void rwnx_set_he_capa(struct rwnx - if (rwnx_hw->mod_params->stbc_on) - he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ; - -- #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) -+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) || defined(BUILD_OPENWRT) - he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM | - IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 | - IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU; -@@ -1330,7 +1330,7 @@ static void rwnx_set_he_capa(struct rwnx - } - he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK | - IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK; -- #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) -+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) || defined(BUILD_OPENWRT) - he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | - IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | - IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | -@@ -1429,7 +1429,7 @@ static void rwnx_set_he_capa(struct rwnx - #endif - if (rwnx_hw->mod_params->stbc_on) - he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ; -- #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) -+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) || defined(BUILD_OPENWRT) - he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM | - IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 | - IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU; -@@ -1446,7 +1446,7 @@ static void rwnx_set_he_capa(struct rwnx - } - he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK | - IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK; -- #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) -+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) || defined(BUILD_OPENWRT) - he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | - IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | - IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | -@@ -1732,7 +1732,7 @@ void rwnx_custregd(struct rwnx_hw *rwnx_ - wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; - - rtnl_lock(); -- #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) -+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) || defined(BUILD_OPENWRT) - if (regulatory_set_wiphy_regd_sync(wiphy, getRegdomainFromRwnxDB(wiphy, default_ccode))){ - wiphy_err(wiphy, "Failed to set custom regdomain\n"); - } ---- a/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_radar.c -+++ b/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_radar.c -@@ -1282,7 +1282,11 @@ static void rwnx_radar_detected(struct r - chan_def = rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx].chan_def; - - rwnx_radar_cancel_cac(&rwnx_hw->radar); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) || defined(BUILD_OPENWRT) -+ __cfg80211_radar_event(rwnx_hw->wiphy, &chan_def, true, GFP_KERNEL); -+#else - cfg80211_radar_event(rwnx_hw->wiphy, &chan_def, GFP_KERNEL); -+#endif - - #endif /* CONFIG_RWNX_FULLMAC */ - } diff --git a/lede/package/lean/aic8800/patches/031-wireless-6.1.patch b/lede/package/lean/aic8800/patches/031-wireless-6.1.patch deleted file mode 100644 index 735984bdea..0000000000 --- a/lede/package/lean/aic8800/patches/031-wireless-6.1.patch +++ /dev/null @@ -1,159 +0,0 @@ ---- a/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_main.c -+++ b/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_main.c -@@ -783,7 +783,7 @@ static void rwnx_csa_finish(struct work_ - spin_unlock_bh(&rwnx_hw->cb_lock); - #if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION3) - cfg80211_ch_switch_notify(vif->ndev, &csa->chandef, 0, 0); --#elif (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION) -+#elif (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION) || defined(BUILD_OPENWRT) - cfg80211_ch_switch_notify(vif->ndev, &csa->chandef, 0); - #else - cfg80211_ch_switch_notify(vif->ndev, &csa->chandef); -@@ -3605,7 +3605,7 @@ bool key_flag = false; - * when adding a group key. - */ - static int rwnx_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, --#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) -+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) || defined(BUILD_OPENWRT) - int link_id, - #endif - u8 key_index, bool pairwise, const u8 *mac_addr, -@@ -3702,7 +3702,7 @@ bool key_flag = false; - * - */ - static int rwnx_cfg80211_get_key(struct wiphy *wiphy, struct net_device *netdev, --#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) -+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) || defined(BUILD_OPENWRT) - int link_id, - #endif - u8 key_index, bool pairwise, const u8 *mac_addr, -@@ -3720,7 +3720,7 @@ static int rwnx_cfg80211_get_key(struct - * and @key_index, return -ENOENT if the key doesn't exist. - */ - static int rwnx_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, --#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) -+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) || defined(BUILD_OPENWRT) - int link_id, - #endif - u8 key_index, bool pairwise, const u8 *mac_addr) -@@ -3759,7 +3759,7 @@ static int rwnx_cfg80211_del_key(struct - */ - static int rwnx_cfg80211_set_default_key(struct wiphy *wiphy, - struct net_device *netdev, --#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) -+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) || defined(BUILD_OPENWRT) - int link_id, - #endif - u8 key_index, bool unicast, bool multicast) -@@ -3774,7 +3774,7 @@ static int rwnx_cfg80211_set_default_key - */ - static int rwnx_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, - struct net_device *netdev, --#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) -+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) || defined(BUILD_OPENWRT) - int link_id, - #endif - u8 key_index) -@@ -3861,7 +3861,7 @@ static int rwnx_cfg80211_connect(struct - key_params.seq_len = 0; - key_params.cipher = sme->crypto.cipher_group; - rwnx_cfg80211_add_key(wiphy, dev, --#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) -+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2) || defined(BUILD_OPENWRT) - 0, - #endif - sme->key_idx, false, NULL, &key_params); -@@ -4128,7 +4128,7 @@ static int rwnx_cfg80211_add_station(str - sta->vif_idx = rwnx_vif->vif_index; - sta->vlan_idx = sta->vif_idx; - sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0; --#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION -+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION || defined(BUILD_OPENWRT) - sta->ht = params->link_sta_params.ht_capa ? 1 : 0; - sta->vht = params->link_sta_params.vht_capa ? 1 : 0; - #else -@@ -4534,7 +4534,7 @@ static int rwnx_cfg80211_change_station( - sta->vif_idx = rwnx_vif->vif_index; - sta->vlan_idx = sta->vif_idx; - sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0; --#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION -+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION || defined(BUILD_OPENWRT) - sta->ht = params->link_sta_params.ht_capa ? 1 : 0; - sta->vht = params->link_sta_params.vht_capa ? 1 : 0; - #else -@@ -4781,7 +4781,7 @@ static int rwnx_cfg80211_change_beacon(s - /** - * * @stop_ap: Stop being an AP, including stopping beaconing. - */ --#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION) -+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION) || defined(BUILD_OPENWRT) - static int rwnx_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id) - #else - static int rwnx_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) -@@ -5266,7 +5266,7 @@ static int rwnx_cfg80211_dump_survey(str - */ - static int rwnx_cfg80211_get_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, --#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION -+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION || defined(BUILD_OPENWRT) - unsigned int link_id, - #endif - struct cfg80211_chan_def *chandef) -@@ -5563,7 +5563,7 @@ int rwnx_cfg80211_channel_switch (struct - INIT_WORK(&csa->work, rwnx_csa_finish); - #if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION4 - cfg80211_ch_switch_started_notify(dev, &csa->chandef, 0, params->count, false, 0); --#elif LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2 -+#elif LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION2 || defined(BUILD_OPENWRT) - cfg80211_ch_switch_started_notify(dev, &csa->chandef, 0, params->count, false); - #elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) - cfg80211_ch_switch_started_notify(dev, &csa->chandef, params->count, params->block_tx); ---- a/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_msg_rx.c -+++ b/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_msg_rx.c -@@ -851,7 +851,7 @@ static inline int rwnx_rx_sm_connect_ind - - do { - bss = cfg80211_get_bss(wdev->wiphy, NULL, rwnx_vif->sta.bssid, --#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION -+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION || defined(BUILD_OPENWRT) - wdev->u.client.ssid, wdev->u.client.ssid_len, - #else - wdev->ssid, wdev->ssid_len, -@@ -881,7 +881,7 @@ static inline int rwnx_rx_sm_connect_ind - #else - WLAN_CAPABILITY_ESS, - #endif --#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION -+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION || defined(BUILD_OPENWRT) - (int)wdev->u.client.ssid_len, - wdev->u.client.ssid, - #else -@@ -902,7 +902,7 @@ static inline int rwnx_rx_sm_connect_ind - rwnx_vif->sta.bssid[0], rwnx_vif->sta.bssid[1], rwnx_vif->sta.bssid[2], - rwnx_vif->sta.bssid[3], rwnx_vif->sta.bssid[4], rwnx_vif->sta.bssid[5]); - --#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION -+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION || defined(BUILD_OPENWRT) - wdev->u.client.ssid_len = (int)rwnx_vif->sta.ssid_len; - memcpy(wdev->u.client.ssid, rwnx_vif->sta.ssid, wdev->u.client.ssid_len); - #else -@@ -942,7 +942,7 @@ static inline int rwnx_rx_sm_connect_ind - #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) - struct cfg80211_roam_info info; - memset(&info, 0, sizeof(info)); --#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION -+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION || defined(BUILD_OPENWRT) - if (rwnx_vif->ch_index < NX_CHAN_CTXT_CNT) - info.links[0].channel = rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def.chan; - info.links[0].bssid = (const u8 *)ind->bssid.array; ---- a/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_msg_tx.c -+++ b/src/SDIO/driver_fw/driver/aic8800/aic8800_fdrv/rwnx_msg_tx.c -@@ -2076,7 +2076,7 @@ int rwnx_send_me_sta_add(struct rwnx_hw - { - struct me_sta_add_req *req; - --#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION -+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION || defined(BUILD_OPENWRT) - struct link_station_parameters *link_sta_params = ¶ms->link_sta_params; - #else - struct station_parameters *link_sta_params = params; diff --git a/lede/package/utils/usbmode/data/a69c-5721 b/lede/package/utils/usbmode/data/a69c-5721 new file mode 100644 index 0000000000..4b5ca02d4f --- /dev/null +++ b/lede/package/utils/usbmode/data/a69c-5721 @@ -0,0 +1,4 @@ +# AIC8800 Wifi Dongle +TargetVendor=0xa69c +TargetProductList="88dc,8d81" +StandardEject=1 diff --git a/mihomo/go.mod b/mihomo/go.mod index ee9a209247..1eb79004b5 100644 --- a/mihomo/go.mod +++ b/mihomo/go.mod @@ -23,6 +23,7 @@ require ( github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 github.com/metacubex/randv2 v0.2.0 + github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 github.com/metacubex/sing-shadowsocks v0.2.8 github.com/metacubex/sing-shadowsocks2 v0.2.2 @@ -40,7 +41,6 @@ require ( github.com/sagernet/cors v1.2.1 github.com/sagernet/fswatch v0.1.1 github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a - github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/sing v0.5.1 github.com/sagernet/sing-mux v0.2.1 github.com/sagernet/sing-shadowtls v0.1.5 diff --git a/mihomo/go.sum b/mihomo/go.sum index 82fa5d50b6..7e485eacb1 100644 --- a/mihomo/go.sum +++ b/mihomo/go.sum @@ -109,6 +109,8 @@ github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/j github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs= +github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok= github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA= @@ -168,8 +170,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= -github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo= github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE= github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo= diff --git a/mihomo/listener/anytls/server.go b/mihomo/listener/anytls/server.go index f89f2277a2..31a7c55ae9 100644 --- a/mihomo/listener/anytls/server.go +++ b/mihomo/listener/anytls/server.go @@ -135,6 +135,8 @@ func (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) { defer conn.Close() b := buf.NewPacket() + defer b.Release() + _, err := b.ReadOnceFrom(conn) if err != nil { return @@ -177,6 +179,6 @@ func (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) { Destination: destination, }) }, &l.padding) - session.Run(true) + session.Run() session.Close() } diff --git a/mihomo/listener/reality/reality.go b/mihomo/listener/reality/reality.go index 16245e2bb7..f629f2b4fe 100644 --- a/mihomo/listener/reality/reality.go +++ b/mihomo/listener/reality/reality.go @@ -12,7 +12,7 @@ import ( "github.com/metacubex/mihomo/listener/inner" "github.com/metacubex/mihomo/ntp" - "github.com/sagernet/reality" + "github.com/metacubex/reality" ) type Conn = reality.Conn diff --git a/mihomo/transport/anytls/client.go b/mihomo/transport/anytls/client.go index fd573ebe79..19776df97c 100644 --- a/mihomo/transport/anytls/client.go +++ b/mihomo/transport/anytls/client.go @@ -71,6 +71,8 @@ func (c *Client) CreateOutboundTLSConnection(ctx context.Context) (net.Conn, err } b := buf.NewPacket() + defer b.Release() + b.Write(c.passwordSha256) var paddingLen int if pad := c.padding.Load().GenerateRecordPayloadSizes(0); len(pad) > 0 { diff --git a/mihomo/transport/anytls/pipe/deadline.go b/mihomo/transport/anytls/pipe/deadline.go new file mode 100644 index 0000000000..29c4ec0ab7 --- /dev/null +++ b/mihomo/transport/anytls/pipe/deadline.go @@ -0,0 +1,74 @@ +package pipe + +import ( + "sync" + "time" +) + +// PipeDeadline is an abstraction for handling timeouts. +type PipeDeadline struct { + mu sync.Mutex // Guards timer and cancel + timer *time.Timer + cancel chan struct{} // Must be non-nil +} + +func MakePipeDeadline() PipeDeadline { + return PipeDeadline{cancel: make(chan struct{})} +} + +// Set sets the point in time when the deadline will time out. +// A timeout event is signaled by closing the channel returned by waiter. +// Once a timeout has occurred, the deadline can be refreshed by specifying a +// t value in the future. +// +// A zero value for t prevents timeout. +func (d *PipeDeadline) Set(t time.Time) { + d.mu.Lock() + defer d.mu.Unlock() + + if d.timer != nil && !d.timer.Stop() { + <-d.cancel // Wait for the timer callback to finish and close cancel + } + d.timer = nil + + // Time is zero, then there is no deadline. + closed := isClosedChan(d.cancel) + if t.IsZero() { + if closed { + d.cancel = make(chan struct{}) + } + return + } + + // Time in the future, setup a timer to cancel in the future. + if dur := time.Until(t); dur > 0 { + if closed { + d.cancel = make(chan struct{}) + } + d.timer = time.AfterFunc(dur, func() { + close(d.cancel) + }) + return + } + + // Time in the past, so close immediately. + if !closed { + close(d.cancel) + } +} + +// Wait returns a channel that is closed when the deadline is exceeded. +func (d *PipeDeadline) Wait() chan struct{} { + d.mu.Lock() + defer d.mu.Unlock() + return d.cancel +} + +func isClosedChan(c <-chan struct{}) bool { + select { + case <-c: + return true + default: + return false + } +} diff --git a/mihomo/transport/anytls/pipe/io_pipe.go b/mihomo/transport/anytls/pipe/io_pipe.go new file mode 100644 index 0000000000..5d0fd252ec --- /dev/null +++ b/mihomo/transport/anytls/pipe/io_pipe.go @@ -0,0 +1,232 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pipe adapter to connect code expecting an io.Reader +// with code expecting an io.Writer. + +package pipe + +import ( + "io" + "os" + "sync" + "time" +) + +// onceError is an object that will only store an error once. +type onceError struct { + sync.Mutex // guards following + err error +} + +func (a *onceError) Store(err error) { + a.Lock() + defer a.Unlock() + if a.err != nil { + return + } + a.err = err +} +func (a *onceError) Load() error { + a.Lock() + defer a.Unlock() + return a.err +} + +// A pipe is the shared pipe structure underlying PipeReader and PipeWriter. +type pipe struct { + wrMu sync.Mutex // Serializes Write operations + wrCh chan []byte + rdCh chan int + + once sync.Once // Protects closing done + done chan struct{} + rerr onceError + werr onceError + + readDeadline PipeDeadline + writeDeadline PipeDeadline +} + +func (p *pipe) read(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.readCloseError() + case <-p.readDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + default: + } + + select { + case bw := <-p.wrCh: + nr := copy(b, bw) + p.rdCh <- nr + return nr, nil + case <-p.done: + return 0, p.readCloseError() + case <-p.readDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + } +} + +func (p *pipe) closeRead(err error) error { + if err == nil { + err = io.ErrClosedPipe + } + p.rerr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +func (p *pipe) write(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.writeCloseError() + case <-p.writeDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + default: + p.wrMu.Lock() + defer p.wrMu.Unlock() + } + + for once := true; once || len(b) > 0; once = false { + select { + case p.wrCh <- b: + nw := <-p.rdCh + b = b[nw:] + n += nw + case <-p.done: + return n, p.writeCloseError() + case <-p.writeDeadline.Wait(): + return n, os.ErrDeadlineExceeded + } + } + return n, nil +} + +func (p *pipe) closeWrite(err error) error { + if err == nil { + err = io.EOF + } + p.werr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +// readCloseError is considered internal to the pipe type. +func (p *pipe) readCloseError() error { + rerr := p.rerr.Load() + if werr := p.werr.Load(); rerr == nil && werr != nil { + return werr + } + return io.ErrClosedPipe +} + +// writeCloseError is considered internal to the pipe type. +func (p *pipe) writeCloseError() error { + werr := p.werr.Load() + if rerr := p.rerr.Load(); werr == nil && rerr != nil { + return rerr + } + return io.ErrClosedPipe +} + +// A PipeReader is the read half of a pipe. +type PipeReader struct{ pipe } + +// Read implements the standard Read interface: +// it reads data from the pipe, blocking until a writer +// arrives or the write end is closed. +// If the write end is closed with an error, that error is +// returned as err; otherwise err is EOF. +func (r *PipeReader) Read(data []byte) (n int, err error) { + return r.pipe.read(data) +} + +// Close closes the reader; subsequent writes to the +// write half of the pipe will return the error [ErrClosedPipe]. +func (r *PipeReader) Close() error { + return r.CloseWithError(nil) +} + +// CloseWithError closes the reader; subsequent writes +// to the write half of the pipe will return the error err. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (r *PipeReader) CloseWithError(err error) error { + return r.pipe.closeRead(err) +} + +// A PipeWriter is the write half of a pipe. +type PipeWriter struct{ r PipeReader } + +// Write implements the standard Write interface: +// it writes data to the pipe, blocking until one or more readers +// have consumed all the data or the read end is closed. +// If the read end is closed with an error, that err is +// returned as err; otherwise err is [ErrClosedPipe]. +func (w *PipeWriter) Write(data []byte) (n int, err error) { + return w.r.pipe.write(data) +} + +// Close closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and EOF. +func (w *PipeWriter) Close() error { + return w.CloseWithError(nil) +} + +// CloseWithError closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and the error err, +// or EOF if err is nil. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (w *PipeWriter) CloseWithError(err error) error { + return w.r.pipe.closeWrite(err) +} + +// Pipe creates a synchronous in-memory pipe. +// It can be used to connect code expecting an [io.Reader] +// with code expecting an [io.Writer]. +// +// Reads and Writes on the pipe are matched one to one +// except when multiple Reads are needed to consume a single Write. +// That is, each Write to the [PipeWriter] blocks until it has satisfied +// one or more Reads from the [PipeReader] that fully consume +// the written data. +// The data is copied directly from the Write to the corresponding +// Read (or Reads); there is no internal buffering. +// +// It is safe to call Read and Write in parallel with each other or with Close. +// Parallel calls to Read and parallel calls to Write are also safe: +// the individual calls will be gated sequentially. +// +// Added SetReadDeadline and SetWriteDeadline methods based on `io.Pipe`. +func Pipe() (*PipeReader, *PipeWriter) { + pw := &PipeWriter{r: PipeReader{pipe: pipe{ + wrCh: make(chan []byte), + rdCh: make(chan int), + done: make(chan struct{}), + readDeadline: MakePipeDeadline(), + writeDeadline: MakePipeDeadline(), + }}} + return &pw.r, pw +} + +func (p *PipeReader) SetReadDeadline(t time.Time) error { + if isClosedChan(p.done) { + return io.ErrClosedPipe + } + p.readDeadline.Set(t) + return nil +} + +func (p *PipeWriter) SetWriteDeadline(t time.Time) error { + if isClosedChan(p.r.done) { + return io.ErrClosedPipe + } + p.r.writeDeadline.Set(t) + return nil +} diff --git a/mihomo/transport/anytls/session/client.go b/mihomo/transport/anytls/session/client.go index 869a4b317a..2312a6ff56 100644 --- a/mihomo/transport/anytls/session/client.go +++ b/mihomo/transport/anytls/session/client.go @@ -123,7 +123,7 @@ func (c *Client) createSession(ctx context.Context) (*Session, error) { c.idleSession.Remove(math.MaxUint64 - session.seq) c.idleSessionLock.Unlock() } - session.Run(false) + session.Run() return session, nil } diff --git a/mihomo/transport/anytls/session/session.go b/mihomo/transport/anytls/session/session.go index ce4d580f20..963533ead1 100644 --- a/mihomo/transport/anytls/session/session.go +++ b/mihomo/transport/anytls/session/session.go @@ -36,10 +36,11 @@ type Session struct { padding *atomic.TypedValue[*padding.PaddingFactory] // client - isClient bool - buffering bool - buffer []byte - pktCounter atomic.Uint32 + isClient bool + sendPadding bool + buffering bool + buffer []byte + pktCounter atomic.Uint32 // server onNewStream func(stream *Stream) @@ -47,9 +48,10 @@ type Session struct { func NewClientSession(conn net.Conn, _padding *atomic.TypedValue[*padding.PaddingFactory]) *Session { s := &Session{ - conn: conn, - isClient: true, - padding: _padding, + conn: conn, + isClient: true, + sendPadding: true, + padding: _padding, } s.die = make(chan struct{}) s.streams = make(map[uint32]*Stream) @@ -60,7 +62,6 @@ func NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding s := &Session{ conn: conn, onNewStream: onNewStream, - isClient: false, padding: _padding, } s.die = make(chan struct{}) @@ -68,8 +69,8 @@ func NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding return s } -func (s *Session) Run(isServer bool) { - if isServer { +func (s *Session) Run() { + if !s.isClient { s.recvLoop() return } @@ -257,19 +258,18 @@ func (s *Session) recvLoop() error { } case cmdUpdatePaddingScheme: if hdr.Length() > 0 { - buffer := pool.Get(int(hdr.Length())) - if _, err := io.ReadFull(s.conn, buffer); err != nil { - pool.Put(buffer) + // `rawScheme` Do not use buffer to prevent subsequent misuse + rawScheme := make([]byte, int(hdr.Length())) + if _, err := io.ReadFull(s.conn, rawScheme); err != nil { return err } if s.isClient { - if padding.UpdatePaddingScheme(buffer, s.padding) { - log.Infoln("[Update padding succeed] %x\n", md5.Sum(buffer)) + if padding.UpdatePaddingScheme(rawScheme, s.padding) { + log.Infoln("[Update padding succeed] %x\n", md5.Sum(rawScheme)) } else { - log.Warnln("[Update padding failed] %x\n", md5.Sum(buffer)) + log.Warnln("[Update padding failed] %x\n", md5.Sum(rawScheme)) } } - pool.Put(buffer) } default: // I don't know what command it is (can't have data) @@ -319,7 +319,7 @@ func (s *Session) writeConn(b []byte) (n int, err error) { } // calulate & send padding - if s.isClient { + if s.sendPadding { pkt := s.pktCounter.Add(1) paddingF := s.padding.Load() if pkt < paddingF.Stop { @@ -333,7 +333,6 @@ func (s *Session) writeConn(b []byte) (n int, err error) { continue } } - // logrus.Debugln(pkt, "write", l, "len", remainPayloadLen, "remain", remainPayloadLen-l) if remainPayloadLen > l { // this packet is all payload _, err = s.conn.Write(b[:l]) if err != nil { @@ -371,7 +370,12 @@ func (s *Session) writeConn(b []byte) (n int, err error) { // maybe still remain payload to write if len(b) == 0 { return + } else { + n2, err := s.conn.Write(b) + return n + n2, err } + } else { + s.sendPadding = false } } diff --git a/mihomo/transport/anytls/session/stream.go b/mihomo/transport/anytls/session/stream.go index 140396e486..9f21ff04b3 100644 --- a/mihomo/transport/anytls/session/stream.go +++ b/mihomo/transport/anytls/session/stream.go @@ -6,6 +6,8 @@ import ( "os" "sync" "time" + + "github.com/metacubex/mihomo/transport/anytls/pipe" ) // Stream implements net.Conn @@ -14,8 +16,9 @@ type Stream struct { sess *Session - pipeR *io.PipeReader - pipeW *io.PipeWriter + pipeR *pipe.PipeReader + pipeW *pipe.PipeWriter + writeDeadline pipe.PipeDeadline dieOnce sync.Once dieHook func() @@ -26,7 +29,8 @@ func newStream(id uint32, sess *Session) *Stream { s := new(Stream) s.id = id s.sess = sess - s.pipeR, s.pipeW = io.Pipe() + s.pipeR, s.pipeW = pipe.Pipe() + s.writeDeadline = pipe.MakePipeDeadline() return s } @@ -37,6 +41,11 @@ func (s *Stream) Read(b []byte) (n int, err error) { // Write implements net.Conn func (s *Stream) Write(b []byte) (n int, err error) { + select { + case <-s.writeDeadline.Wait(): + return 0, os.ErrDeadlineExceeded + default: + } f := newFrame(cmdPSH, s.id) f.data = b n, err = s.sess.writeFrame(f) @@ -67,15 +76,17 @@ func (s *Stream) sessionClose() (once bool) { } func (s *Stream) SetReadDeadline(t time.Time) error { - return os.ErrNotExist + return s.pipeR.SetReadDeadline(t) } func (s *Stream) SetWriteDeadline(t time.Time) error { - return os.ErrNotExist + s.writeDeadline.Set(t) + return nil } func (s *Stream) SetDeadline(t time.Time) error { - return os.ErrNotExist + s.SetWriteDeadline(t) + return s.SetReadDeadline(t) } // LocalAddr satisfies net.Conn interface diff --git a/shadowsocks-rust/Cargo.lock b/shadowsocks-rust/Cargo.lock index 4a21a34722..d8b94aec67 100644 --- a/shadowsocks-rust/Cargo.lock +++ b/shadowsocks-rust/Cargo.lock @@ -309,15 +309,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "memmap2", ] [[package]] @@ -545,18 +546,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.29" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184" +checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.29" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9" +checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" dependencies = [ "anstream", "anstyle", @@ -950,7 +951,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1900,7 +1901,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -2045,6 +2046,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + [[package]] name = "mimalloc" version = "0.1.43" @@ -2615,7 +2625,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2934,7 +2944,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3624,7 +3634,7 @@ dependencies = [ "fastrand", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/small/luci-app-fchomo/root/usr/libexec/fchomo/update_resources.sh b/small/luci-app-fchomo/root/usr/libexec/fchomo/update_resources.sh index d46483a71d..aff1ce8fbe 100755 --- a/small/luci-app-fchomo/root/usr/libexec/fchomo/update_resources.sh +++ b/small/luci-app-fchomo/root/usr/libexec/fchomo/update_resources.sh @@ -235,16 +235,16 @@ case "$1" in mv -f "$RESOURCES_DIR/asn.mmdb" "$RESOURCES_DIR/../asn.mmdb" ;; "china_ip4") - check_list_update "$1" "muink/route-list" "release" "china_ipv4.txt" + check_list_update "$1" "fcshark-org/route-list" "release" "china_ipv4.txt" ;; "china_ip6") - check_list_update "$1" "muink/route-list" "release" "china_ipv6.txt" + check_list_update "$1" "fcshark-org/route-list" "release" "china_ipv6.txt" ;; "gfw_list") - check_list_update "$1" "muink/route-list" "release" "gfwlist.txt" + check_list_update "$1" "fcshark-org/route-list" "release" "gfwlist.txt" ;; "china_list") - check_list_update "$1" "muink/route-list" "release" "china_list2.txt" + check_list_update "$1" "fcshark-org/route-list" "release" "china_list2.txt" ;; *) echo -e "Usage: $0 " diff --git a/small/v2ray-geodata/Makefile b/small/v2ray-geodata/Makefile index 91bcee1f64..990b30b87f 100644 --- a/small/v2ray-geodata/Makefile +++ b/small/v2ray-geodata/Makefile @@ -21,13 +21,13 @@ define Download/geoip HASH:=f2f5f03da44d007fa91fb6a37c077c9efae8ad0269ef0e4130cf90b0822873e3 endef -GEOSITE_VER:=20250216152937 +GEOSITE_VER:=20250219031756 GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER) define Download/geosite URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/ URL_FILE:=dlc.dat FILE:=$(GEOSITE_FILE) - HASH:=85467d3aa9b8c7cdbd7d979cb28a421e607492aaa6ff52ebaff9032a2ec90bd4 + HASH:=7fb82385fe16e44b4932d9f9fbd12fa7e146e9e7929a7fb1865bc699c50ef705 endef GEOSITE_IRAN_VER:=202502170036 diff --git a/v2rayn/v2rayN/Directory.Build.props b/v2rayn/v2rayN/Directory.Build.props index 3159a733c6..0e9218d974 100644 --- a/v2rayn/v2rayN/Directory.Build.props +++ b/v2rayn/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.9.1 + 7.9.2 diff --git a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index cd11ecd103..1ad3de1375 100644 --- a/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayn/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1324,7 +1324,7 @@ namespace ServiceLib.Services.CoreConfig singboxConfig.experimental.cache_file = new CacheFile4Sbox() { enabled = true, - path = Utils.GetBinConfigPath("cache.db") + path = Utils.GetBinPath("cache.db") }; } diff --git a/v2rayn/v2rayN/v2rayN.Desktop/App.axaml b/v2rayn/v2rayN/v2rayN.Desktop/App.axaml index 9dc9dd4a29..fb487d1d1e 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/App.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/App.axaml @@ -19,6 +19,7 @@ + diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml new file mode 100644 index 0000000000..97fec908d4 --- /dev/null +++ b/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.axaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs b/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs new file mode 100644 index 0000000000..adb760633c --- /dev/null +++ b/v2rayn/v2rayN/v2rayN.Desktop/Controls/AutoCompleteBox.cs @@ -0,0 +1,38 @@ +using Avalonia.Input; +using Avalonia.Interactivity; + +namespace v2rayN.Desktop.Controls; + +public class AutoCompleteBox : Avalonia.Controls.AutoCompleteBox +{ + static AutoCompleteBox() + { + MinimumPrefixLengthProperty.OverrideDefaultValue(0); + } + + public AutoCompleteBox() + { + AddHandler(PointerPressedEvent, OnBoxPointerPressed, RoutingStrategies.Tunnel); + } + + private void OnBoxPointerPressed(object? sender, PointerPressedEventArgs e) + { + if (Equals(sender, this) && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + { + SetCurrentValue(IsDropDownOpenProperty, true); + } + } + + protected override void OnGotFocus(GotFocusEventArgs e) + { + base.OnGotFocus(e); + if (IsDropDownOpen) + return; + SetCurrentValue(IsDropDownOpenProperty, true); + } + + public void Clear() + { + SetCurrentValue(SelectedItemProperty, null); + } +} diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index e70a68acdc..0f3def91a0 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -414,18 +414,17 @@ namespace v2rayN.Desktop.Views } else { - if (Utils.IsOSX() || _config.UiItem.Hide2TrayWhenClose) - { - foreach (var ownedWindow in this.OwnedWindows) - { - ownedWindow.Close(); - } - this.Hide(); - } - else + if (Utils.IsLinux() && _config.UiItem.Hide2TrayWhenClose == false) { this.WindowState = WindowState.Minimized; + return; } + + foreach (var ownedWindow in this.OwnedWindows) + { + ownedWindow.Close(); + } + this.Hide(); } _config.UiItem.ShowInTaskbar = bl; diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index 0bcfc214d2..5d5ec95e62 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -2,6 +2,7 @@ x:Class="v2rayN.Desktop.Views.OptionSettingWindow" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" @@ -219,8 +220,7 @@ Grid.Row="13" Grid.Column="1" Width="200" - Classes="Margin8" - ToolTip.Tip="Level" /> + Classes="Margin8" /> - + Width="300" + Classes="Margin8" /> - - - + Classes="Margin8" /> + Classes="Margin8" /> diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs index ac1b49566d..e3e3cfefad 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs @@ -74,18 +74,11 @@ namespace v2rayN.Desktop.Views { cmbSpeedTestTimeout.Items.Add(i * 5); } - Global.SpeedTestUrls.ForEach(it => - { - cmbSpeedTestUrl.Items.Add(it); - }); - Global.SpeedPingTestUrls.ForEach(it => - { - cmbSpeedPingTestUrl.Items.Add(it); - }); - Global.SubConvertUrls.ForEach(it => - { - cmbSubConvertUrl.Items.Add(it); - }); + + cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls; + cmbSpeedPingTestUrl.ItemsSource = Global.SpeedPingTestUrls; + cmbSubConvertUrl.ItemsSource = Global.SubConvertUrls; + Global.GeoFilesSources.ForEach(it => { cmbGetFilesSourceUrl.Items.Add(it); @@ -139,12 +132,12 @@ namespace v2rayN.Desktop.Views this.Bind(ViewModel, vm => vm.Hide2TrayWhenClose, v => v.togHide2TrayWhenClose.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.CurrentFontFamily, v => v.cmbcurrentFontFamily.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.CurrentFontFamily, v => v.cmbcurrentFontFamily.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.SelectedValue).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.SelectedValue).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.MixedConcurrencyCount, v => v.cmbMixedConcurrencyCount.SelectedValue).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables); @@ -174,15 +167,27 @@ namespace v2rayN.Desktop.Views if (Utils.IsWindows()) { txbSettingsExceptionTip2.IsVisible = false; + txtLinuxSudoPassword.IsVisible = false; labLinuxSudoPassword.IsVisible = false; labLinuxSudoPasswordTip.IsVisible = false; + + labHide2TrayWhenClose.IsVisible = false; + togHide2TrayWhenClose.IsVisible = false; } - else + else if (Utils.IsLinux()) { txbSettingsExceptionTip.IsVisible = false; panSystemProxyAdvanced.IsVisible = false; } + else if (Utils.IsOSX()) + { + txbSettingsExceptionTip.IsVisible = false; + panSystemProxyAdvanced.IsVisible = false; + + labHide2TrayWhenClose.IsVisible = false; + togHide2TrayWhenClose.IsVisible = false; + } } private async Task UpdateViewHandler(EViewAction action, object? obj) @@ -203,8 +208,9 @@ namespace v2rayN.Desktop.Views private async Task InitSettingFont() { var lstFonts = await GetFonts(); - lstFonts.ForEach(it => { cmbcurrentFontFamily.Items.Add(it); }); - cmbcurrentFontFamily.Items.Add(string.Empty); + + lstFonts.Add(string.Empty); + cmbcurrentFontFamily.ItemsSource = lstFonts; } private async Task> GetFonts() diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml b/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml index fbadc95be8..272f22d1f5 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml @@ -84,10 +84,12 @@ diff --git a/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs b/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs index a5bb3d7d88..62f73472e4 100644 --- a/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs +++ b/v2rayn/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs @@ -372,9 +372,8 @@ namespace v2rayN.Desktop.Views private void StorageUI(string? n = null) { List lvColumnItem = new(); - for (int k = 0; k < lstProfiles.Columns.Count; k++) + foreach (var item2 in lstProfiles.Columns) { - var item2 = lstProfiles.Columns[k]; if (item2.Tag == null) { continue; diff --git a/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 5244954880..a5d7aeee62 100644 --- a/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayn/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -764,7 +764,7 @@ x:Name="cmbcurrentFontFamily" Grid.Row="15" Grid.Column="1" - Width="200" + Width="300" Margin="{StaticResource Margin8}" MaxDropDownHeight="1000" Style="{StaticResource DefComboBox}" /> @@ -867,7 +867,7 @@ x:Name="cmbMainGirdOrientation" Grid.Row="21" Grid.Column="1" - Width="300" + Width="200" Margin="{StaticResource Margin8}" Style="{StaticResource DefComboBox}" /> diff --git a/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml.cs b/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml.cs index 749a31e9bf..d3a6c11228 100644 --- a/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml.cs +++ b/v2rayn/v2rayN/v2rayN/Views/ProfilesView.xaml.cs @@ -353,9 +353,9 @@ namespace v2rayN.Views private void StorageUI(string? n = null) { List lvColumnItem = new(); - for (int k = 0; k < lstProfiles.Columns.Count; k++) + foreach (var t in lstProfiles.Columns) { - var item2 = (MyDGTextColumn)lstProfiles.Columns[k]; + var item2 = (MyDGTextColumn)t; lvColumnItem.Add(new() { Name = item2.ExName, diff --git a/v2rayng/AndroidLibXrayLite/gen_assets.sh b/v2rayng/AndroidLibXrayLite/gen_assets.sh index b80cb8658f..3a102fa2db 100644 --- a/v2rayng/AndroidLibXrayLite/gen_assets.sh +++ b/v2rayng/AndroidLibXrayLite/gen_assets.sh @@ -61,8 +61,8 @@ compile_dat () { download_dat () { - wget -qO - https://api.github.com/repos/dyhkwong/v2ray-geoip/releases/latest \ - | jq -r .assets[].browser_download_url | grep geoip.dat \ + wget -qO - https://api.github.com/repos/v2ray/geoip/releases/latest \ + | grep browser_download_url | cut -d '"' -f 4 \ | wget -i - -O $DATADIR/geoip.dat wget -qO - https://api.github.com/repos/v2ray/domain-list-community/releases/latest \ diff --git a/v2rayng/AndroidLibXrayLite/go.mod b/v2rayng/AndroidLibXrayLite/go.mod index aae6b486d1..c12a75c856 100644 --- a/v2rayng/AndroidLibXrayLite/go.mod +++ b/v2rayng/AndroidLibXrayLite/go.mod @@ -1,10 +1,10 @@ module github.com/2dust/AndroidLibXrayLite -go 1.23.5 +go 1.24 require ( - github.com/xtls/xray-core v1.8.25-0.20250209010635-6b6fbcb459a8 - golang.org/x/mobile v0.0.0-20250106192035-c31d5b91ecc3 + github.com/xtls/xray-core v1.8.25-0.20250218115507-52381a3c038b + golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3 golang.org/x/sys v0.30.0 ) @@ -16,7 +16,7 @@ require ( github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/pprof v0.0.0-20250121033306-997b0b79cac0 // indirect + github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect @@ -27,7 +27,7 @@ require ( github.com/quic-go/quic-go v0.49.0 // indirect github.com/refraction-networking/utls v1.6.7 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect - github.com/sagernet/sing v0.5.1 // indirect + github.com/sagernet/sing v0.6.1 // indirect github.com/sagernet/sing-shadowsocks v0.2.7 // indirect github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect @@ -36,20 +36,20 @@ require ( github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect go.uber.org/mock v0.5.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.34.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.35.0 // indirect golang.org/x/sync v0.11.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/time v0.10.0 // indirect + golang.org/x/tools v0.30.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gvisor.dev/gvisor v0.0.0-20241227193629-b8cde430ca0a // indirect + gvisor.dev/gvisor v0.0.0-20250215002057-313350f3e697 // indirect lukechampine.com/blake3 v1.3.0 // indirect ) diff --git a/v2rayng/AndroidLibXrayLite/go.sum b/v2rayng/AndroidLibXrayLite/go.sum index 8828b53f4e..aa23adf2be 100644 --- a/v2rayng/AndroidLibXrayLite/go.sum +++ b/v2rayng/AndroidLibXrayLite/go.sum @@ -26,8 +26,8 @@ github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20250121033306-997b0b79cac0 h1:EinjE47mmVVsxcjIwVKQWNY+3P+5R2BhkbULjhEDThc= -github.com/google/pprof v0.0.0-20250121033306-997b0b79cac0/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc= +github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= @@ -56,8 +56,8 @@ github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= -github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y= -github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.6.1 h1:mJ6e7Ir2wtCoGLbdnnXWBsNJu5YHtbXmv66inoE0zFA= +github.com/sagernet/sing v0.6.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4= @@ -75,8 +75,8 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM= github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg= -github.com/xtls/xray-core v1.8.25-0.20250209010635-6b6fbcb459a8 h1:skW6aFSoLlpgjpAZ1EgB+KKICgJECuJNno+c8PG/Odc= -github.com/xtls/xray-core v1.8.25-0.20250209010635-6b6fbcb459a8/go.mod h1:3CIiFGvfTn/V7FKXz8j79ynHkf6QcTFOWLuQgfEQi2U= +github.com/xtls/xray-core v1.8.25-0.20250218115507-52381a3c038b h1:OaYQ4rfSSiJBX1wsIRsONGwpIzY1hkPN19WY0IHXFcs= +github.com/xtls/xray-core v1.8.25-0.20250218115507-52381a3c038b/go.mod h1:THIVeACwSOFwh9ublxTZiRTX2pe9d99ML32H5exwPwU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= @@ -93,34 +93,34 @@ go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= -golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= -golang.org/x/mobile v0.0.0-20250106192035-c31d5b91ecc3 h1:8LrYkH99trX3onYF3dT9frUSRDXokkceG+9tHBaDAFQ= -golang.org/x/mobile v0.0.0-20250106192035-c31d5b91ecc3/go.mod h1:sY92m3V/rTEa4JCJ1FkKHK978K6wxOSX1PStMYo+6wI= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= +golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= +golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3 h1:0V/7Y1FEaFdAzb9DkVDh4QFp4vL4yYCiJ5cjk80lZyA= +golang.org/x/mobile v0.0.0-20250218173827-cd096645fcd3/go.mod h1:j5VYNgQ6lZYZlzHFjdgS2UeqRSZunDk+/zXVTAIA3z4= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= +golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= @@ -134,7 +134,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gvisor.dev/gvisor v0.0.0-20241227193629-b8cde430ca0a h1:uqyWV7OBmknOheViupl+rEAT9yzgNU9wWSI4Mu1z5n4= -gvisor.dev/gvisor v0.0.0-20241227193629-b8cde430ca0a/go.mod h1:5DMfjtclAbTIjbXqO1qCe2K5GKKxWz2JHvCChuTcJEM= +gvisor.dev/gvisor v0.0.0-20250215002057-313350f3e697 h1:3xb9C+AmVuRnDDAwJGJ8ZVAmcIourUD9DwHaAd5Ldyk= +gvisor.dev/gvisor v0.0.0-20250215002057-313350f3e697/go.mod h1:5DMfjtclAbTIjbXqO1qCe2K5GKKxWz2JHvCChuTcJEM= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= diff --git a/v2rayng/AndroidLibXrayLite/libv2ray_support.go b/v2rayng/AndroidLibXrayLite/libv2ray_support.go index 62cf801fb4..5b628ae40e 100644 --- a/v2rayng/AndroidLibXrayLite/libv2ray_support.go +++ b/v2rayng/AndroidLibXrayLite/libv2ray_support.go @@ -222,7 +222,7 @@ func (d *ProtectedDialer) Init(_ dns.Client, _ outbound.Manager) { func (d *ProtectedDialer) Dial(ctx context.Context, src v2net.Address, dest v2net.Destination, sockopt *v2internet.SocketConfig) (net.Conn, error) { - network := dest.Network.SystemString() + // network := dest.Network.SystemString() Address := dest.NetAddr() // v2ray server address, @@ -260,7 +260,7 @@ func (d *ProtectedDialer) Dial(ctx context.Context, } // v2ray connecting to "domestic" servers, no caching results - log.Printf("Not Using Prepared: %s,%s", network, Address) + // log.Printf("Not Using Prepared: %s,%s", network, Address) resolved, err := d.lookupAddr(Address) if err != nil { return nil, err @@ -302,7 +302,7 @@ func (d *ProtectedDialer) fdConn(ctx context.Context, ip net.IP, port int, netwo } } else { if err := unix.Connect(fd, sa); err != nil { - log.Printf("fdConn unix.Connect err, Close Fd: %d Err: %v", fd, err) + // log.Printf("fdConn unix.Connect err, Close Fd: %d Err: %v", fd, err) return nil, err } } diff --git a/v2rayng/V2rayNG/app/build.gradle.kts b/v2rayng/V2rayNG/app/build.gradle.kts index 0f783ef982..02b6bbced4 100644 --- a/v2rayng/V2rayNG/app/build.gradle.kts +++ b/v2rayng/V2rayNG/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 35 - versionCode = 631 - versionName = "1.9.35" + versionCode = 632 + versionName = "1.9.36" multiDexEnabled = true val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') diff --git a/v2rayng/V2rayNG/gradle/libs.versions.toml b/v2rayng/V2rayNG/gradle/libs.versions.toml index 5dfb99cd49..2a2de8f914 100644 --- a/v2rayng/V2rayNG/gradle/libs.versions.toml +++ b/v2rayng/V2rayNG/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.8.0" +agp = "8.8.1" desugar_jdk_libs = "2.1.4" gradleLicensePlugin = "0.9.8" kotlin = "2.1.10" diff --git a/xray-core/proxy/proxy.go b/xray-core/proxy/proxy.go index 5764979595..3fec31af94 100644 --- a/xray-core/proxy/proxy.go +++ b/xray-core/proxy/proxy.go @@ -547,8 +547,8 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) { conn = pc.Raw() // 8192 > 4096, there is no need to process pc's bufReader } - if uc, ok := conn.(*internet.UDSWrapperConn); ok { - conn = uc.Conn + if uc, ok := conn.(*internet.UnixConnWrapper); ok { + conn = uc.UnixConn } } return conn, readCounter, writerCounter diff --git a/xray-core/transport/internet/system_listener.go b/xray-core/transport/internet/system_listener.go index 1086aef596..fa3092a1f3 100644 --- a/xray-core/transport/internet/system_listener.go +++ b/xray-core/transport/internet/system_listener.go @@ -44,32 +44,32 @@ func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []co // For some reason, other component of ray will assume the listener is a TCP listener and have valid remote address. // But in fact it doesn't. So we need to wrap the listener to make it return 0.0.0.0(unspecified) as remote address. // If other issues encountered, we should able to fix it here. -type listenUDSWrapper struct { - net.Listener +type UnixListenerWrapper struct { + *net.UnixListener locker *FileLocker } -func (l *listenUDSWrapper) Accept() (net.Conn, error) { - conn, err := l.Listener.Accept() +func (l *UnixListenerWrapper) Accept() (net.Conn, error) { + conn, err := l.UnixListener.Accept() if err != nil { return nil, err } - return &UDSWrapperConn{Conn: conn}, nil + return &UnixConnWrapper{UnixConn: conn.(*net.UnixConn)}, nil } -func (l *listenUDSWrapper) Close() error { +func (l *UnixListenerWrapper) Close() error { if l.locker != nil { l.locker.Release() l.locker = nil } - return l.Listener.Close() + return l.UnixListener.Close() } -type UDSWrapperConn struct { - net.Conn +type UnixConnWrapper struct { + *net.UnixConn } -func (conn *UDSWrapperConn) RemoteAddr() net.Addr { +func (conn *UnixConnWrapper) RemoteAddr() net.Addr { return &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, } @@ -136,7 +136,7 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S locker.Release() return nil, err } - l = &listenUDSWrapper{Listener: l, locker: locker} + l = &UnixListenerWrapper{UnixListener: l.(*net.UnixListener), locker: locker} if filePerm == nil { return l, nil } diff --git a/yt-dlp/.github/workflows/codeql.yml b/yt-dlp/.github/workflows/codeql.yml index 170a6ac19f..910c409efc 100644 --- a/yt-dlp/.github/workflows/codeql.yml +++ b/yt-dlp/.github/workflows/codeql.yml @@ -33,7 +33,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -47,7 +47,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -60,6 +60,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" diff --git a/yt-dlp/CONTRIBUTORS b/yt-dlp/CONTRIBUTORS index 7376b18015..0b90c0e6cc 100644 --- a/yt-dlp/CONTRIBUTORS +++ b/yt-dlp/CONTRIBUTORS @@ -736,3 +736,9 @@ NecroRomnt pjrobertson subsense test20140 +arantius +entourage8 +lfavole +mp3butcher +slipinthedove +YoshiTabletopGamer diff --git a/yt-dlp/Changelog.md b/yt-dlp/Changelog.md index 3232c158b5..9c544feb92 100644 --- a/yt-dlp/Changelog.md +++ b/yt-dlp/Changelog.md @@ -4,6 +4,49 @@ # To create a release, dispatch the https://github.com/yt-dlp/yt-dlp/actions/workflows/release.yml workflow on master --> +### 2025.02.19 + +#### Core changes +- **jsinterp** + - [Add `js_number_to_string`](https://github.com/yt-dlp/yt-dlp/commit/0d9f061d38c3a4da61972e2adad317079f2f1c84) ([#12110](https://github.com/yt-dlp/yt-dlp/issues/12110)) by [Grub4K](https://github.com/Grub4K) + - [Improve zeroise](https://github.com/yt-dlp/yt-dlp/commit/4ca8c44a073d5aa3a3e3112c35b2b23d6ce25ac6) ([#12313](https://github.com/yt-dlp/yt-dlp/issues/12313)) by [seproDev](https://github.com/seproDev) + +#### Extractor changes +- **acast**: [Support shows.acast.com URLs](https://github.com/yt-dlp/yt-dlp/commit/57c717fee4bfbc9309845bbb48901b72e4b69304) ([#12223](https://github.com/yt-dlp/yt-dlp/issues/12223)) by [barsnick](https://github.com/barsnick) +- **cwtv** + - [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/18a28514e306e822eab4f3a79c76d515bf076406) ([#12207](https://github.com/yt-dlp/yt-dlp/issues/12207)) by [arantius](https://github.com/arantius) + - movie: [Add extractor](https://github.com/yt-dlp/yt-dlp/commit/03c3d705778c07739e0034b51490877cffdc0983) ([#12227](https://github.com/yt-dlp/yt-dlp/issues/12227)) by [bashonly](https://github.com/bashonly) +- **digiview**: [Add extractor](https://github.com/yt-dlp/yt-dlp/commit/f53553087d3fde9dcd61d6e9f98caf09db1d8ef2) ([#9902](https://github.com/yt-dlp/yt-dlp/issues/9902)) by [lfavole](https://github.com/lfavole) +- **dropbox**: [Fix extraction](https://github.com/yt-dlp/yt-dlp/commit/861aeec449c8f3c062d962945b234ff0341f61f3) ([#12228](https://github.com/yt-dlp/yt-dlp/issues/12228)) by [bashonly](https://github.com/bashonly) +- **francetv** + - site + - [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/817483ccc68aed6049ed9c4a2ffae44ca82d2b1c) ([#12236](https://github.com/yt-dlp/yt-dlp/issues/12236)) by [bashonly](https://github.com/bashonly) + - [Fix livestream extraction](https://github.com/yt-dlp/yt-dlp/commit/1295bbedd45fa8d9bc3f7a194864ae280297848e) ([#12316](https://github.com/yt-dlp/yt-dlp/issues/12316)) by [bashonly](https://github.com/bashonly) +- **francetvinfo.fr**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/5c4c2ddfaa47988b4d50c1ad4988badc0b4f30c2) ([#12402](https://github.com/yt-dlp/yt-dlp/issues/12402)) by [bashonly](https://github.com/bashonly) +- **gem.cbc.ca**: [Fix extractors](https://github.com/yt-dlp/yt-dlp/commit/5271ef48c6f61c145e03e18e960995d2e651d205) ([#12404](https://github.com/yt-dlp/yt-dlp/issues/12404)) by [bashonly](https://github.com/bashonly), [dirkf](https://github.com/dirkf) +- **generic**: [Extract `live_status` for DASH manifest URLs](https://github.com/yt-dlp/yt-dlp/commit/19edaa44fcd375f54e63d6227b092f5252d3e889) ([#12256](https://github.com/yt-dlp/yt-dlp/issues/12256)) by [mp3butcher](https://github.com/mp3butcher) +- **globo**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/f8d0161455f00add65585ca1a476a7b5d56f5f96) ([#11795](https://github.com/yt-dlp/yt-dlp/issues/11795)) by [slipinthedove](https://github.com/slipinthedove), [YoshiTabletopGamer](https://github.com/YoshiTabletopGamer) +- **goplay**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/d59f14a0a7a8b55e6bf468237def62b73ab4a517) ([#12237](https://github.com/yt-dlp/yt-dlp/issues/12237)) by [alard](https://github.com/alard) +- **pbs**: [Support www.thirteen.org URLs](https://github.com/yt-dlp/yt-dlp/commit/9fb8ab2ff67fb699f60cce09163a580976e90c0e) ([#11191](https://github.com/yt-dlp/yt-dlp/issues/11191)) by [rohieb](https://github.com/rohieb) +- **reddit**: [Bypass gated subreddit warning](https://github.com/yt-dlp/yt-dlp/commit/6ca23ffaa4663cb552f937f0b1e9769b66db11bd) ([#12335](https://github.com/yt-dlp/yt-dlp/issues/12335)) by [bashonly](https://github.com/bashonly) +- **twitter**: [Fix syndication token generation](https://github.com/yt-dlp/yt-dlp/commit/14cd7f3443c6da4d49edaefcc12da9dee86e243e) ([#12107](https://github.com/yt-dlp/yt-dlp/issues/12107)) by [Grub4K](https://github.com/Grub4K), [pjrobertson](https://github.com/pjrobertson) +- **youtube** + - [Retry on more critical requests](https://github.com/yt-dlp/yt-dlp/commit/d48e612609d012abbea3785be4d26d78a014abb2) ([#12339](https://github.com/yt-dlp/yt-dlp/issues/12339)) by [coletdjnz](https://github.com/coletdjnz) + - [nsig workaround for `tce` player JS](https://github.com/yt-dlp/yt-dlp/commit/ec17fb16e8d69d4e3e10fb73bf3221be8570dfee) ([#12401](https://github.com/yt-dlp/yt-dlp/issues/12401)) by [bashonly](https://github.com/bashonly) +- **zdf**: [Extract more metadata](https://github.com/yt-dlp/yt-dlp/commit/241ace4f104d50fdf7638f9203927aefcf57a1f7) ([#9565](https://github.com/yt-dlp/yt-dlp/issues/9565)) by [StefanLobbenmeier](https://github.com/StefanLobbenmeier) (With fixes in [e7882b6](https://github.com/yt-dlp/yt-dlp/commit/e7882b682b959e476d8454911655b3e9b14c79b2) by [bashonly](https://github.com/bashonly)) + +#### Downloader changes +- **hls** + - [Fix `BYTERANGE` logic](https://github.com/yt-dlp/yt-dlp/commit/10b7ff68e98f17655e31952f6e17120b2d7dda96) ([#11972](https://github.com/yt-dlp/yt-dlp/issues/11972)) by [entourage8](https://github.com/entourage8) + - [Support `--write-pages` for m3u8 media playlists](https://github.com/yt-dlp/yt-dlp/commit/be69468752ff598cacee57bb80533deab2367a5d) ([#12333](https://github.com/yt-dlp/yt-dlp/issues/12333)) by [bashonly](https://github.com/bashonly) + - [Support `hls_media_playlist_data` format field](https://github.com/yt-dlp/yt-dlp/commit/c987be0acb6872c6561f28aa28171e803393d851) ([#12322](https://github.com/yt-dlp/yt-dlp/issues/12322)) by [bashonly](https://github.com/bashonly) + +#### Misc. changes +- [Improve Issue/PR templates](https://github.com/yt-dlp/yt-dlp/commit/517ddf3c3f12560ab93e3d36244dc82db9f97818) ([#11499](https://github.com/yt-dlp/yt-dlp/issues/11499)) by [seproDev](https://github.com/seproDev) (With fixes in [4ecb833](https://github.com/yt-dlp/yt-dlp/commit/4ecb833472c90e078567b561fb7c089f1aa9587b) by [bashonly](https://github.com/bashonly)) +- **cleanup**: Miscellaneous: [4985a40](https://github.com/yt-dlp/yt-dlp/commit/4985a4041770eaa0016271809a1fd950dc809a55) by [dirkf](https://github.com/dirkf), [Grub4K](https://github.com/Grub4K), [StefanLobbenmeier](https://github.com/StefanLobbenmeier) +- **docs**: [Add note to `supportedsites.md`](https://github.com/yt-dlp/yt-dlp/commit/01a63629a21781458dcbd38779898e117678f5ff) ([#12382](https://github.com/yt-dlp/yt-dlp/issues/12382)) by [seproDev](https://github.com/seproDev) +- **test**: download: [Validate and sort info dict fields](https://github.com/yt-dlp/yt-dlp/commit/208163447408c78673b08c172beafe5c310fb167) ([#12299](https://github.com/yt-dlp/yt-dlp/issues/12299)) by [bashonly](https://github.com/bashonly), [pzhlkj6612](https://github.com/pzhlkj6612) + ### 2025.01.26 #### Core changes diff --git a/yt-dlp/README.md b/yt-dlp/README.md index 0ac27c462f..e8ef1980a6 100644 --- a/yt-dlp/README.md +++ b/yt-dlp/README.md @@ -1525,7 +1525,7 @@ The available fields are: - `hasvid`: Gives priority to formats that have a video stream - `hasaud`: Gives priority to formats that have an audio stream - `ie_pref`: The format preference - - `lang`: The language preference + - `lang`: The language preference as determined by the extractor (e.g. original language preferred over audio description) - `quality`: The quality of the format - `source`: The preference of the source - `proto`: Protocol used for download (`https`/`ftps` > `http`/`ftp` > `m3u8_native`/`m3u8` > `http_dash_segments`> `websocket_frag` > `mms`/`rtsp` > `f4f`/`f4m`) diff --git a/yt-dlp/devscripts/make_supportedsites.py b/yt-dlp/devscripts/make_supportedsites.py index 01548ef97a..145f6d47f2 100644 --- a/yt-dlp/devscripts/make_supportedsites.py +++ b/yt-dlp/devscripts/make_supportedsites.py @@ -10,10 +10,21 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from devscripts.utils import get_filename_args, write_file from yt_dlp.extractor import list_extractor_classes +TEMPLATE = '''\ +# Supported sites + +Below is a list of all extractors that are currently included with yt-dlp. +If a site is not listed here, it might still be supported by yt-dlp's embed extraction or generic extractor. +Not all sites listed here are guaranteed to work; websites are constantly changing and sometimes this breaks yt-dlp's support for them. +The only reliable way to check if a site is supported is to try it. + +{ie_list} +''' + def main(): out = '\n'.join(ie.description() for ie in list_extractor_classes() if ie.IE_DESC is not False) - write_file(get_filename_args(), f'# Supported sites\n{out}\n') + write_file(get_filename_args(), TEMPLATE.format(ie_list=out)) if __name__ == '__main__': diff --git a/yt-dlp/devscripts/run_tests.py b/yt-dlp/devscripts/run_tests.py index eb614fe591..ebb3500b6c 100755 --- a/yt-dlp/devscripts/run_tests.py +++ b/yt-dlp/devscripts/run_tests.py @@ -25,7 +25,8 @@ def parse_args(): def run_tests(*tests, pattern=None, ci=False): - run_core = 'core' in tests or (not pattern and not tests) + # XXX: hatch uses `tests` if no arguments are passed + run_core = 'core' in tests or 'tests' in tests or (not pattern and not tests) run_download = 'download' in tests pytest_args = args.pytest_args or os.getenv('HATCH_TEST_ARGS', '') diff --git a/yt-dlp/supportedsites.md b/yt-dlp/supportedsites.md index 70909ef002..0924c88ffb 100644 --- a/yt-dlp/supportedsites.md +++ b/yt-dlp/supportedsites.md @@ -1,4 +1,10 @@ # Supported sites + +Below is a list of all extractors that are currently included with yt-dlp. +If a site is not listed here, it might still be supported by yt-dlp's embed extraction or generic extractor. +Not all sites listed here are guaranteed to work; websites are constantly changing and sometimes this breaks yt-dlp's support for them. +The only reliable way to check if a site is supported is to try it. + - **17live** - **17live:clip** - **1News**: 1news.co.nz article videos @@ -314,7 +320,8 @@ - **curiositystream**: [*curiositystream*](## "netrc machine") - **curiositystream:collections**: [*curiositystream*](## "netrc machine") - **curiositystream:series**: [*curiositystream*](## "netrc machine") - - **CWTV** + - **cwtv** + - **cwtv:movie** - **Cybrary**: [*cybrary*](## "netrc machine") - **CybraryCourse**: [*cybrary*](## "netrc machine") - **DacastPlaylist** @@ -349,6 +356,7 @@ - **DigitalConcertHall**: [*digitalconcerthall*](## "netrc machine") DigitalConcertHall extractor - **DigitallySpeaking** - **Digiteka** + - **Digiview** - **DiscogsReleasePlaylist** - **DiscoveryLife** - **DiscoveryNetworksDe** @@ -465,9 +473,9 @@ - **fptplay**: fptplay.vn - **FranceCulture** - **FranceInter** - - **FranceTV** + - **francetv** + - **francetv:site** - **francetvinfo.fr** - - **FranceTVSite** - **Freesound** - **freespeech.org** - **freetv:series** @@ -499,7 +507,7 @@ - **GediDigital** - **gem.cbc.ca**: [*cbcgem*](## "netrc machine") - **gem.cbc.ca:live** - - **gem.cbc.ca:playlist** + - **gem.cbc.ca:playlist**: [*cbcgem*](## "netrc machine") - **Genius** - **GeniusLyrics** - **Germanupa**: germanupa.de diff --git a/yt-dlp/yt_dlp/__init__.py b/yt-dlp/yt_dlp/__init__.py index c76fe27483..3819656e2c 100644 --- a/yt-dlp/yt_dlp/__init__.py +++ b/yt-dlp/yt_dlp/__init__.py @@ -295,18 +295,20 @@ def validate_options(opts): raise ValueError(f'invalid {key} retry sleep expression {expr!r}') # Bytes - def validate_bytes(name, value): + def validate_bytes(name, value, strict_positive=False): if value is None: return None numeric_limit = parse_bytes(value) - validate(numeric_limit is not None, 'rate limit', value) + validate(numeric_limit is not None, name, value) + if strict_positive: + validate_positive(name, numeric_limit, True) return numeric_limit - opts.ratelimit = validate_bytes('rate limit', opts.ratelimit) + opts.ratelimit = validate_bytes('rate limit', opts.ratelimit, True) opts.throttledratelimit = validate_bytes('throttled rate limit', opts.throttledratelimit) opts.min_filesize = validate_bytes('min filesize', opts.min_filesize) opts.max_filesize = validate_bytes('max filesize', opts.max_filesize) - opts.buffersize = validate_bytes('buffer size', opts.buffersize) + opts.buffersize = validate_bytes('buffer size', opts.buffersize, True) opts.http_chunk_size = validate_bytes('http chunk size', opts.http_chunk_size) # Output templates diff --git a/yt-dlp/yt_dlp/downloader/hls.py b/yt-dlp/yt_dlp/downloader/hls.py index 7a47f8f836..1f36a07f5f 100644 --- a/yt-dlp/yt_dlp/downloader/hls.py +++ b/yt-dlp/yt_dlp/downloader/hls.py @@ -16,6 +16,7 @@ from ..utils import ( update_url_query, urljoin, ) +from ..utils._utils import _request_dump_filename class HlsFD(FragmentFD): @@ -80,7 +81,15 @@ class HlsFD(FragmentFD): self.to_screen(f'[{self.FD_NAME}] Downloading m3u8 manifest') urlh = self.ydl.urlopen(self._prepare_url(info_dict, man_url)) man_url = urlh.url - s = urlh.read().decode('utf-8', 'ignore') + s_bytes = urlh.read() + if self.params.get('write_pages'): + dump_filename = _request_dump_filename( + man_url, info_dict['id'], None, + trim_length=self.params.get('trim_file_name')) + self.to_screen(f'[{self.FD_NAME}] Saving request to {dump_filename}') + with open(dump_filename, 'wb') as outf: + outf.write(s_bytes) + s = s_bytes.decode('utf-8', 'ignore') can_download, message = self.can_download(s, info_dict, self.params.get('allow_unplayable_formats')), None if can_download: diff --git a/yt-dlp/yt_dlp/extractor/cbc.py b/yt-dlp/yt_dlp/extractor/cbc.py index c0cf3da3de..e63c608fc9 100644 --- a/yt-dlp/yt_dlp/extractor/cbc.py +++ b/yt-dlp/yt_dlp/extractor/cbc.py @@ -14,16 +14,18 @@ from ..utils import ( js_to_json, mimetype2ext, orderedSet, + parse_age_limit, parse_iso8601, replace_extension, smuggle_url, strip_or_none, - traverse_obj, try_get, + unified_timestamp, update_url, url_basename, url_or_none, ) +from ..utils.traversal import require, traverse_obj, trim_str class CBCIE(InfoExtractor): @@ -516,9 +518,43 @@ class CBCPlayerPlaylistIE(InfoExtractor): return self.playlist_result(entries(), playlist_id) -class CBCGemIE(InfoExtractor): +class CBCGemBaseIE(InfoExtractor): + _NETRC_MACHINE = 'cbcgem' + _GEO_COUNTRIES = ['CA'] + + def _call_show_api(self, item_id, display_id=None): + return self._download_json( + f'https://services.radio-canada.ca/ott/catalog/v2/gem/show/{item_id}', + display_id or item_id, query={'device': 'web'}) + + def _extract_item_info(self, item_info): + episode_number = None + title = traverse_obj(item_info, ('title', {str})) + if title and (mobj := re.match(r'(?P\d+)\. (?P.+)', title)): + episode_number = int_or_none(mobj.group('episode')) + title = mobj.group('title') + + return { + 'episode_number': episode_number, + **traverse_obj(item_info, { + 'id': ('url', {str}), + 'episode_id': ('url', {str}), + 'description': ('description', {str}), + 'thumbnail': ('images', 'card', 'url', {url_or_none}, {update_url(query=None)}), + 'episode_number': ('episodeNumber', {int_or_none}), + 'duration': ('metadata', 'duration', {int_or_none}), + 'release_timestamp': ('metadata', 'airDate', {unified_timestamp}), + 'timestamp': ('metadata', 'availabilityDate', {unified_timestamp}), + 'age_limit': ('metadata', 'rating', {trim_str(start='C')}, {parse_age_limit}), + }), + 'episode': title, + 'title': title, + } + + +class CBCGemIE(CBCGemBaseIE): IE_NAME = 'gem.cbc.ca' - _VALID_URL = r'https?://gem\.cbc\.ca/(?:media/)?(?P<id>[0-9a-z-]+/s[0-9]+[a-z][0-9]+)' + _VALID_URL = r'https?://gem\.cbc\.ca/(?:media/)?(?P<id>[0-9a-z-]+/s(?P<season>[0-9]+)[a-z][0-9]+)' _TESTS = [{ # This is a normal, public, TV show video 'url': 'https://gem.cbc.ca/media/schitts-creek/s06e01', @@ -529,7 +565,7 @@ class CBCGemIE(InfoExtractor): 'description': 'md5:929868d20021c924020641769eb3e7f1', 'thumbnail': r're:https://images\.radio-canada\.ca/[^#?]+/cbc_schitts_creek_season_06e01_thumbnail_v01\.jpg', 'duration': 1324, - 'categories': ['comedy'], + 'genres': ['Comédie et humour'], 'series': 'Schitt\'s Creek', 'season': 'Season 6', 'season_number': 6, @@ -537,9 +573,10 @@ class CBCGemIE(InfoExtractor): 'episode_number': 1, 'episode_id': 'schitts-creek/s06e01', 'upload_date': '20210618', - 'timestamp': 1623988800, + 'timestamp': 1623974400, 'release_date': '20200107', - 'release_timestamp': 1578427200, + 'release_timestamp': 1578355200, + 'age_limit': 14, }, 'params': {'format': 'bv'}, }, { @@ -557,12 +594,13 @@ class CBCGemIE(InfoExtractor): 'episode_number': 1, 'episode': 'The Cup Runneth Over', 'episode_id': 'schitts-creek/s01e01', - 'duration': 1309, - 'categories': ['comedy'], + 'duration': 1308, + 'genres': ['Comédie et humour'], 'upload_date': '20210617', - 'timestamp': 1623902400, - 'release_date': '20151124', - 'release_timestamp': 1448323200, + 'timestamp': 1623888000, + 'release_date': '20151123', + 'release_timestamp': 1448236800, + 'age_limit': 14, }, 'params': {'format': 'bv'}, }, { @@ -570,9 +608,7 @@ class CBCGemIE(InfoExtractor): 'only_matching': True, }] - _GEO_COUNTRIES = ['CA'] _TOKEN_API_KEY = '3f4beddd-2061-49b0-ae80-6f1f2ed65b37' - _NETRC_MACHINE = 'cbcgem' _claims_token = None def _new_claims_token(self, email, password): @@ -634,10 +670,12 @@ class CBCGemIE(InfoExtractor): self._claims_token = self.cache.load(self._NETRC_MACHINE, 'claims_token') def _real_extract(self, url): - video_id = self._match_id(url) - video_info = self._download_json( - f'https://services.radio-canada.ca/ott/cbc-api/v2/assets/{video_id}', - video_id, expected_status=426) + video_id, season_number = self._match_valid_url(url).group('id', 'season') + video_info = self._call_show_api(video_id) + item_info = traverse_obj(video_info, ( + 'content', ..., 'lineups', ..., 'items', + lambda _, v: v['url'] == video_id, any, {require('item info')})) + media_id = item_info['idMedia'] email, password = self._get_login_info() if email and password: @@ -645,7 +683,20 @@ class CBCGemIE(InfoExtractor): headers = {'x-claims-token': claims_token} else: headers = {} - m3u8_info = self._download_json(video_info['playSession']['url'], video_id, headers=headers) + + m3u8_info = self._download_json( + 'https://services.radio-canada.ca/media/validation/v2/', + video_id, headers=headers, query={ + 'appCode': 'gem', + 'connectionType': 'hd', + 'deviceType': 'ipad', + 'multibitrate': 'true', + 'output': 'json', + 'tech': 'hls', + 'manifestVersion': '2', + 'manifestType': 'desktop', + 'idMedia': media_id, + }) if m3u8_info.get('errorCode') == 1: self.raise_geo_restricted(countries=['CA']) @@ -671,26 +722,20 @@ class CBCGemIE(InfoExtractor): fmt['preference'] = -2 return { + 'season_number': int_or_none(season_number), + **traverse_obj(video_info, { + 'series': ('title', {str}), + 'season_number': ('structuredMetadata', 'partofSeason', 'seasonNumber', {int_or_none}), + 'genres': ('structuredMetadata', 'genre', ..., {str}), + }), + **self._extract_item_info(item_info), 'id': video_id, 'episode_id': video_id, 'formats': formats, - **traverse_obj(video_info, { - 'title': ('title', {str}), - 'episode': ('title', {str}), - 'description': ('description', {str}), - 'thumbnail': ('image', {url_or_none}), - 'series': ('series', {str}), - 'season_number': ('season', {int_or_none}), - 'episode_number': ('episode', {int_or_none}), - 'duration': ('duration', {int_or_none}), - 'categories': ('category', {str}, all), - 'release_timestamp': ('airDate', {int_or_none(scale=1000)}), - 'timestamp': ('availableDate', {int_or_none(scale=1000)}), - }), } -class CBCGemPlaylistIE(InfoExtractor): +class CBCGemPlaylistIE(CBCGemBaseIE): IE_NAME = 'gem.cbc.ca:playlist' _VALID_URL = r'https?://gem\.cbc\.ca/(?:media/)?(?P<id>(?P<show>[0-9a-z-]+)/s(?P<season>[0-9]+))/?(?:[?#]|$)' _TESTS = [{ @@ -700,70 +745,35 @@ class CBCGemPlaylistIE(InfoExtractor): 'info_dict': { 'id': 'schitts-creek/s06', 'title': 'Season 6', - 'description': 'md5:6a92104a56cbeb5818cc47884d4326a2', 'series': 'Schitt\'s Creek', 'season_number': 6, 'season': 'Season 6', - 'thumbnail': 'https://images.radio-canada.ca/v1/synps-cbc/season/perso/cbc_schitts_creek_season_06_carousel_v03.jpg?impolicy=ott&im=Resize=(_Size_)&quality=75', }, }, { 'url': 'https://gem.cbc.ca/schitts-creek/s06', 'only_matching': True, }] - _API_BASE = 'https://services.radio-canada.ca/ott/cbc-api/v2/shows/' + + def _entries(self, season_info): + for episode in traverse_obj(season_info, ('items', lambda _, v: v['url'])): + yield self.url_result( + f'https://gem.cbc.ca/media/{episode["url"]}', CBCGemIE, + **self._extract_item_info(episode)) def _real_extract(self, url): - match = self._match_valid_url(url) - season_id = match.group('id') - show = match.group('show') - show_info = self._download_json(self._API_BASE + show, season_id, expected_status=426) - season = int(match.group('season')) + season_id, show, season = self._match_valid_url(url).group('id', 'show', 'season') + show_info = self._call_show_api(show, display_id=season_id) + season_info = traverse_obj(show_info, ( + 'content', ..., 'lineups', + lambda _, v: v['seasonNumber'] == int(season), any, {require('season info')})) - season_info = next((s for s in show_info['seasons'] if s.get('season') == season), None) - - if season_info is None: - raise ExtractorError(f'Couldn\'t find season {season} of {show}') - - episodes = [] - for episode in season_info['assets']: - episodes.append({ - '_type': 'url_transparent', - 'ie_key': 'CBCGem', - 'url': 'https://gem.cbc.ca/media/' + episode['id'], - 'id': episode['id'], - 'title': episode.get('title'), - 'description': episode.get('description'), - 'thumbnail': episode.get('image'), - 'series': episode.get('series'), - 'season_number': episode.get('season'), - 'season': season_info['title'], - 'season_id': season_info.get('id'), - 'episode_number': episode.get('episode'), - 'episode': episode.get('title'), - 'episode_id': episode['id'], - 'duration': episode.get('duration'), - 'categories': [episode.get('category')], - }) - - thumbnail = None - tn_uri = season_info.get('image') - # the-national was observed to use a "data:image/png;base64" - # URI for their 'image' value. The image was 1x1, and is - # probably just a placeholder, so it is ignored. - if tn_uri is not None and not tn_uri.startswith('data:'): - thumbnail = tn_uri - - return { - '_type': 'playlist', - 'entries': episodes, - 'id': season_id, - 'title': season_info['title'], - 'description': season_info.get('description'), - 'thumbnail': thumbnail, - 'series': show_info.get('title'), - 'season_number': season_info.get('season'), - 'season': season_info['title'], - } + return self.playlist_result( + self._entries(season_info), season_id, + **traverse_obj(season_info, { + 'title': ('title', {str}), + 'season': ('title', {str}), + 'season_number': ('seasonNumber', {int_or_none}), + }), series=traverse_obj(show_info, ('title', {str}))) class CBCGemLiveIE(InfoExtractor): diff --git a/yt-dlp/yt_dlp/extractor/common.py b/yt-dlp/yt_dlp/extractor/common.py index 3e7734ce1b..8d199b353c 100644 --- a/yt-dlp/yt_dlp/extractor/common.py +++ b/yt-dlp/yt_dlp/extractor/common.py @@ -2,7 +2,6 @@ import base64 import collections import functools import getpass -import hashlib import http.client import http.cookiejar import http.cookies @@ -78,7 +77,6 @@ from ..utils import ( parse_iso8601, parse_m3u8_attributes, parse_resolution, - sanitize_filename, sanitize_url, smuggle_url, str_or_none, @@ -100,6 +98,7 @@ from ..utils import ( xpath_text, xpath_with_ns, ) +from ..utils._utils import _request_dump_filename class InfoExtractor: @@ -1022,23 +1021,6 @@ class InfoExtractor: 'Visit http://blocklist.rkn.gov.ru/ for a block reason.', expected=True) - def _request_dump_filename(self, url, video_id, data=None): - if data is not None: - data = hashlib.md5(data).hexdigest() - basen = join_nonempty(video_id, data, url, delim='_') - trim_length = self.get_param('trim_file_name') or 240 - if len(basen) > trim_length: - h = '___' + hashlib.md5(basen.encode()).hexdigest() - basen = basen[:trim_length - len(h)] + h - filename = sanitize_filename(f'{basen}.dump', restricted=True) - # Working around MAX_PATH limitation on Windows (see - # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx) - if os.name == 'nt': - absfilepath = os.path.abspath(filename) - if len(absfilepath) > 259: - filename = fR'\\?\{absfilepath}' - return filename - def __decode_webpage(self, webpage_bytes, encoding, headers): if not encoding: encoding = self._guess_encoding_from_content(headers.get('Content-Type', ''), webpage_bytes) @@ -1067,7 +1049,9 @@ class InfoExtractor: if self.get_param('write_pages'): if isinstance(url_or_request, Request): data = self._create_request(url_or_request, data).data - filename = self._request_dump_filename(urlh.url, video_id, data) + filename = _request_dump_filename( + urlh.url, video_id, data, + trim_length=self.get_param('trim_file_name')) self.to_screen(f'Saving request to {filename}') with open(filename, 'wb') as outf: outf.write(webpage_bytes) @@ -1128,7 +1112,9 @@ class InfoExtractor: impersonate=None, require_impersonation=False): if self.get_param('load_pages'): url_or_request = self._create_request(url_or_request, data, headers, query) - filename = self._request_dump_filename(url_or_request.url, video_id, url_or_request.data) + filename = _request_dump_filename( + url_or_request.url, video_id, url_or_request.data, + trim_length=self.get_param('trim_file_name')) self.to_screen(f'Loading request from {filename}') try: with open(filename, 'rb') as dumpf: diff --git a/yt-dlp/yt_dlp/extractor/dreisat.py b/yt-dlp/yt_dlp/extractor/dreisat.py index 4b0a269b98..376ff672df 100644 --- a/yt-dlp/yt_dlp/extractor/dreisat.py +++ b/yt-dlp/yt_dlp/extractor/dreisat.py @@ -1,10 +1,24 @@ -from .zdf import ZDFIE +from .zdf import ZDFBaseIE -class DreiSatIE(ZDFIE): # XXX: Do not subclass from concrete IE +class DreiSatIE(ZDFBaseIE): IE_NAME = '3sat' _VALID_URL = r'https?://(?:www\.)?3sat\.de/(?:[^/]+/)*(?P<id>[^/?#&]+)\.html' _TESTS = [{ + 'url': 'https://www.3sat.de/dokumentation/reise/traumziele-suedostasiens-die-philippinen-und-vietnam-102.html', + 'info_dict': { + 'id': '231124_traumziele_philippinen_und_vietnam_dokreise', + 'ext': 'mp4', + 'title': 'Traumziele Südostasiens (1/2): Die Philippinen und Vietnam', + 'description': 'md5:26329ce5197775b596773b939354079d', + 'duration': 2625.0, + 'thumbnail': 'https://www.3sat.de/assets/traumziele-suedostasiens-die-philippinen-und-vietnam-100~2400x1350?cb=1699870351148', + 'episode': 'Traumziele Südostasiens (1/2): Die Philippinen und Vietnam', + 'episode_id': 'POS_cc7ff51c-98cf-4d12-b99d-f7a551de1c95', + 'timestamp': 1738593000, + 'upload_date': '20250203', + }, + }, { # Same as https://www.zdf.de/dokumentation/ab-18/10-wochen-sommer-102.html 'url': 'https://www.3sat.de/film/ab-18/10-wochen-sommer-108.html', 'md5': '0aff3e7bc72c8813f5e0fae333316a1d', @@ -17,6 +31,7 @@ class DreiSatIE(ZDFIE): # XXX: Do not subclass from concrete IE 'timestamp': 1608604200, 'upload_date': '20201222', }, + 'skip': '410 Gone', }, { 'url': 'https://www.3sat.de/gesellschaft/schweizweit/waidmannsheil-100.html', 'info_dict': { @@ -30,6 +45,7 @@ class DreiSatIE(ZDFIE): # XXX: Do not subclass from concrete IE 'params': { 'skip_download': True, }, + 'skip': '404 Not Found', }, { # Same as https://www.zdf.de/filme/filme-sonstige/der-hauptmann-112.html 'url': 'https://www.3sat.de/film/spielfilm/der-hauptmann-100.html', @@ -39,3 +55,14 @@ class DreiSatIE(ZDFIE): # XXX: Do not subclass from concrete IE 'url': 'https://www.3sat.de/wissen/nano/nano-21-mai-2019-102.html', 'only_matching': True, }] + + def _real_extract(self, url): + video_id = self._match_id(url) + + webpage = self._download_webpage(url, video_id, fatal=False) + if webpage: + player = self._extract_player(webpage, url, fatal=False) + if player: + return self._extract_regular(url, player, video_id) + + return self._extract_mobile(video_id) diff --git a/yt-dlp/yt_dlp/extractor/francetv.py b/yt-dlp/yt_dlp/extractor/francetv.py index c6036b306f..5c9f8e36dd 100644 --- a/yt-dlp/yt_dlp/extractor/francetv.py +++ b/yt-dlp/yt_dlp/extractor/francetv.py @@ -9,6 +9,7 @@ from ..utils import ( ExtractorError, clean_html, determine_ext, + extract_attributes, filter_dict, format_field, int_or_none, @@ -18,7 +19,7 @@ from ..utils import ( unsmuggle_url, url_or_none, ) -from ..utils.traversal import traverse_obj +from ..utils.traversal import find_element, traverse_obj class FranceTVBaseInfoExtractor(InfoExtractor): @@ -460,11 +461,16 @@ class FranceTVInfoIE(FranceTVBaseInfoExtractor): self.url_result(dailymotion_url, DailymotionIE.ie_key()) for dailymotion_url in dailymotion_urls]) - video_id = self._search_regex( - (r'player\.load[^;]+src:\s*["\']([^"\']+)', - r'id-video=([^@]+@[^"]+)', - r'<a[^>]+href="(?:https?:)?//videos\.francetv\.fr/video/([^@]+@[^"]+)"', - r'(?:data-id|<figure[^<]+\bid)=["\']([\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})'), - webpage, 'video id') + video_id = ( + traverse_obj(webpage, ( + {find_element(tag='button', attr='data-cy', value='francetv-player-wrapper', html=True)}, + {extract_attributes}, 'id')) + or self._search_regex( + (r'player\.load[^;]+src:\s*["\']([^"\']+)', + r'id-video=([^@]+@[^"]+)', + r'<a[^>]+href="(?:https?:)?//videos\.francetv\.fr/video/([^@]+@[^"]+)"', + r'(?:data-id|<figure[^<]+\bid)=["\']([\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})'), + webpage, 'video id') + ) return self._make_url_result(video_id, url=url) diff --git a/yt-dlp/yt_dlp/extractor/pbs.py b/yt-dlp/yt_dlp/extractor/pbs.py index 2f839a2e9d..53b1994156 100644 --- a/yt-dlp/yt_dlp/extractor/pbs.py +++ b/yt-dlp/yt_dlp/extractor/pbs.py @@ -501,7 +501,7 @@ class PBSIE(InfoExtractor): r"div\s*:\s*'videoembed'\s*,\s*mediaid\s*:\s*'(\d+)'", # frontline video embed r'class="coveplayerid">([^<]+)<', # coveplayer r'<section[^>]+data-coveid="(\d+)"', # coveplayer from http://www.pbs.org/wgbh/frontline/film/real-csi/ - r'\bclass="passportcoveplayer"[^>]+\bdata-media="(\d+)', # https://www.thirteen.org/programs/the-woodwrights-shop/who-wrote-the-book-of-sloyd-fggvvq/ + r'\sclass="passportcoveplayer"[^>]*\sdata-media="(\d+)', # https://www.thirteen.org/programs/the-woodwrights-shop/who-wrote-the-book-of-sloyd-fggvvq/ r'<input type="hidden" id="pbs_video_id_[0-9]+" value="([0-9]+)"/>', # jwplayer r"(?s)window\.PBS\.playerConfig\s*=\s*{.*?id\s*:\s*'([0-9]+)',", r'<div[^>]+\bdata-cove-id=["\'](\d+)"', # http://www.pbs.org/wgbh/roadshow/watch/episode/2105-indianapolis-hour-2/ diff --git a/yt-dlp/yt_dlp/extractor/youtube.py b/yt-dlp/yt_dlp/extractor/youtube.py index 738ade1b02..4dc80fe28e 100644 --- a/yt-dlp/yt_dlp/extractor/youtube.py +++ b/yt-dlp/yt_dlp/extractor/youtube.py @@ -857,6 +857,18 @@ class YoutubeBaseInfoExtractor(InfoExtractor): } return filter_dict(headers) + def _download_webpage_with_retries(self, *args, retry_fatal=False, retry_on_status=None, **kwargs): + for retry in self.RetryManager(fatal=retry_fatal): + try: + return self._download_webpage(*args, **kwargs) + except ExtractorError as e: + if isinstance(e.cause, network_exceptions): + if not isinstance(e.cause, HTTPError) or e.cause.status not in (retry_on_status or (403, 429)): + retry.error = e + continue + self._error_or_warning(e, fatal=retry_fatal) + break + def _download_ytcfg(self, client, video_id): url = { 'web': 'https://www.youtube.com', @@ -866,8 +878,8 @@ class YoutubeBaseInfoExtractor(InfoExtractor): }.get(client) if not url: return {} - webpage = self._download_webpage( - url, video_id, fatal=False, note=f'Downloading {client.replace("_", " ").strip()} client config', + webpage = self._download_webpage_with_retries( + url, video_id, note=f'Downloading {client.replace("_", " ").strip()} client config', headers=traverse_obj(self._get_default_ytcfg(client), { 'User-Agent': ('INNERTUBE_CONTEXT', 'client', 'userAgent', {str}), })) @@ -3135,15 +3147,22 @@ class YoutubeIE(YoutubeBaseInfoExtractor): get_all=False, expected_type=str) if not player_url: return + # TODO: Add proper support for the 'tce' variant players + # See https://github.com/yt-dlp/yt-dlp/issues/12398 + if '/player_ias_tce.vflset/' in player_url: + self.write_debug(f'Modifying tce player URL: {player_url}') + player_url = player_url.replace('/player_ias_tce.vflset/', '/player_ias.vflset/') return urljoin('https://www.youtube.com', player_url) def _download_player_url(self, video_id, fatal=False): - res = self._download_webpage( + iframe_webpage = self._download_webpage_with_retries( 'https://www.youtube.com/iframe_api', - note='Downloading iframe API JS', video_id=video_id, fatal=fatal) - if res: + note='Downloading iframe API JS', + video_id=video_id, retry_fatal=fatal) + + if iframe_webpage: player_version = self._search_regex( - r'player\\?/([0-9a-fA-F]{8})\\?/', res, 'player version', fatal=fatal) + r'player\\?/([0-9a-fA-F]{8})\\?/', iframe_webpage, 'player version', fatal=fatal) if player_version: return f'https://www.youtube.com/s/player/{player_version}/player_ias.vflset/en_US/base.js' @@ -3369,7 +3388,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): def _extract_n_function_code(self, video_id, player_url): player_id = self._extract_player_info(player_url) - func_code = self.cache.load('youtube-nsig', player_id, min_ver='2024.07.09') + func_code = self.cache.load('youtube-nsig', player_id, min_ver='2025.02.19') jscode = func_code or self._load_player(video_id, player_url) jsi = JSInterpreter(jscode) @@ -4556,8 +4575,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): pp = self._configuration_arg('player_params', [None], casesense=True)[0] if pp: query['pp'] = pp - webpage = self._download_webpage( - webpage_url, video_id, fatal=False, query=query) + webpage = self._download_webpage_with_retries(webpage_url, video_id, query=query) master_ytcfg = self.extract_ytcfg(video_id, webpage) or self._get_default_ytcfg() diff --git a/yt-dlp/yt_dlp/extractor/zdf.py b/yt-dlp/yt_dlp/extractor/zdf.py index b64a88f6c0..4bef1017cc 100644 --- a/yt-dlp/yt_dlp/extractor/zdf.py +++ b/yt-dlp/yt_dlp/extractor/zdf.py @@ -137,6 +137,116 @@ class ZDFBaseIE(InfoExtractor): group='json'), video_id) + def _extract_entry(self, url, player, content, video_id): + title = content.get('title') or content['teaserHeadline'] + + t = content['mainVideoContent']['http://zdf.de/rels/target'] + ptmd_path = traverse_obj(t, ( + (('streams', 'default'), None), + ('http://zdf.de/rels/streams/ptmd', 'http://zdf.de/rels/streams/ptmd-template'), + ), get_all=False) + if not ptmd_path: + raise ExtractorError('Could not extract ptmd_path') + + info = self._extract_ptmd( + urljoin(url, ptmd_path.replace('{playerId}', 'android_native_5')), video_id, player['apiToken'], url) + + thumbnails = [] + layouts = try_get( + content, lambda x: x['teaserImageRef']['layouts'], dict) + if layouts: + for layout_key, layout_url in layouts.items(): + layout_url = url_or_none(layout_url) + if not layout_url: + continue + thumbnail = { + 'url': layout_url, + 'format_id': layout_key, + } + mobj = re.search(r'(?P<width>\d+)x(?P<height>\d+)', layout_key) + if mobj: + thumbnail.update({ + 'width': int(mobj.group('width')), + 'height': int(mobj.group('height')), + }) + thumbnails.append(thumbnail) + + chapter_marks = t.get('streamAnchorTag') or [] + chapter_marks.append({'anchorOffset': int_or_none(t.get('duration'))}) + chapters = [{ + 'start_time': chap.get('anchorOffset'), + 'end_time': next_chap.get('anchorOffset'), + 'title': chap.get('anchorLabel'), + } for chap, next_chap in zip(chapter_marks, chapter_marks[1:])] + + return merge_dicts(info, { + 'title': title, + 'description': content.get('leadParagraph') or content.get('teasertext'), + 'duration': int_or_none(t.get('duration')), + 'timestamp': unified_timestamp(content.get('editorialDate')), + 'thumbnails': thumbnails, + 'chapters': chapters or None, + 'episode': title, + **traverse_obj(content, ('programmeItem', 0, 'http://zdf.de/rels/target', { + 'series_id': ('http://zdf.de/rels/cmdm/series', 'seriesUuid', {str}), + 'series': ('http://zdf.de/rels/cmdm/series', 'seriesTitle', {str}), + 'season': ('http://zdf.de/rels/cmdm/season', 'seasonTitle', {str}), + 'season_number': ('http://zdf.de/rels/cmdm/season', 'seasonNumber', {int_or_none}), + 'season_id': ('http://zdf.de/rels/cmdm/season', 'seasonUuid', {str}), + 'episode_number': ('episodeNumber', {int_or_none}), + 'episode_id': ('contentId', {str}), + })), + }) + + def _extract_regular(self, url, player, video_id, query=None): + player_url = player['content'] + + content = self._call_api( + update_url_query(player_url, query), + video_id, 'content', player['apiToken'], url) + + return self._extract_entry(player_url, player, content, video_id) + + def _extract_mobile(self, video_id): + video = self._download_v2_doc(video_id) + + formats = [] + formitaeten = try_get(video, lambda x: x['document']['formitaeten'], list) + document = formitaeten and video['document'] + if formitaeten: + title = document['titel'] + content_id = document['basename'] + + format_urls = set() + for f in formitaeten or []: + self._extract_format(content_id, formats, format_urls, f) + + thumbnails = [] + teaser_bild = document.get('teaserBild') + if isinstance(teaser_bild, dict): + for thumbnail_key, thumbnail in teaser_bild.items(): + thumbnail_url = try_get( + thumbnail, lambda x: x['url'], str) + if thumbnail_url: + thumbnails.append({ + 'url': thumbnail_url, + 'id': thumbnail_key, + 'width': int_or_none(thumbnail.get('width')), + 'height': int_or_none(thumbnail.get('height')), + }) + + return { + 'id': content_id, + 'title': title, + 'description': document.get('beschreibung'), + 'duration': int_or_none(document.get('length')), + 'timestamp': unified_timestamp(document.get('date')) or unified_timestamp( + try_get(video, lambda x: x['meta']['editorialDate'], str)), + 'thumbnails': thumbnails, + 'subtitles': self._extract_subtitles(document), + 'formats': formats, + } + class ZDFIE(ZDFBaseIE): _VALID_URL = r'https?://www\.zdf\.de/(?:[^/]+/)*(?P<id>[^/?#&]+)\.html' @@ -306,121 +416,6 @@ class ZDFIE(ZDFBaseIE): }, }] - def _extract_entry(self, url, player, content, video_id): - title = content.get('title') or content['teaserHeadline'] - - t = content['mainVideoContent']['http://zdf.de/rels/target'] - ptmd_path = traverse_obj(t, ( - (('streams', 'default'), None), - ('http://zdf.de/rels/streams/ptmd', 'http://zdf.de/rels/streams/ptmd-template'), - ), get_all=False) - if not ptmd_path: - raise ExtractorError('Could not extract ptmd_path') - - info = self._extract_ptmd( - urljoin(url, ptmd_path.replace('{playerId}', 'android_native_5')), video_id, player['apiToken'], url) - - thumbnails = [] - layouts = try_get( - content, lambda x: x['teaserImageRef']['layouts'], dict) - if layouts: - for layout_key, layout_url in layouts.items(): - layout_url = url_or_none(layout_url) - if not layout_url: - continue - thumbnail = { - 'url': layout_url, - 'format_id': layout_key, - } - mobj = re.search(r'(?P<width>\d+)x(?P<height>\d+)', layout_key) - if mobj: - thumbnail.update({ - 'width': int(mobj.group('width')), - 'height': int(mobj.group('height')), - }) - thumbnails.append(thumbnail) - - chapter_marks = t.get('streamAnchorTag') or [] - chapter_marks.append({'anchorOffset': int_or_none(t.get('duration'))}) - chapters = [{ - 'start_time': chap.get('anchorOffset'), - 'end_time': next_chap.get('anchorOffset'), - 'title': chap.get('anchorLabel'), - } for chap, next_chap in zip(chapter_marks, chapter_marks[1:])] - - return merge_dicts(info, { - 'title': title, - 'description': content.get('leadParagraph') or content.get('teasertext'), - 'duration': int_or_none(t.get('duration')), - 'timestamp': unified_timestamp(content.get('editorialDate')), - 'thumbnails': thumbnails, - 'chapters': chapters or None, - 'episode': title, - **traverse_obj(content, ('programmeItem', 0, 'http://zdf.de/rels/target', { - 'series_id': ('http://zdf.de/rels/cmdm/series', 'seriesUuid', {str}), - 'series': ('http://zdf.de/rels/cmdm/series', 'seriesTitle', {str}), - 'season': ('http://zdf.de/rels/cmdm/season', 'seasonTitle', {str}), - 'season_number': ('http://zdf.de/rels/cmdm/season', 'seasonNumber', {int_or_none}), - 'season_id': ('http://zdf.de/rels/cmdm/season', 'seasonUuid', {str}), - 'episode_number': ('episodeNumber', {int_or_none}), - 'episode_id': ('contentId', {str}), - })), - }) - - def _extract_regular(self, url, player, video_id): - player_url = player['content'] - - try: - content = self._call_api( - update_url_query(player_url, {'profile': 'player-3'}), - video_id, 'content', player['apiToken'], url) - except ExtractorError as e: - self.report_warning(f'{video_id}: {e.orig_msg}; retrying with v2 profile') - content = self._call_api( - player_url, video_id, 'content', player['apiToken'], url) - - return self._extract_entry(player_url, player, content, video_id) - - def _extract_mobile(self, video_id): - video = self._download_v2_doc(video_id) - - formats = [] - formitaeten = try_get(video, lambda x: x['document']['formitaeten'], list) - document = formitaeten and video['document'] - if formitaeten: - title = document['titel'] - content_id = document['basename'] - - format_urls = set() - for f in formitaeten or []: - self._extract_format(content_id, formats, format_urls, f) - - thumbnails = [] - teaser_bild = document.get('teaserBild') - if isinstance(teaser_bild, dict): - for thumbnail_key, thumbnail in teaser_bild.items(): - thumbnail_url = try_get( - thumbnail, lambda x: x['url'], str) - if thumbnail_url: - thumbnails.append({ - 'url': thumbnail_url, - 'id': thumbnail_key, - 'width': int_or_none(thumbnail.get('width')), - 'height': int_or_none(thumbnail.get('height')), - }) - - return { - 'id': content_id, - 'title': title, - 'description': document.get('beschreibung'), - 'duration': int_or_none(document.get('length')), - 'timestamp': unified_timestamp(document.get('date')) or unified_timestamp( - try_get(video, lambda x: x['meta']['editorialDate'], str)), - 'thumbnails': thumbnails, - 'subtitles': self._extract_subtitles(document), - 'formats': formats, - } - def _real_extract(self, url): video_id = self._match_id(url) @@ -428,7 +423,7 @@ class ZDFIE(ZDFBaseIE): if webpage: player = self._extract_player(webpage, url, fatal=False) if player: - return self._extract_regular(url, player, video_id) + return self._extract_regular(url, player, video_id, query={'profile': 'player-3'}) return self._extract_mobile(video_id) @@ -474,7 +469,8 @@ class ZDFChannelIE(ZDFBaseIE): 'title': ('titel', {str}), 'description': ('beschreibung', {str}), 'duration': ('length', {float_or_none}), - # TODO: seasonNumber and episodeNumber can be extracted but need to also be in ZDFIE + 'season_number': ('seasonNumber', {int_or_none}), + 'episode_number': ('episodeNumber', {int_or_none}), })) def _entries(self, data, document_id): diff --git a/yt-dlp/yt_dlp/utils/_utils.py b/yt-dlp/yt_dlp/utils/_utils.py index a71a381e54..c6a90a0ddb 100644 --- a/yt-dlp/yt_dlp/utils/_utils.py +++ b/yt-dlp/yt_dlp/utils/_utils.py @@ -5631,6 +5631,24 @@ def filesize_from_tbr(tbr, duration): return int(duration * tbr * (1000 / 8)) +def _request_dump_filename(url, video_id, data=None, trim_length=None): + if data is not None: + data = hashlib.md5(data).hexdigest() + basen = join_nonempty(video_id, data, url, delim='_') + trim_length = trim_length or 240 + if len(basen) > trim_length: + h = '___' + hashlib.md5(basen.encode()).hexdigest() + basen = basen[:trim_length - len(h)] + h + filename = sanitize_filename(f'{basen}.dump', restricted=True) + # Working around MAX_PATH limitation on Windows (see + # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx) + if os.name == 'nt': + absfilepath = os.path.abspath(filename) + if len(absfilepath) > 259: + filename = fR'\\?\{absfilepath}' + return filename + + # XXX: Temporary class _YDLLogger: def __init__(self, ydl=None): diff --git a/yt-dlp/yt_dlp/version.py b/yt-dlp/yt_dlp/version.py index 4f7a2ec90b..7346ca49c9 100644 --- a/yt-dlp/yt_dlp/version.py +++ b/yt-dlp/yt_dlp/version.py @@ -1,8 +1,8 @@ # Autogenerated by devscripts/update-version.py -__version__ = '2025.01.26' +__version__ = '2025.02.19' -RELEASE_GIT_HEAD = '3b4531934465580be22937fecbb6e1a3a9e2334f' +RELEASE_GIT_HEAD = '4985a4041770eaa0016271809a1fd950dc809a55' VARIANT = None @@ -12,4 +12,4 @@ CHANNEL = 'stable' ORIGIN = 'yt-dlp/yt-dlp' -_pkg_version = '2025.01.26' +_pkg_version = '2025.02.19'