Update dependencies, finished video service and refactored logger to write to files

This commit is contained in:
Kevin Rodrigues Borges
2026-01-03 18:35:12 +00:00
parent ade64a9481
commit 0ba90d8f39
32 changed files with 526 additions and 1359 deletions

2
.fvmrc
View File

@@ -1,3 +1,3 @@
{
"flutter": "3.32.6"
"flutter": "3.38.1"
}

View File

@@ -1,16 +0,0 @@
# Run with `flutter pub run ffigen --config ffigen.yaml`.
name: TorrentLibrary
description: Bindings to `lib/ffi/libmtorrentserver.h`.
output: 'lib/ffi/generated_bindings.dart'
headers:
entry-points:
- 'ffigen/libmtorrentserver.h'
preamble: |
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: unused_field
// ignore_for_file: unused_element
comments:
style: any
length: full

View File

@@ -1,87 +0,0 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif
#endif
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
/* Return type for Start */
struct Start_return {
GoInt r0;
char* r1;
};
extern struct Start_return Start(char* mcfg);
#ifdef __cplusplus
}
#endif

View File

@@ -1,20 +0,0 @@
package main
import (
"C"
"encoding/json"
"server"
)
//export Start
func Start(mcfg *C.char) (int, *C.char) {
var config server.Config
json.Unmarshal([]byte(C.GoString(mcfg)), &config)
port, err := server.Start(&config)
if err != nil {
return 0, C.CString(err.Error())
}
return port, nil
}
func main() {}

View File

@@ -1,94 +0,0 @@
module server
go 1.22
require (
github.com/anacrolix/torrent v1.56.1
github.com/rs/cors v1.11.1
)
require (
github.com/RoaringBitmap/roaring v1.9.4 // indirect
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/anacrolix/chansync v0.5.1 // indirect
github.com/anacrolix/dht/v2 v2.21.1 // indirect
github.com/anacrolix/envpprof v1.3.0 // indirect
github.com/anacrolix/generics v0.0.2 // indirect
github.com/anacrolix/go-libutp v1.3.1 // indirect
github.com/anacrolix/log v0.15.2 // indirect
github.com/anacrolix/missinggo v1.3.0 // indirect
github.com/anacrolix/missinggo/perf v1.0.0 // indirect
github.com/anacrolix/missinggo/v2 v2.7.3 // indirect
github.com/anacrolix/mmsg v1.0.0 // indirect
github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7 // indirect
github.com/anacrolix/stm v0.5.0 // indirect
github.com/anacrolix/sync v0.5.1 // indirect
github.com/anacrolix/upnp v0.1.4 // indirect
github.com/anacrolix/utp v0.2.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/benbjohnson/immutable v0.4.3 // indirect
github.com/bits-and-blooms/bitset v1.14.2 // indirect
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/go-llsqlite/adapter v0.1.0 // indirect
github.com/go-llsqlite/crawshaw v0.5.5 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/multiformats/go-multihash v0.2.3 // indirect
github.com/multiformats/go-varint v0.0.7 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/pion/datachannel v1.5.9 // indirect
github.com/pion/dtls/v2 v2.2.12 // indirect
github.com/pion/ice/v2 v2.3.34 // indirect
github.com/pion/interceptor v0.1.30 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/mdns v0.0.12 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.14 // indirect
github.com/pion/rtp v1.8.9 // indirect
github.com/pion/sctp v1.8.33 // indirect
github.com/pion/sdp/v3 v3.0.9 // indirect
github.com/pion/srtp/v2 v2.0.20 // indirect
github.com/pion/stun v0.6.1 // indirect
github.com/pion/transport/v2 v2.2.10 // indirect
github.com/pion/turn/v2 v2.1.6 // indirect
github.com/pion/webrtc/v3 v3.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/tidwall/btree v1.7.0 // indirect
github.com/wlynxg/anet v0.0.4 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/time v0.6.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
modernc.org/libc v1.60.1 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.32.0 // indirect
zombiezen.com/go/sqlite v1.3.0 // indirect
)

549
go/go.sum
View File

@@ -1,549 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk=
crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI=
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ=
github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 h1:byYvvbfSo3+9efR4IeReh77gVs4PnNDR3AMOE9NJ7a0=
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0/go.mod h1:q37NoqncT41qKc048STsifIt69LfUJ8SrWWcz/yam5k=
github.com/alecthomas/assert/v2 v2.0.0-alpha3 h1:pcHeMvQ3OMstAWgaeaXIAL8uzB9xMm2zlxt+/4ml8lk=
github.com/alecthomas/assert/v2 v2.0.0-alpha3/go.mod h1:+zD0lmDXTeQj7TgDgCt0ePWxb0hMC1G+PGTsTCv1B9o=
github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8=
github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI=
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48=
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anacrolix/chansync v0.5.1 h1:j+R9DtotkXm40VFjZ8rJTSJkg2Gv1ldZt8kl96lyJJ0=
github.com/anacrolix/chansync v0.5.1/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k=
github.com/anacrolix/dht/v2 v2.21.1 h1:s1rKkfLLcmBHKv4v/mtMkIeHIEptzEFiB6xVu54+5/o=
github.com/anacrolix/dht/v2 v2.21.1/go.mod h1:SDGC+sEs1pnO2sJGYuhvIis7T8749dDHNfcjtdH4e3g=
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
github.com/anacrolix/envpprof v1.3.0 h1:WJt9bpuT7A/CDCxPOv/eeZqHWlle/Y0keJUvc6tcJDk=
github.com/anacrolix/envpprof v1.3.0/go.mod h1:7QIG4CaX1uexQ3tqd5+BRa/9e2D02Wcertl6Yh0jCB0=
github.com/anacrolix/generics v0.0.0-20230113004304-d6428d516633/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8=
github.com/anacrolix/generics v0.0.2 h1:UbtD+KntUGxeGYMC4RwhsETieL9ixGdSptJQRhdy7No=
github.com/anacrolix/generics v0.0.2/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8=
github.com/anacrolix/go-libutp v1.3.1 h1:idJzreNLl+hNjGC3ZnUOjujEaryeOGgkwHLqSGoige0=
github.com/anacrolix/go-libutp v1.3.1/go.mod h1:heF41EC8kN0qCLMokLBVkB8NXiLwx3t8R8810MTNI5o=
github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68=
github.com/anacrolix/log v0.14.2/go.mod h1:1OmJESOtxQGNMlUO5rcv96Vpp9mfMqXXbe2RdinFLdY=
github.com/anacrolix/log v0.15.2 h1:LTSf5Wm6Q4GNWPFMBP7NPYV6UBVZzZLKckL+/Lj72Oo=
github.com/anacrolix/log v0.15.2/go.mod h1:m0poRtlr41mriZlXBQ9SOVZ8yZBkLjOkDhd5Li5pITA=
github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62 h1:P04VG6Td13FHMgS5ZBcJX23NPC/fiC4cp9bXwYujdYM=
github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM=
github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y=
github.com/anacrolix/missinggo v1.3.0 h1:06HlMsudotL7BAELRZs0yDZ4yVXsHXGi323QBjAVASw=
github.com/anacrolix/missinggo v1.3.0/go.mod h1:bqHm8cE8xr+15uVfMG3BFui/TxyB6//H5fwlq/TeqMc=
github.com/anacrolix/missinggo/perf v1.0.0 h1:7ZOGYziGEBytW49+KmYGTaNfnwUqP1HBsy6BqESAJVw=
github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ=
github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY=
github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA=
github.com/anacrolix/missinggo/v2 v2.7.3 h1:Ee//CmZBMadeNiYB/hHo9ly2PFOEZ4Fhsbnug3rDAIE=
github.com/anacrolix/missinggo/v2 v2.7.3/go.mod h1:mIEtp9pgaXqt8VQ3NQxFOod/eQ1H0D1XsZzKUQfwtac=
github.com/anacrolix/mmsg v0.0.0-20180515031531-a4a3ba1fc8bb/go.mod h1:x2/ErsYUmT77kezS63+wzZp8E3byYB0gzirM/WMBLfw=
github.com/anacrolix/mmsg v1.0.0 h1:btC7YLjOn29aTUAExJiVUhQOuf/8rhm+/nWCMAnL3Hg=
github.com/anacrolix/mmsg v1.0.0/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc=
github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7 h1:lOtCD+LzoD1g7bowhYJNR++uV+FyY5bTZXKwnPex9S8=
github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7/go.mod h1:zJv1JF9AqdZiHwxqPgjuOZDGWER6nyE48WBCi/OOrMM=
github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg=
github.com/anacrolix/stm v0.5.0 h1:9df1KBpttF0TzLgDq51Z+TEabZKMythqgx89f1FQJt8=
github.com/anacrolix/stm v0.5.0/go.mod h1:MOwrSy+jCm8Y7HYfMAwPj7qWVu7XoVvjOiYwJmpeB/M=
github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk=
github.com/anacrolix/sync v0.3.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
github.com/anacrolix/sync v0.5.1 h1:FbGju6GqSjzVoTgcXTUKkF041lnZkG5P0C3T5RL3SGc=
github.com/anacrolix/sync v0.5.1/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8=
github.com/anacrolix/torrent v1.56.1 h1:QeJMOP0NuhpQ5dATsOqEL0vUO85aPMNMGP2FACNt0Eg=
github.com/anacrolix/torrent v1.56.1/go.mod h1:5DMHbeIM1TuC5wTQ99XieKKLiYZYz6iB2lyZpKZEr6w=
github.com/anacrolix/upnp v0.1.4 h1:+2t2KA6QOhm/49zeNyeVwDu1ZYS9dB9wfxyVvh/wk7U=
github.com/anacrolix/upnp v0.1.4/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984+9fIic=
github.com/anacrolix/utp v0.2.0 h1:65Cdmr6q9WSw2KsM+rtJFu7rqDzLl2bdysf4KlNPcFI=
github.com/anacrolix/utp v0.2.0/go.mod h1:HGk4GYQw1O/3T1+yhqT/F6EcBd+AAwlo9dYErNy7mj8=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
github.com/benbjohnson/immutable v0.4.3 h1:GYHcksoJ9K6HyAUpGxwZURrbTkXA0Dh4otXGqbhdrjA=
github.com/benbjohnson/immutable v0.4.3/go.mod h1:qJIKKSmdqz1tVzNtst1DZzvaqOU1onk1rc03IeM3Owk=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.14.2 h1:YXVoyPndbdvcEVcseEovVfp0qjJp7S+i5+xgp/Nfbdc=
github.com/bits-and-blooms/bitset v1.14.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-llsqlite/adapter v0.1.0 h1:wGSQNsu/rtYeu/lqZNZQMjwUdEF3OW66xTLvsFwJQUw=
github.com/go-llsqlite/adapter v0.1.0/go.mod h1:DADrR88ONKPPeSGjFp5iEN55Arx3fi2qXZeKCYDpbmU=
github.com/go-llsqlite/crawshaw v0.5.5 h1:sXnRkiV26MBv++lbPbzp+ZzFcTqzVMxftO8yHyFvwUA=
github.com/go-llsqlite/crawshaw v0.5.5/go.mod h1:/YJdV7uBQaYDE0fwe4z3wwJIZBJxdYzd38ICggWqtaE=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gortc/stun v1.20.0 h1:7FNyWwjRXVByBItBwpnsv6SZ08UzPfmByLjKmOnpb14=
github.com/gortc/stun v1.20.0/go.mod h1:/XeODKxk0b1P5pYtdD/ZlOncR7+6XgU4zb9CzJIFJBs=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA=
github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=
github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
github.com/pion/ice/v2 v2.3.34 h1:Ic1ppYCj4tUOcPAp76U6F3fVrlSw8A9JtRXLqw6BbUM=
github.com/pion/ice/v2 v2.3.34/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ=
github.com/pion/interceptor v0.1.30 h1:au5rlVHsgmxNi+v/mjOPazbW1SHzfx7/hYOEYQnUcxA=
github.com/pion/interceptor v0.1.30/go.mod h1:RQuKT5HTdkP2Fi0cuOS5G5WNymTjzXaGF75J4k7z2nc=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=
github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw=
github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM=
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
github.com/pion/srtp/v2 v2.0.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk=
github.com/pion/srtp/v2 v2.0.20/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
github.com/pion/stun v1.17.3 h1:JioTPckDp7PdfoF1FYSz1/sOWrXimbjyfZzvg2QIinU=
github.com/pion/stun v1.17.3/go.mod h1:4iy9kiYvpncdXoYYJoAvZ4YFybb4/gQmZxUNaU2680Y=
github.com/pion/transport v0.13.1 h1:/UH5yLeQtwm2VZIPjxwnNFxjS4DFhyLfS4GlfuKUzfA=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q=
github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/webrtc/v3 v3.3.0 h1:Rf4u6n6U5t5sUxhYPQk/samzU/oDv7jk6BA5hyO2F9I=
github.com/pion/webrtc/v3 v3.3.0/go.mod h1:hVmrDJvwhEertRWObeb1xzulzHGeVUoPlWvxdGzcfU0=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 h1:18kd+8ZUlt/ARXhljq+14TwAoKa61q6dX8jtwOf6DH8=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/wlynxg/anet v0.0.4 h1:0de1OFQxnNqAu+x2FAKKCVIrnfGKQbs7FQz++tB0+Uw=
github.com/wlynxg/anet v0.0.4/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4=
modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/libc v1.60.1 h1:at373l8IFRTkJIkAU85BIuUoBM4T1b51ds0E1ovPG2s=
modernc.org/libc v1.60.1/go.mod h1:xJuobKuNxKH3RUatS7GjR+suWj+5c2K7bi4m/S5arOY=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s=
modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
zombiezen.com/go/sqlite v1.3.0 h1:98g1gnCm+CNz6AuQHu0gqyw7gR2WU3O3PJufDOStpUs=
zombiezen.com/go/sqlite v1.3.0/go.mod h1:yRl27//s/9aXU3RWs8uFQwjkTG9gYNGEls6+6SvrclY=

View File

@@ -1,531 +0,0 @@
package server
//credits: https://github.com/glblduh/StreamRest
import (
"context"
"encoding/json"
"log"
"net"
"net/http"
"net/url"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
"github.com/rs/cors"
)
var torrentCli *torrent.Client
var torrentcliCfg *torrent.ClientConfig
func Start(config *Config) (int, error) {
torrentcliCfg = torrent.NewDefaultClientConfig()
torrentcliCfg.DataDir = filepath.Clean(config.Path)
log.Printf("[INFO] Download directory is set to: %s\n", torrentcliCfg.DataDir)
var torrentCliErr error
torrentCli, torrentCliErr = torrent.NewClient(torrentcliCfg)
if torrentCliErr != nil {
log.Fatalf("[ERROR] Creation of BitTorrent client failed: %s\n", torrentCliErr)
}
dnsResolve()
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigs
log.Println("[INFO] Termination detected. Removing torrents")
for _, t := range torrentCli.Torrents() {
log.Printf("[INFO] Removing torrent: [%s]\n", t.Name())
t.Drop()
rmaErr := os.RemoveAll(filepath.Join(torrentcliCfg.DataDir, t.Name()))
if rmaErr != nil {
log.Printf("[ERROR] Failed to remove files of torrent: [%s]: %s\n", t.Name(), rmaErr)
}
}
os.Exit(0)
}()
mux := http.NewServeMux()
mux.HandleFunc("/torrent/addmagnet", addMagnet)
mux.HandleFunc("/torrent/stream", streamTorrent)
mux.HandleFunc("/torrent/remove", removeTorrent)
mux.HandleFunc("/torrent/torrents", listTorrents)
mux.HandleFunc("/torrent/play", playTorrent)
mux.HandleFunc("/torrent/add", AddTorrent)
mux.HandleFunc("/", Init)
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "DELETE"},
AllowCredentials: true,
})
listener, err := net.Listen("tcp", config.Address)
if err != nil {
return 0, err
}
addr := listener.Addr().(*net.TCPAddr)
log.Printf("[INFO] Listening on %s\n", addr.AddrPort())
go func() {
if err := http.Serve(listener, c.Handler(mux)); err != nil && err != http.ErrServerClosed {
panic(err)
}
}()
return addr.Port, nil
}
func safenDisplayPath(displayPath string) string {
fileNameArray := strings.Split(displayPath, "/")
return strings.Join(fileNameArray, " ")
}
func appendFilePlaylist(scheme string, host string, infohash string, name string) string {
playList := "#EXTINF:-1," + safenDisplayPath(name) + "\n"
playList += scheme + "://" + host + "/torrent/stream?infohash=" + infohash + "&file=" + url.QueryEscape(name) + "\n"
return playList
}
func nameCheck(str string, substr string) bool {
splittedSubStr := strings.Split(substr, " ")
for _, curWord := range splittedSubStr {
if !strings.Contains(str, curWord) {
return false
}
}
return true
}
func getTorrentFile(files []*torrent.File, filename string, exactName bool) *torrent.File {
var tFile *torrent.File = nil
for _, file := range files {
if exactName && file.DisplayPath() == filename {
tFile = file
}
if !exactName && filename != "" && nameCheck(strings.ToLower(file.DisplayPath()), strings.ToLower(filename)) {
tFile = file
}
if tFile != nil {
break
}
}
return tFile
}
// https://github.com/YouROK/TorrServer/blob/681fc5c343f6d2782dee0c015d2ba2dfd210f88f/server/cmd/main.go#L114
func dnsResolve() {
addrs, err := net.LookupHost("www.google.com")
if len(addrs) == 0 {
log.Printf("Check dns failed", addrs, err)
fn := func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{}
return d.DialContext(ctx, "udp", "1.1.1.1:53")
}
net.DefaultResolver = &net.Resolver{
Dial: fn,
}
addrs, err = net.LookupHost("www.google.com")
log.Printf("Check cloudflare dns", addrs, err)
} else {
log.Printf("Check dns OK", addrs, err)
}
}
func makePlayStreamURL(infohash string, filename string, isStream bool) string {
endPoint := "play"
if isStream {
endPoint = "stream"
}
URL := "/torrent/" + endPoint + "?infohash=" + infohash
if filename != "" {
URL += "&file=" + url.QueryEscape(filename)
}
return URL
}
func httpJSONError(w http.ResponseWriter, error string, code int) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
if json.NewEncoder(w).Encode(errorRes{
Error: error,
}) != nil {
http.Error(w, error, code)
}
}
func parseRequestBody(w http.ResponseWriter, r *http.Request, v any) error {
err := json.NewDecoder(r.Body).Decode(v)
if err != nil {
httpJSONError(w, "Request JSON body decode error", http.StatusInternalServerError)
}
return err
}
func makeJSONResponse(w http.ResponseWriter, v any) {
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(v)
if err != nil {
httpJSONError(w, "Response JSON body encode error", http.StatusInternalServerError)
}
}
func getInfo(t *torrent.Torrent) {
if t != nil {
<-t.GotInfo()
}
}
func initMagnet(w http.ResponseWriter, magnet string, alldn []string, alltr []string) *torrent.Torrent {
var t *torrent.Torrent = nil
var err error
magnetString := magnet
for _, dn := range alldn {
magnetString += "&dn=" + url.QueryEscape(dn)
}
for _, tr := range alltr {
magnetString += "&tr=" + url.QueryEscape(tr)
}
t, err = torrentCli.AddMagnet(magnetString)
if err != nil {
httpJSONError(w, "Torrent add error", http.StatusInternalServerError)
}
getInfo(t)
return t
}
func getTorrent(w http.ResponseWriter, infoHash string) *torrent.Torrent {
var t *torrent.Torrent = nil
var tOk bool
if len(infoHash) != 40 {
httpJSONError(w, "InfoHash not valid", http.StatusInternalServerError)
return t
}
t, tOk = torrentCli.Torrent(metainfo.NewHashFromHex(infoHash))
if !tOk {
httpJSONError(w, "Torrent not found", http.StatusNotFound)
}
getInfo(t)
return t
}
func parseTorrentStats(t *torrent.Torrent) torrentStatsRes {
var tsRes torrentStatsRes
tsRes.InfoHash = t.InfoHash().String()
tsRes.Name = t.Name()
tsRes.TotalPeers = t.Stats().TotalPeers
tsRes.ActivePeers = t.Stats().ActivePeers
tsRes.HalfOpenPeers = t.Stats().HalfOpenPeers
tsRes.PendingPeers = t.Stats().PendingPeers
if t.Info() == nil {
return tsRes
}
for _, tFile := range t.Files() {
tsRes.Files.OnTorrent = append(tsRes.Files.OnTorrent, torrentStatsFilesOnTorrent{
FileName: tFile.DisplayPath(),
FileSizeBytes: int(tFile.Length()),
})
if tFile.BytesCompleted() != 0 {
tsRes.Files.OnDisk = append(tsRes.Files.OnDisk, torrentStatsFilesOnDisk{
FileName: tFile.DisplayPath(),
StreamURL: makePlayStreamURL(t.InfoHash().String(), tFile.DisplayPath(), true),
BytesDownloaded: int(tFile.BytesCompleted()),
FileSizeBytes: int(tFile.Length()),
})
}
}
return tsRes
}
func addMagnet(w http.ResponseWriter, r *http.Request) {
var amBody addMagnetBody
var amRes addMagnetRes
if parseRequestBody(w, r, &amBody) != nil {
return
}
if amBody.Magnet == "" {
httpJSONError(w, "Magnet link is not provided", http.StatusNotFound)
return
}
t := initMagnet(w, amBody.Magnet, []string{}, []string{})
if t == nil {
return
}
amRes.InfoHash = t.InfoHash().String()
amRes.Name = t.Name()
if amBody.AllFiles {
amRes.PlaylistURL = makePlayStreamURL(t.InfoHash().String(), "", false)
for _, tFile := range t.Files() {
amRes.Files = append(amRes.Files, addMagnetFiles{
FileName: tFile.DisplayPath(),
StreamURL: makePlayStreamURL(t.InfoHash().String(), tFile.DisplayPath(), true),
FileSizeBytes: int(tFile.Length()),
})
}
makeJSONResponse(w, &amRes)
return
}
if len(amBody.Files) > 0 {
amRes.PlaylistURL = makePlayStreamURL(t.InfoHash().String(), "", false)
for _, selFile := range amBody.Files {
tFile := getTorrentFile(t.Files(), selFile, false)
if tFile == nil {
continue
}
amRes.PlaylistURL += "&file=" + url.QueryEscape(tFile.DisplayPath())
amRes.Files = append(amRes.Files, addMagnetFiles{
FileName: tFile.DisplayPath(),
StreamURL: makePlayStreamURL(t.InfoHash().String(), tFile.DisplayPath(), true),
FileSizeBytes: int(tFile.Length()),
})
}
makeJSONResponse(w, &amRes)
return
}
for _, tFile := range t.Files() {
amRes.Files = append(amRes.Files, addMagnetFiles{
FileName: tFile.DisplayPath(),
FileSizeBytes: int(tFile.Length()),
})
}
makeJSONResponse(w, &amRes)
}
func streamTorrent(w http.ResponseWriter, r *http.Request) {
infoHash, ihOk := r.URL.Query()["infohash"]
fileName, fnOk := r.URL.Query()["file"]
if !ihOk || !fnOk {
httpJSONError(w, "InfoHash or File is not provided", http.StatusNotFound)
return
}
t := getTorrent(w, infoHash[0])
if t == nil {
return
}
tFile := getTorrentFile(t.Files(), fileName[0], true)
if tFile == nil {
httpJSONError(w, "File not found", http.StatusNotFound)
return
}
fileRead := tFile.NewReader()
defer fileRead.Close()
fileRead.SetReadahead(tFile.Length() / 100)
http.ServeContent(w, r, tFile.DisplayPath(), time.Now(), fileRead)
}
func removeTorrent(w http.ResponseWriter, r *http.Request) {
infoHash, ihOk := r.URL.Query()["infohash"]
if !ihOk {
httpJSONError(w, "InfoHash is not provided", http.StatusNotFound)
return
}
t := getTorrent(w, infoHash[0])
if t == nil {
httpJSONError(w, "Torrent not found", http.StatusNotFound)
return
}
t.Drop()
if os.RemoveAll(filepath.Join(torrentcliCfg.DataDir, t.Name())) != nil {
httpJSONError(w, "ERROR WHEN REMOVING FILE", http.StatusInternalServerError)
return
}
}
func listTorrents(w http.ResponseWriter, r *http.Request) {
infoHash, ihOk := r.URL.Query()["infohash"]
var ltRes listTorrentsRes
allTorrents := torrentCli.Torrents()
if ihOk {
allTorrents = nil
t := getTorrent(w, infoHash[0])
if t == nil {
return
}
allTorrents = append(allTorrents, t)
}
for _, t := range allTorrents {
ltRes.Torrents = append(ltRes.Torrents, parseTorrentStats(t))
}
if !ihOk && len(ltRes.Torrents) < 1 {
w.WriteHeader(404)
}
makeJSONResponse(w, &ltRes)
}
func AddTorrent(w http.ResponseWriter, request *http.Request) {
a, _, error := request.FormFile("file")
if error != nil {
log.Printf("error upload torrent")
w.WriteHeader(http.StatusForbidden)
return
}
metainf, er_ := metainfo.Load(a)
if er_ != nil {
log.Printf("error error when loading MetaInfo")
w.WriteHeader(http.StatusForbidden)
return
}
torrent, err := torrentCli.AddTorrent(metainf)
if err != nil {
log.Print(err.Error())
w.WriteHeader(http.StatusForbidden)
return
}
w.Write([]byte(torrent.InfoHash().HexString()))
}
func Init(w http.ResponseWriter, request *http.Request) {
w.Write([]byte("Torrent server is running"))
}
func playTorrent(w http.ResponseWriter, r *http.Request) {
infoHash, ihOk := r.URL.Query()["infohash"]
magnet, magOk := r.URL.Query()["magnet"]
displayName := r.URL.Query()["dn"]
trackers := r.URL.Query()["tr"]
files, fOk := r.URL.Query()["file"]
if !magOk && !ihOk {
httpJSONError(w, "Magnet link or InfoHash is not provided", http.StatusNotFound)
return
}
var t *torrent.Torrent
if magOk && !ihOk {
t = initMagnet(w, magnet[0], displayName, trackers)
}
if ihOk && !magOk {
t = getTorrent(w, infoHash[0])
}
if t == nil {
return
}
w.Header().Set("Content-Disposition", "attachment; filename=\""+t.InfoHash().String()+".m3u\"")
playList := "#EXTM3U\n"
httpScheme := "http"
if r.Header.Get("X-Forwarded-Proto") != "" {
httpScheme = r.Header.Get("X-Forwarded-Proto")
}
if !fOk {
for _, tFile := range t.Files() {
playList += appendFilePlaylist(httpScheme, r.Host, t.InfoHash().String(), tFile.DisplayPath())
}
}
for _, file := range files {
tFile := getTorrentFile(t.Files(), file, false)
if tFile == nil {
continue
}
playList += appendFilePlaylist(httpScheme, r.Host, t.InfoHash().String(), tFile.DisplayPath())
}
w.Write([]byte(playList))
}
type errorRes struct {
Error string
}
type addMagnetBody struct {
Magnet string
AllFiles bool
Files []string
}
type addMagnetRes struct {
InfoHash string
Name string
PlaylistURL string
Files []addMagnetFiles
}
type addMagnetFiles struct {
FileName string
StreamURL string
FileSizeBytes int
}
type listTorrentsRes struct {
Torrents []torrentStatsRes
}
type torrentStatsRes struct {
InfoHash string
Name string
TotalPeers int
ActivePeers int
PendingPeers int
HalfOpenPeers int
Files torrentStatsFiles
}
type torrentStatsFiles struct {
OnTorrent []torrentStatsFilesOnTorrent
OnDisk []torrentStatsFilesOnDisk
}
type torrentStatsFilesOnTorrent struct {
FileName string
FileSizeBytes int
}
type torrentStatsFilesOnDisk struct {
FileName string
StreamURL string
BytesDownloaded int
FileSizeBytes int
}
type Config struct {
Address string `json:"address"`
Path string `json:"path"`
}

View File

@@ -30,6 +30,7 @@ import 'package:unyo/domain/entities/anime.dart';
import 'package:unyo/domain/entities/anime_details.dart';
import 'package:unyo/domain/entities/episode_info.dart';
import 'package:unyo/domain/entities/extension.dart';
import 'package:unyo/domain/entities/extension/video.dart' as ext;
import 'package:unyo/domain/entities/media_list.dart';
import 'package:unyo/domain/entities/media_list_entry.dart';
import 'package:unyo/domain/entities/settings.dart';
@@ -521,7 +522,7 @@ class AnimeDetailsCubit extends Cubit<AnimeDetailsState> with EffectMixin<AnimeD
selectedExtension,
);
if (videoResults.isNotEmpty) {
emit(state.copyWith(extensionVideoResults: videoResults));
emit(state.copyWith(extensionVideoResults: videoResults.map((jVideo) => ext.Video.fromJVideo(jVideo)).toList()));
return true;
} else {
showSnackBarEffect(

View File

@@ -6,6 +6,7 @@ import 'package:logger/logger.dart';
import 'package:unyo/application/cubits/effect_mixin.dart';
import 'package:unyo/application/effects/app_effects.dart';
import 'package:unyo/application/states/tabs_state.dart';
import 'package:unyo/core/di/locator.dart';
import 'package:unyo/core/enums/selected_menu_option.dart';
import 'package:unyo/core/notification/menu_bar_notifier.dart';
import 'package:unyo/core/notification/user_notifier.dart';
@@ -15,7 +16,7 @@ class TabsCubit extends Cubit<TabsState>
with EffectMixin<TabsState> {
final UserNotifier _loggedUserNotifier;
final MenuBarNotifier _menuBarNotifier;
final Logger _logger = Logger();
final Logger _logger = sl<Logger>();
late StreamSubscription<User> _newLoggedUserSubscription;
late StreamSubscription<bool> _showMenuBarSubscription;

View File

@@ -1,12 +1,12 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:k3vinb5_aniyomi_bridge/jmodels/jsanime.dart';
import 'package:k3vinb5_aniyomi_bridge/jmodels/jsepisode.dart';
import 'package:k3vinb5_aniyomi_bridge/jmodels/jvideo.dart';
import 'package:unyo/application/cubits/effect_mixin.dart';
import 'package:unyo/application/effects/app_effects.dart';
import 'package:unyo/domain/entities/anime.dart';
import 'package:unyo/domain/entities/episode_info.dart';
import 'package:unyo/domain/entities/extension.dart';
import 'package:unyo/domain/entities/extension/video.dart' as ext;
import 'package:unyo/domain/entities/media_character.dart';
import 'package:unyo/domain/entities/media_list.dart';
import 'package:unyo/domain/entities/media_list_entry.dart';
@@ -34,7 +34,7 @@ abstract class AnimeDetailsState with _$AnimeDetailsState implements HasEffects{
// voice actors
required List<JSAnime> extensionAnimeResults,
required List<JSEpisode> extensionEpisodeResults,
required List<JVideo> extensionVideoResults,
required List<ext.Video> extensionVideoResults,
@Default(<AppEffect>[]) List<AppEffect> effects,
}) = _AnimeDetailsState;

View File

@@ -16,7 +16,7 @@ mixin _$AnimeDetailsState {
User get loggedUser; MediaList get selectedMediaList; Anime get selectedAnime; MediaListEntry get mediaListEntry; (bool, List<MediaCharacter>) get characters; (bool, List<Anime>) get recommendations; List<EpisodeInfo> get episodesInfo; List<String> get banners; String get alternateImage; Set<Extension> get installedExtensions; bool get userLoaded; bool get animeServerDialogReady; Extension? get selectedExtension;// relations
// voice actors
List<JSAnime> get extensionAnimeResults; List<JSEpisode> get extensionEpisodeResults; List<JVideo> get extensionVideoResults; List<AppEffect> get effects;
List<JSAnime> get extensionAnimeResults; List<JSEpisode> get extensionEpisodeResults; List<ext.Video> get extensionVideoResults; List<AppEffect> get effects;
/// Create a copy of AnimeDetailsState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -47,7 +47,7 @@ abstract mixin class $AnimeDetailsStateCopyWith<$Res> {
factory $AnimeDetailsStateCopyWith(AnimeDetailsState value, $Res Function(AnimeDetailsState) _then) = _$AnimeDetailsStateCopyWithImpl;
@useResult
$Res call({
User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<JVideo> extensionVideoResults, List<AppEffect> effects
User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<ext.Video> extensionVideoResults, List<AppEffect> effects
});
@@ -82,7 +82,7 @@ as bool,selectedExtension: freezed == selectedExtension ? _self.selectedExtensio
as Extension?,extensionAnimeResults: null == extensionAnimeResults ? _self.extensionAnimeResults : extensionAnimeResults // ignore: cast_nullable_to_non_nullable
as List<JSAnime>,extensionEpisodeResults: null == extensionEpisodeResults ? _self.extensionEpisodeResults : extensionEpisodeResults // ignore: cast_nullable_to_non_nullable
as List<JSEpisode>,extensionVideoResults: null == extensionVideoResults ? _self.extensionVideoResults : extensionVideoResults // ignore: cast_nullable_to_non_nullable
as List<JVideo>,effects: null == effects ? _self.effects : effects // ignore: cast_nullable_to_non_nullable
as List<ext.Video>,effects: null == effects ? _self.effects : effects // ignore: cast_nullable_to_non_nullable
as List<AppEffect>,
));
}
@@ -168,7 +168,7 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<JVideo> extensionVideoResults, List<AppEffect> effects)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<ext.Video> extensionVideoResults, List<AppEffect> effects)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _AnimeDetailsState() when $default != null:
return $default(_that.loggedUser,_that.selectedMediaList,_that.selectedAnime,_that.mediaListEntry,_that.characters,_that.recommendations,_that.episodesInfo,_that.banners,_that.alternateImage,_that.installedExtensions,_that.userLoaded,_that.animeServerDialogReady,_that.selectedExtension,_that.extensionAnimeResults,_that.extensionEpisodeResults,_that.extensionVideoResults,_that.effects);case _:
@@ -189,7 +189,7 @@ return $default(_that.loggedUser,_that.selectedMediaList,_that.selectedAnime,_th
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<JVideo> extensionVideoResults, List<AppEffect> effects) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<ext.Video> extensionVideoResults, List<AppEffect> effects) $default,) {final _that = this;
switch (_that) {
case _AnimeDetailsState():
return $default(_that.loggedUser,_that.selectedMediaList,_that.selectedAnime,_that.mediaListEntry,_that.characters,_that.recommendations,_that.episodesInfo,_that.banners,_that.alternateImage,_that.installedExtensions,_that.userLoaded,_that.animeServerDialogReady,_that.selectedExtension,_that.extensionAnimeResults,_that.extensionEpisodeResults,_that.extensionVideoResults,_that.effects);case _:
@@ -209,7 +209,7 @@ return $default(_that.loggedUser,_that.selectedMediaList,_that.selectedAnime,_th
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<JVideo> extensionVideoResults, List<AppEffect> effects)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<ext.Video> extensionVideoResults, List<AppEffect> effects)? $default,) {final _that = this;
switch (_that) {
case _AnimeDetailsState() when $default != null:
return $default(_that.loggedUser,_that.selectedMediaList,_that.selectedAnime,_that.mediaListEntry,_that.characters,_that.recommendations,_that.episodesInfo,_that.banners,_that.alternateImage,_that.installedExtensions,_that.userLoaded,_that.animeServerDialogReady,_that.selectedExtension,_that.extensionAnimeResults,_that.extensionEpisodeResults,_that.extensionVideoResults,_that.effects);case _:
@@ -224,7 +224,7 @@ return $default(_that.loggedUser,_that.selectedMediaList,_that.selectedAnime,_th
class _AnimeDetailsState extends AnimeDetailsState {
const _AnimeDetailsState({required this.loggedUser, required this.selectedMediaList, required this.selectedAnime, required this.mediaListEntry, required this.characters, required this.recommendations, required final List<EpisodeInfo> episodesInfo, required final List<String> banners, required this.alternateImage, required final Set<Extension> installedExtensions, required this.userLoaded, required this.animeServerDialogReady, required this.selectedExtension, required final List<JSAnime> extensionAnimeResults, required final List<JSEpisode> extensionEpisodeResults, required final List<JVideo> extensionVideoResults, final List<AppEffect> effects = const <AppEffect>[]}): _episodesInfo = episodesInfo,_banners = banners,_installedExtensions = installedExtensions,_extensionAnimeResults = extensionAnimeResults,_extensionEpisodeResults = extensionEpisodeResults,_extensionVideoResults = extensionVideoResults,_effects = effects,super._();
const _AnimeDetailsState({required this.loggedUser, required this.selectedMediaList, required this.selectedAnime, required this.mediaListEntry, required this.characters, required this.recommendations, required final List<EpisodeInfo> episodesInfo, required final List<String> banners, required this.alternateImage, required final Set<Extension> installedExtensions, required this.userLoaded, required this.animeServerDialogReady, required this.selectedExtension, required final List<JSAnime> extensionAnimeResults, required final List<JSEpisode> extensionEpisodeResults, required final List<ext.Video> extensionVideoResults, final List<AppEffect> effects = const <AppEffect>[]}): _episodesInfo = episodesInfo,_banners = banners,_installedExtensions = installedExtensions,_extensionAnimeResults = extensionAnimeResults,_extensionEpisodeResults = extensionEpisodeResults,_extensionVideoResults = extensionVideoResults,_effects = effects,super._();
@override final User loggedUser;
@@ -276,8 +276,8 @@ class _AnimeDetailsState extends AnimeDetailsState {
return EqualUnmodifiableListView(_extensionEpisodeResults);
}
final List<JVideo> _extensionVideoResults;
@override List<JVideo> get extensionVideoResults {
final List<ext.Video> _extensionVideoResults;
@override List<ext.Video> get extensionVideoResults {
if (_extensionVideoResults is EqualUnmodifiableListView) return _extensionVideoResults;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_extensionVideoResults);
@@ -321,7 +321,7 @@ abstract mixin class _$AnimeDetailsStateCopyWith<$Res> implements $AnimeDetailsS
factory _$AnimeDetailsStateCopyWith(_AnimeDetailsState value, $Res Function(_AnimeDetailsState) _then) = __$AnimeDetailsStateCopyWithImpl;
@override @useResult
$Res call({
User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<JVideo> extensionVideoResults, List<AppEffect> effects
User loggedUser, MediaList selectedMediaList, Anime selectedAnime, MediaListEntry mediaListEntry, (bool, List<MediaCharacter>) characters, (bool, List<Anime>) recommendations, List<EpisodeInfo> episodesInfo, List<String> banners, String alternateImage, Set<Extension> installedExtensions, bool userLoaded, bool animeServerDialogReady, Extension? selectedExtension, List<JSAnime> extensionAnimeResults, List<JSEpisode> extensionEpisodeResults, List<ext.Video> extensionVideoResults, List<AppEffect> effects
});
@@ -356,7 +356,7 @@ as bool,selectedExtension: freezed == selectedExtension ? _self.selectedExtensio
as Extension?,extensionAnimeResults: null == extensionAnimeResults ? _self._extensionAnimeResults : extensionAnimeResults // ignore: cast_nullable_to_non_nullable
as List<JSAnime>,extensionEpisodeResults: null == extensionEpisodeResults ? _self._extensionEpisodeResults : extensionEpisodeResults // ignore: cast_nullable_to_non_nullable
as List<JSEpisode>,extensionVideoResults: null == extensionVideoResults ? _self._extensionVideoResults : extensionVideoResults // ignore: cast_nullable_to_non_nullable
as List<JVideo>,effects: null == effects ? _self._effects : effects // ignore: cast_nullable_to_non_nullable
as List<ext.Video>,effects: null == effects ? _self._effects : effects // ignore: cast_nullable_to_non_nullable
as List<AppEffect>,
));
}

View File

@@ -42,10 +42,14 @@ import 'package:unyo/application/cubits/home_cubit.dart';
final sl = GetIt.instance;
void setupLocator() {
void setupLocator() async{
// Singletons
sl.registerLazySingleton<Logger>(() => getLogger());
sl.registerSingletonAsync<Directory>(
() => getApplicationSupportDirectory(),
instanceName: config.applicationSupportDirectory,
);
await sl.isReady<Directory>(instanceName: config.applicationSupportDirectory);
sl.registerSingleton<Logger>(getLogger());
// Services
sl.registerLazySingleton<HttpService>(() => HttpService());
sl.registerLazySingleton<GraphQLService>(
@@ -55,10 +59,6 @@ void setupLocator() {
sl.registerLazySingleton<AppEffectHandler>(() => AppEffectHandler());
sl.registerSingleton<ThemeService>(ThemeService());
sl.registerLazySingleton<ColorImageService>(() => ColorImageService());
sl.registerLazySingletonAsync<Directory>(
() => getApplicationSupportDirectory(),
instanceName: config.applicationSupportDirectory,
);
sl.registerSingleton<AniyomiBridge>(AniyomiBridge());
sl.registerSingleton<TorrentService>(TorrentService());
// Notifiers

View File

@@ -1,10 +1,27 @@
import 'dart:io';
import 'package:logger/logger.dart';
import 'package:unyo/config/config.dart' as config;
import 'package:unyo/core/di/locator.dart';
Logger getLogger() {
Directory supportDirectory = sl<Directory>(instanceName: config.applicationSupportDirectory);
File newLogFile = File('${supportDirectory.path}/logs/${DateTime.now().millisecondsSinceEpoch}.log');
newLogFile.create(recursive: true);
List<FileSystemEntity> logFiles = supportDirectory.listSync().where((file) => file.path.endsWith('.log')).toList();
if (logFiles.length > 20) {
logFiles.sort((a, b) => a.statSync().modified.compareTo(b.statSync().modified));
for (int i = 0; i < logFiles.length - 20; i++) {
logFiles[i].deleteSync();
}
}
return Logger(
printer: PrettyPrinter(methodCount: 20),
printer: PrettyPrinter(methodCount: 15),
level: Level.debug,
// output: FileOutput(),
output: MultiOutput([
ConsoleOutput(),
FileOutput(file: newLogFile, overrideExisting: true),
]),
filter: ProductionFilter(),
);
}

View File

@@ -1,11 +1,14 @@
import 'package:flutter/foundation.dart';
class ApiResponse<T> {
final T data;
final Uint8List bodyBytes;
final int statusCode;
final Map<String, String> headers;
ApiResponse({
required this.data,
required this.bodyBytes,
required this.statusCode,
required this.headers,
});

View File

@@ -9,7 +9,6 @@ import 'package:retry/retry.dart';
// Internal dependencies
import 'package:unyo/config/config.dart' as config;
import 'package:unyo/core/di/locator.dart';
import 'package:unyo/core/services/api/http/empty_api_response.dart';
import 'api_response.dart';
import 'http_exception.dart';
@@ -237,6 +236,7 @@ class HttpService {
_logger.d("Response body is empty, returning empty ApiResponse");
return ApiResponse(
data: fromJson({}),
bodyBytes: response.bodyBytes,
statusCode: status,
headers: response.headers,
);
@@ -245,6 +245,7 @@ class HttpService {
final jsonList = json.decode(response.body) as List<dynamic>;
return ApiResponse(
data: fromJson({'list': jsonList}),
bodyBytes: response.bodyBytes,
statusCode: status,
headers: response.headers
);
@@ -252,6 +253,7 @@ class HttpService {
final jsonMap = json.decode(response.body) as Map<String, dynamic>;
return ApiResponse(
data: fromJson(jsonMap),
bodyBytes: response.bodyBytes,
statusCode: status,
headers: response.headers,
);

View File

@@ -5,7 +5,6 @@ import 'package:path/path.dart' as path;
import 'package:unyo/config/config.dart' as config;
import 'package:crypto/crypto.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:system_info3/system_info3.dart';
import 'package:unyo/core/di/locator.dart';
import 'package:unyo/core/services/api/http/api_response.dart';
@@ -21,12 +20,12 @@ class TorrentService {
Process? torrentProcess;
TorrentService() {
initTorrentServer();
_initTorrentServer();
}
Future<void> initTorrentServer() async {
Future<void> _initTorrentServer() async {
await stopServer();
Directory supportDirectory = await getApplicationSupportDirectory();
Directory supportDirectory = sl<Directory>(instanceName: config.applicationSupportDirectory);
String torrServerPath = await _loadTorrServerIfNeeded(supportDirectory);
_startServer(torrServerPath);
}
@@ -108,5 +107,4 @@ class TorrentService {
}
torrentProcess?.kill();
}
}

View File

@@ -0,0 +1,272 @@
// External dependencies
import 'dart:convert';
import 'package:fvp/mdk.dart' as mdk;
import 'package:video_player/video_player.dart';
import 'package:logger/logger.dart';
// Internal dependencies
import 'package:unyo/domain/entities/extension/headers.dart' as ext;
import 'package:unyo/domain/entities/extension/track.dart' as ext;
import 'package:unyo/domain/entities/extension/video.dart' as ext;
import 'package:unyo/core/services/api/http/api_response.dart';
import 'package:unyo/core/services/api/http/empty_api_response.dart';
import 'package:unyo/core/services/api/http/http_service.dart';
import 'package:unyo/core/di/locator.dart';
import 'package:window_manager/window_manager.dart';
class VideoService {
// Services
final Logger _logger = sl<Logger>();
final HttpService _httpService = sl<HttpService>();
// Properties
ext.Video _video;
int _playlistIndex;
late final mdk.Player _player;
final List<ext.Track> captionTracks = [];
final List<ext.Track> audioTracks = [];
ClosedCaptionFile? _currentCaptionFile;
ext.Track? _currentCaptionTrack;
ext.Track? _currentAudioTrack;
bool _isBuffering = true;
VideoService({required ext.Video video, required int playlistIndex}) : _playlistIndex = playlistIndex, _video = video {
_player = mdk.Player();
// TODO handle magnets
// Set player media properties
_player.setMedia(_video.videoUrl, mdk.MediaType.video);
_player.loop = 0; // No loop
_player.state = mdk.PlaybackState.paused;
// Set player ffmpeg properties
_configurePlayer();
// Set player HTTP headers
_setPlayerHttpHeaders(_video.headers);
// Set player logs handler
_setPlayerLogsHandler();
// Waits for video to be ready and initializes embedded tracks
_player.onMediaStatus((mdk.MediaStatus oldStatus, mdk.MediaStatus newStatus) {
if (newStatus.rawValue == mdk.MediaStatus.loaded) {
_initEmbeddedCaptionsAndAudiotracks();
}
if (newStatus.rawValue == mdk.MediaStatus.buffering) {
_isBuffering = true;
}
if (newStatus.rawValue == mdk.MediaStatus.buffered) {
_isBuffering = false;
}
return true;
});
}
// Getters
Duration get position => Duration(milliseconds: _player.position);
Duration get duration => Duration(milliseconds: _player.mediaInfo.duration);
double get percentagePlayed => (position.inMilliseconds / duration.inMilliseconds);
double get volume => _player.volume;
bool get isPlaying => _player.state == mdk.PlaybackState.playing;
bool get isBuffering => _isBuffering;
Future<bool> get isFullscreen async => await windowManager.isFullScreen();
double get aspectRatio {
final streams = _player.mediaInfo.video;
if (streams != null && streams.isNotEmpty) {
final codec = streams.first.codec;
// codec.width and codec.height are ints
return codec.width / codec.height;
}
return 16 / 9;
}
// Setters
void play() {
if (_isBuffering) return;
_player.state = mdk.PlaybackState.playing;
}
void pause() {
if (_isBuffering) return;
_player.state = mdk.PlaybackState.paused;
}
void setLooping(bool loop) {
loop ? _player.loop = -1 : _player.loop = 0;
}
void setVolume(double volume) {
_player.volume = volume;
}
void setPlaybackSpeed(double newPlayBackSpeed) {
_player.playbackRate = newPlayBackSpeed;
}
void seekTo(Duration newDuration) {
_player.seek(position: newDuration.inMilliseconds);
}
void setCaptionOffset(Duration duration) {
throw UnimplementedError("CaptionOffset not yet implemented");
}
Future<void> setCaption(int captionIndex) async {
if (captionIndex < 0 || captionIndex >= captionTracks.length) {
_currentCaptionTrack = null;
return;
}
_currentCaptionTrack = captionTracks[captionIndex];
if (_currentCaptionTrack!.embedded) {
_player.activeSubtitleTracks = [
_currentCaptionTrack?.embeddedIndex ?? 0
];
} else {
_currentCaptionFile = await _loadExternalCaption(_currentCaptionTrack!);
}
}
void setAudioTrack(int audioTrackIndex) {
if (audioTrackIndex < 0 || audioTrackIndex >= audioTracks.length) {
_currentAudioTrack = null;
return;
}
_currentAudioTrack = audioTracks[audioTrackIndex];
if (_currentAudioTrack!.embedded) {
_player.activeAudioTracks = [
_currentAudioTrack?.embeddedIndex ?? 0
];
}
}
void setFullscreen(bool fullscreen) {
windowManager.setFullScreen(fullscreen);
}
void dispose() {
_player.state = mdk.PlaybackState.stopped;
_player.dispose();
}
// Utilities
void _configurePlayer() {
// Note might want to use avformat.user_agent
_player.setProperty('avformat.extension_picky', '0');
_player.setProperty('avformat.http_persistent', '1'); // NOTE: Not sure
_player.setProperty('avformat.reconnect', '1');
_player.setProperty('avformat.reconnect_streamed', '1');
_player.setProperty('avformat.reconnect_delay_max', '5');
_player.setProperty(
'avformat.protocol_whitelist',
'file,http,https,tcp,tls,udp,rtp,rtmp,rtmpe,rtmps,rtmpt,rtmpte,crypto,data',
); // NOTE: Not sure
}
void _setPlayerHttpHeaders(ext.Headers? headers) {
if (headers != null && headers.headersMap.isNotEmpty) {
final formattedHeaders = headers.headersMap.entries.map((e) => '${e.key}: ${e.value}').join('\r\n');
_player.setProperty('headers', formattedHeaders);
_player.setProperty('avio.headers', formattedHeaders);
}
}
void _setPlayerLogsHandler() {
mdk.setLogHandler((mdk.LogLevel level, String message) {
if (!message.contains("unloaded media's position")) {
switch (level) {
case mdk.LogLevel.debug:
_logger.d("MDK Log: $message");
case mdk.LogLevel.info:
_logger.i("MDK Log: $message");
case mdk.LogLevel.warning:
_logger.w("MDK Log: $message");
case mdk.LogLevel.error:
_logger.e("MDK Log: $message");
case mdk.LogLevel.off:
break;
case mdk.LogLevel.all:
break;
}
}
});
}
Future<void> _initEmbeddedCaptionsAndAudiotracks() async {
if (_player.mediaInfo.subtitle != null && _player.mediaInfo.subtitle!.isNotEmpty) {
for (mdk.SubtitleStreamInfo subtitleStreamInfo in _player.mediaInfo.subtitle!) {
captionTracks.add(
ext.Track(
url: "",
lang:
"${subtitleStreamInfo.metadata["title"] ?? ""} (${subtitleStreamInfo.metadata["language"]} - Embedded)",
embedded: true,
),
);
}
}
if (_player.mediaInfo.audio != null && _player.mediaInfo.audio!.length > 1) {
for (mdk.AudioStreamInfo audioStreamInfo in _player.mediaInfo.audio!) {
audioTracks.add(
ext.Track(
url: "",
lang:
"${audioStreamInfo.metadata["title"] ?? ""} (${audioStreamInfo.metadata["language"]} - Embedded)",
embedded: true,
embeddedIndex: audioStreamInfo.index
),
);
}
}
}
Future<ClosedCaptionFile> _loadExternalCaption(ext.Track captionTrack) async {
if (captionTrack.url.isEmpty) {
throw Exception("Caption track URL is empty");
}
ApiResponse<EmptyApiResponse> response = await _httpService.get(
captionTrack.url,
fromJson: EmptyApiResponse.fromJson,
);
if (response.statusCode != 200) {
throw Exception(
"Failed to load captions from ${captionTrack.url} with status code ${response.statusCode}",
);
}
var bytes = response.bodyBytes;
String content = String.fromCharCodes(bytes);
return WebVTTCaptionFile(_formatCaptions(_getUtf8Text(content)));
}
String _getUtf8Text(String text) {
List<int> bytes = text.codeUnits;
return utf8.decode(bytes);
}
String _formatCaptions(String captions) {
// Split the captions into pieces based on empty lines
List<String> pieces = captions.split('\n\n');
List<String> formattedPieces = [];
for (int i = 0; i < pieces.length; i++) {
formattedPieces.add(_replaceSecondNewLine(pieces[i], "\n", " "));
}
// Join the formatted pieces back together with empty lines
String formattedCaptions = formattedPieces.join('\n\n');
return formattedCaptions;
}
String _replaceSecondNewLine(String original, String pattern, String replacement) {
int firstIndex = original.indexOf(pattern);
if (firstIndex != -1) {
int secondIndex = original.indexOf(pattern, firstIndex + 1);
if (secondIndex != -1) {
return original.replaceFirst(pattern, replacement, secondIndex);
}
}
return original;
}
}

View File

@@ -0,0 +1,46 @@
import 'package:k3vinb5_aniyomi_bridge/jmodels/jsanime.dart';
class Anime {
final String url;
final String title;
final String? thumbnailUrl;
final String? artist;
final String? author;
final String? description;
final String? genre;
final List<String>? genres;
final bool initialized;
final double seasonNumber;
final int status;
const Anime({
required this.url,
required this.title,
required this.thumbnailUrl,
required this.artist,
required this.author,
required this.description,
required this.genre,
required this.genres,
required this.initialized,
required this.seasonNumber,
required this.status,
});
factory Anime.fromJSAnime(JSAnime jsAnime) {
jObjIsNotNull(jObj) => jObj != null;
return Anime(
url: jsAnime.getUrl().toDartString(),
title: jsAnime.getTitle().toDartString(),
thumbnailUrl: jsAnime.getThumbnail_url()?.toDartString(),
artist: jsAnime.getArtist()?.toDartString(),
author: jsAnime.getAuthor()?.toDartString(),
description: jsAnime.getDescription()?.toDartString(),
genre: jsAnime.getGenre()?.toDartString(),
genres: jsAnime.getGenres()?.where(jObjIsNotNull).map((jObj) => jObj.toDartString()).toList(),
initialized: jsAnime.getInitialized(),
seasonNumber: jsAnime.getSeason_number(),
status: jsAnime.getStatus(),
);
}
}

View File

@@ -0,0 +1,28 @@
import 'package:k3vinb5_aniyomi_bridge/jmodels/jsepisode.dart';
class Episode {
final String url;
final String title;
final String? scanlator;
final double episodeNumber;
final int dateUploaded;
const Episode({
required this.url,
required this.title,
required this.scanlator,
required this.episodeNumber,
required this.dateUploaded,
});
factory Episode.fromJSEpisode(JSEpisode jsEpisode) {
return Episode(
url: jsEpisode.getUrl().toDartString(),
title: jsEpisode.getName().toDartString(),
scanlator: jsEpisode.getScanlator()?.toDartString(),
episodeNumber: jsEpisode.getEpisode_number(),
dateUploaded: jsEpisode.getDate_upload()
);
}
}

View File

@@ -0,0 +1,16 @@
import 'package:k3vinb5_aniyomi_bridge/jmodels/jheaders.dart';
class Headers {
final Map<String, String> headersMap;
const Headers({required this.headersMap});
factory Headers.fromJHeaders(JHeaders? jHeaders) {
if (jHeaders == null) {
return const Headers(headersMap: {});
}
return Headers(
headersMap: jHeaders.toMultimap().map((key, value) => MapEntry(key.toDartString(), value.join(","))),
);
}
}

View File

View File

@@ -0,0 +1,23 @@
import 'package:k3vinb5_aniyomi_bridge/jmodels/jtrack.dart';
class Track {
final String url;
final String lang;
final bool embedded;
final int? embeddedIndex;
const Track({
required this.url,
required this.lang,
required this.embedded,
this.embeddedIndex,
});
factory Track.fromJTrack(JTrack jTrack) {
return Track(
url: jTrack.getUrl().toDartString(),
lang: jTrack.getLang().toDartString(),
embedded: false
);
}
}

View File

@@ -0,0 +1,57 @@
import 'package:k3vinb5_aniyomi_bridge/jmodels/jheaders.dart';
import 'package:k3vinb5_aniyomi_bridge/jmodels/jtrack.dart';
import 'package:k3vinb5_aniyomi_bridge/jmodels/jvideo.dart';
import 'package:unyo/domain/entities/extension/headers.dart';
import 'package:unyo/domain/entities/extension/track.dart';
class Video {
final String url;
final String title;
final String quality;
final String videoUrl;
final Headers? headers;
final bool initialized;
final List<Track> audioTracks;
final List<Track> subtitleTracks;
final int? bitrate;
final int? resolution;
const Video({
required this.url,
required this.title,
required this.quality,
required this.videoUrl,
required this.headers,
required this.initialized,
required this.audioTracks,
required this.subtitleTracks,
required this.bitrate,
required this.resolution,
});
factory Video.fromJVideo(JVideo jVideo) {
jObjIsNotNull(jObj) => jObj != null;
return Video(
url: jVideo.getUrl().toDartString(),
title: jVideo.getVideoTitle().toDartString(),
quality: jVideo.getQuality().toDartString(),
videoUrl: jVideo.getVideoUrl().toDartString(),
headers: Headers.fromJHeaders(jVideo.getHeaders()?.as<JHeaders>(JHeaders.type)),
initialized: jVideo.getInitialized(),
audioTracks:
jVideo
.getAudioTracks()
.where(jObjIsNotNull)
.map((jObj) => Track.fromJTrack(jObj.as<JTrack>(JTrack.type)))
.toList(),
subtitleTracks:
jVideo
.getSubtitleTracks()
.where(jObjIsNotNull)
.map((jObj) => Track.fromJTrack(jObj.as<JTrack>(JTrack.type)))
.toList(),
bitrate: jVideo.getBitrate()?.intValue(),
resolution: jVideo.getResolution()?.intValue(),
);
}
}

View File

@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:k3vinb5_aniyomi_bridge/jmodels/jvideo.dart';
import 'package:unyo/application/cubits/anime_details_cubit.dart';
import 'package:unyo/application/states/anime_details_state.dart';
import 'package:unyo/domain/entities/extension/video.dart' as ext;
import 'package:unyo/presentation/views/loading_view.dart';
import 'package:unyo/presentation/widgets/styled/unyo_server_button.dart';
@@ -59,7 +59,7 @@ class _AnimeServerSelectionDialogState extends State<AnimeServerSelectionDialog>
padding: EdgeInsets.symmetric(horizontal: 15.0.w),
children: [
...state.extensionVideoResults.map(
(JVideo video) => Column(
(ext.Video video) => Column(
children: [SizedBox(height: 25.0.h), UnyoServerButton(videoServer: video)],
),
),

View File

@@ -4,8 +4,8 @@ import 'dart:ui' as ui;
import 'package:flutter/material.dart';
class UnyoAnimatedImageBanner extends StatefulWidget {
final imageUrl;
const UnyoAnimatedImageBanner({super.key, this.imageUrl});
final String imageUrl;
const UnyoAnimatedImageBanner({super.key, required this.imageUrl});
@override
State<UnyoAnimatedImageBanner> createState() =>

View File

@@ -114,7 +114,6 @@ class _UnyoDropdownState extends State<UnyoDropdown> {
),
),
)
.toList(),
].toList(),
),
);

View File

@@ -40,7 +40,6 @@ class _UnyoMultiSelectDropdownState extends State<UnyoMultiSelectDropdown> {
@override
void didUpdateWidget(covariant UnyoMultiSelectDropdown oldWidget) {
if (oldWidget.selectedValues != _selectedItems ) {
print("Needs rebuild of selected items");
setState(() {
_selectedItems = List.from(widget.selectedValues);
});

View File

@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:k3vinb5_aniyomi_bridge/jmodels/jvideo.dart';
import 'package:unyo/domain/entities/extension/video.dart' as ext;
class UnyoServerButton extends StatelessWidget {
final JVideo videoServer;
final ext.Video videoServer;
const UnyoServerButton({super.key, required this.videoServer});
@@ -32,13 +32,13 @@ class UnyoServerButton extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
videoServer.getQuality().toDartString(),
videoServer.quality,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
Row(
children: [
Icon(
RegExp(r'(720|1080)[pP]?').hasMatch(videoServer.getQuality().toDartString())
RegExp(r'(720|1080)[pP]?').hasMatch(videoServer.quality)
? Icons.hd
: Icons.sd,
color: ColorScheme.of(context).tertiary,

View File

@@ -67,7 +67,7 @@ class _UnyoSortWidgetState extends State<UnyoSortWidget> {
child: CompositedTransformFollower(
link: _layerLink,
showWhenUnlinked: false,
offset: Offset(0, 54),
offset: const Offset(0, 54),
child: Material(
elevation: 4,
borderRadius: BorderRadius.circular(16),

View File

@@ -6,7 +6,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0
environment:
sdk: ^3.7.2
sdk: ^3.10.0
# List your package dependencies below. Run `flutter pub outdated` to check for updates.
dependencies:
@@ -14,11 +14,11 @@ dependencies:
flutter:
sdk: flutter
# State management
bloc: ^9.0.0
bloc: ^9.2.0
flutter_bloc: ^9.1.1
rxdart: ^0.28.0
# Dependency Injection
get_it: ^8.0.3
get_it: ^9.2.0
# Logging
logger: ^2.6.0
# Code generation
@@ -26,47 +26,49 @@ dependencies:
freezed_annotation: ^3.1.0
json_annotation: ^4.9.0
# HTTP requests
http: ^1.4.0
http: ^1.6.0
retry: ^3.1.2
# Routing and Navigation
auto_route: ^10.1.0+1
auto_route_generator: ^10.1.0
auto_route: ^11.1.0
auto_route_generator: ^10.4.0
# UI and Layout
flutter_screenutil: ^5.9.3
loading_animation_widget: ^1.3.0
awesome_snackbar_content: ^0.1.6
flutter_svg: ^2.2.0
fl_chart: ^1.0.0
dynamic_color: ^1.7.0
awesome_snackbar_content: ^0.1.8
flutter_svg: ^2.2.3
fl_chart: ^1.1.1
dynamic_color: ^1.8.1
material_color_utilities: ^0.11.1
palette_generator: ^0.3.3+7
# Platform Integration
window_manager: ^0.5.1
url_launcher: ^6.3.1
url_launcher: ^6.3.2
flutter_discord_rpc: ^1.1.0
# Media
smooth_video_progress: ^0.0.4
fvp: ^0.33.0
fvp: ^0.35.2
video_player: ^2.10.1
# Backend/Server
shelf: ^1.4.2
# Storage
path_provider: ^2.1.5
hive_ce: ^2.11.3
hive_ce: ^2.16.0
path: ^1.9.1
crypto: ^3.0.7
# Asset Management
icons_launcher: ^3.0.1
icons_launcher: ^3.0.3
# Utilities
collection: ^1.19.1
html: ^0.15.6
cast: ^2.1.0
# Localization
easy_localization: ^3.0.7+1
easy_localization: ^3.0.8
# FFI/Native Integration
system_info3: ^4.0.2
ffi: ^2.1.4
ffigen: ^19.0.0
meta: ^1.16.0
jni: ^0.14.2
# ffi: ^2.1.4
# ffigen: ^19.0.0
meta: ^1.17.0
jwt_decoder: ^2.0.1
k3vinb5_aniyomi_bridge:
path: ../k3vinb5_aniyomi_bridge
@@ -77,13 +79,13 @@ dev_dependencies:
sdk: flutter
# Linting and code quality
flutter_lints: ^6.0.0
bloc_lint: ^0.2.0
bloc_lint: ^0.3.5
# Testing
bloc_test: ^10.0.0
# Code generation
hive_ce_generator: ^1.9.2
json_serializable: ^6.9.5
build_runner: ^2.5.4
build_runner: ^2.10.4
# The following section is specific to Flutter packages.
flutter: