From 46f313d7356ff775093dab48a63dc9c21c58bb94 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Fri, 18 Jul 2025 23:19:05 +0200 Subject: [PATCH] Add swagger for transcoder & move routes to router module --- transcoder/.env.example | 2 +- transcoder/go.mod | 24 ++- transcoder/go.sum | 162 +++++++++++++++++++++ transcoder/main.go | 233 +++++------------------------- transcoder/shell.nix | 1 + transcoder/src/api/streams.go | 216 +++++++++++++++++++++++++++ transcoder/{ => src/api}/utils.go | 2 +- transcoder/src/settings.go | 16 +- 8 files changed, 449 insertions(+), 207 deletions(-) create mode 100644 transcoder/src/api/streams.go rename transcoder/{ => src/api}/utils.go (99%) diff --git a/transcoder/.env.example b/transcoder/.env.example index 652f0507..f62bf54b 100644 --- a/transcoder/.env.example +++ b/transcoder/.env.example @@ -4,7 +4,7 @@ # used to verify who's making the jwt JWT_ISSUER=$PUBLIC_URL # keibi's server to retrieve the public jwt secret -AUTH_SERVER=http://auth:4568 +JWKS_URL=http://auth:4568/.well-known/jwks.json # where to store temporary transcoded files GOCODER_CACHE_ROOT="/cache" diff --git a/transcoder/go.mod b/transcoder/go.mod index 4015c003..5009affd 100644 --- a/transcoder/go.mod +++ b/transcoder/go.mod @@ -4,17 +4,37 @@ go 1.24.2 require ( github.com/aws/aws-sdk-go-v2 v1.36.5 - github.com/aws/aws-sdk-go-v2/service/s3 v1.83.0 + github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0 github.com/disintegration/imaging v1.6.2 github.com/golang-migrate/migrate/v4 v4.18.3 github.com/labstack/echo-jwt/v4 v4.3.1 github.com/labstack/echo/v4 v4.13.4 github.com/lib/pq v1.10.9 + github.com/swaggo/echo-swagger v1.4.1 gitlab.com/opennota/screengen v1.0.2 golang.org/x/sync v0.16.0 gopkg.in/vansante/go-ffprobe.v2 v2.2.1 ) +require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.2.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-openapi/jsonpointer v0.21.1 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/swaggo/files/v2 v2.0.2 // indirect + github.com/swaggo/swag v1.16.5 // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/tools v0.35.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + require ( github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect github.com/aws/aws-sdk-go-v2/config v1.29.17 @@ -34,7 +54,7 @@ require ( github.com/aws/smithy-go v1.22.4 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/goccy/go-json v0.10.5 // indirect - github.com/golang-jwt/jwt/v5 v5.2.2 + github.com/golang-jwt/jwt/v5 v5.2.3 github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/labstack/gommon v0.4.2 // indirect diff --git a/transcoder/go.sum b/transcoder/go.sum index ce3c1f23..1ad6c928 100644 --- a/transcoder/go.sum +++ b/transcoder/go.sum @@ -1,3 +1,15 @@ +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.2.1 h1:QsZ4TjvwiMpat6gBCBxEQI0rcS9ehtkKtSpiUnd9N28= +github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/aws/aws-sdk-go-v2 v1.36.5 h1:0OF9RiEMEdDdZEMqF9MRjevyxAQcf6gY+E7vwBILFj0= github.com/aws/aws-sdk-go-v2 v1.36.5/go.mod h1:EYrzvCCN9CMUTa5+6lf6MM4tq3Zjp8UhSGR/cBsjai0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 h1:12SpdwU8Djs+YGklkinSSlcrPyj3H4VifVsKf78KbwA= @@ -26,6 +38,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17 h1:qcLWgdhq45sDM github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17/go.mod h1:M+jkjBFZ2J6DJrjMv2+vkBbuht6kxJYtJiwoVgX4p4U= github.com/aws/aws-sdk-go-v2/service/s3 v1.83.0 h1:5Y75q0RPQoAbieyOuGLhjV9P3txvYgXv2lg0UwJOfmE= github.com/aws/aws-sdk-go-v2/service/s3 v1.83.0/go.mod h1:kUklwasNoCn5YpyAqC/97r6dzTA1SRKJfKq16SXeoDU= +github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0 h1:0reDqfEN+tB+sozj2r92Bep8MEwBZgtAXTND1Kk9OXg= +github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0/go.mod h1:kUklwasNoCn5YpyAqC/97r6dzTA1SRKJfKq16SXeoDU= github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 h1:AIRJ3lfb2w/1/8wOOSqYb9fUKGwQbtysJ2H1MofRUPg= github.com/aws/aws-sdk-go-v2/service/sso v1.25.5/go.mod h1:b7SiVprpU+iGazDUqvRSLf5XmCdn+JtT1on7uNL6Ipc= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 h1:BpOxT3yhLwSJ77qIY3DoHAQjZsc4HEGfMCE4NGy3uFg= @@ -34,22 +48,77 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 h1:NFOJ/NXEGV4Rq//71Hs1jC/NvPs1 github.com/aws/aws-sdk-go-v2/service/sts v1.34.0/go.mod h1:7ph2tGpfQvwzgistp2+zga9f+bCjlQJPkPUmMgDSD7w= github.com/aws/smithy-go v1.22.4 h1:uqXzVZNuNexwc/xrh6Tb56u89WDlJY6HS+KC0S4QSjw= github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +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/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/dhui/dktest v0.4.5 h1:uUfYBIVREmj/Rw6MvgmqNAYzTiKOHJak+enB5Di73MM= +github.com/dhui/dktest v0.4.5/go.mod h1:tmcyeHDKagvlDrz7gDKq4UAJOLIfVZYkfD5OnHDwcCo= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4= +github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= +github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0= +github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs= github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo-jwt/v4 v4.3.1 h1:d8+/qf8nx7RxeL46LtoIwHJsH2PNN8xXCQ/jDianycE= github.com/labstack/echo-jwt/v4 v4.3.1/go.mod h1:yJi83kN8S/5vePVPd+7ID75P4PqPNVRs2HVeuvYJH00= +github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= +github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA= github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= @@ -68,33 +137,126 @@ github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLO github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +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/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/swaggo/echo-swagger v1.4.1 h1:Yf0uPaJWp1uRtDloZALyLnvdBeoEL5Kc7DtnjzO/TUk= +github.com/swaggo/echo-swagger v1.4.1/go.mod h1:C8bSi+9yH2FLZsnhqMZLIZddpUxZdBYuNHbtaS1Hljc= +github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw= +github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= +github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU= +github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0= +github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w= +github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its= +github.com/swaggo/swag v1.16.5 h1:nMf2fEV1TetMTJb4XzD0Lz7jFfKJmJKGTygEey8NSxM= +github.com/swaggo/swag v1.16.5/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= gitlab.com/opennota/screengen v1.0.2 h1:GxYTJdAPEzmg5v5CV4dgn45JVW+EcXXAvCxhE7w6UDw= gitlab.com/opennota/screengen v1.0.2/go.mod h1:4kED4yriw2zslwYmXFCa5qCvEKwleBA7l5OE+d94NTU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +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= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= +golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas= golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +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-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/vansante/go-ffprobe.v2 v2.2.1 h1:sFV08OT1eZ1yroLCZVClIVd9YySgCh9eGjBWO0oRayI= gopkg.in/vansante/go-ffprobe.v2 v2.2.1/go.mod h1:qF0AlAjk7Nqzqf3y333Ly+KxN3cKF2JqA3JT5ZheUGE= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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= diff --git a/transcoder/main.go b/transcoder/main.go index 15187667..d9c7885b 100644 --- a/transcoder/main.go +++ b/transcoder/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "errors" "fmt" "io" "mime" @@ -10,7 +9,10 @@ import ( "path/filepath" "strconv" + _ "github.com/zoriya/kyoo/transcoder/docs" + "github.com/golang-jwt/jwt/v5" + echoSwagger "github.com/swaggo/echo-swagger" "github.com/zoriya/kyoo/transcoder/src" "github.com/zoriya/kyoo/transcoder/src/api" "github.com/zoriya/kyoo/transcoder/src/utils" @@ -23,176 +25,6 @@ import ( "github.com/labstack/echo/v4/middleware" ) -// Direct video -// -// Retrieve the raw video stream, in the same container as the one on the server. No transcoding or -// transmuxing is done. -// -// Path: /:path/direct -func DirectStream(c echo.Context) error { - path, _, err := GetPath(c) - if err != nil { - return err - } - return c.File(path) -} - -// Get master playlist -// -// Get a master playlist containing all possible video qualities and audios available for this resource. -// Note that the direct stream is missing (since the direct is not an hls stream) and -// subtitles/fonts are not included to support more codecs than just webvtt. -// -// Path: /:path/master.m3u8 -func (h *Handler) GetMaster(c echo.Context) error { - client, err := GetClientId(c) - if err != nil { - return err - } - path, sha, err := GetPath(c) - if err != nil { - return err - } - - ret, err := h.transcoder.GetMaster(c.Request().Context(), path, client, sha) - if err != nil { - return err - } - return c.String(http.StatusOK, ret) -} - -// Transcode video -// -// Transcode the video to the selected quality. -// This route can take a few seconds to respond since it will way for at least one segment to be -// available. -// -// Path: /:path/:video/:quality/index.m3u8 -func (h *Handler) GetVideoIndex(c echo.Context) error { - video, err := strconv.ParseInt(c.Param("video"), 10, 32) - if err != nil { - return err - } - quality, err := src.QualityFromString(c.Param("quality")) - if err != nil { - return err - } - client, err := GetClientId(c) - if err != nil { - return err - } - path, sha, err := GetPath(c) - if err != nil { - return err - } - - ret, err := h.transcoder.GetVideoIndex(c.Request().Context(), path, uint32(video), quality, client, sha) - if err != nil { - return err - } - return c.String(http.StatusOK, ret) -} - -// Transcode audio -// -// Get the selected audio -// This route can take a few seconds to respond since it will way for at least one segment to be -// available. -// -// Path: /:path/audio/:audio/index.m3u8 -func (h *Handler) GetAudioIndex(c echo.Context) error { - audio, err := strconv.ParseInt(c.Param("audio"), 10, 32) - if err != nil { - return err - } - client, err := GetClientId(c) - if err != nil { - return err - } - path, sha, err := GetPath(c) - if err != nil { - return err - } - - ret, err := h.transcoder.GetAudioIndex(c.Request().Context(), path, uint32(audio), client, sha) - if err != nil { - return err - } - return c.String(http.StatusOK, ret) -} - -// Get transmuxed chunk -// -// Retrieve a chunk of a transmuxed video. -// -// Path: /:path/:video/:quality/segments-:chunk.ts -func (h *Handler) GetVideoSegment(c echo.Context) error { - video, err := strconv.ParseInt(c.Param("video"), 10, 32) - if err != nil { - return err - } - quality, err := src.QualityFromString(c.Param("quality")) - if err != nil { - return err - } - segment, err := ParseSegment(c.Param("chunk")) - if err != nil { - return err - } - client, err := GetClientId(c) - if err != nil { - return err - } - path, sha, err := GetPath(c) - if err != nil { - return err - } - - ret, err := h.transcoder.GetVideoSegment( - c.Request().Context(), - path, - uint32(video), - quality, - segment, - client, - sha, - ) - if err != nil { - return err - } - return c.File(ret) -} - -// Get audio chunk -// -// Retrieve a chunk of a transcoded audio. -// -// Path: /:path/audio/:audio/segments-:chunk.ts -func (h *Handler) GetAudioSegment(c echo.Context) error { - audio, err := strconv.ParseInt(c.Param("audio"), 10, 32) - if err != nil { - return err - } - segment, err := ParseSegment(c.Param("chunk")) - if err != nil { - return err - } - client, err := GetClientId(c) - if err != nil { - return err - } - path, sha, err := GetPath(c) - if err != nil { - return err - } - - ret, err := h.transcoder.GetAudioSegment(c.Request().Context(), path, uint32(audio), segment, client, sha) - if err != nil { - return err - } - return c.File(ret) -} - // Identify // // Identify metadata about a file. @@ -362,27 +194,43 @@ func guessMimeType(path string, content any) (string, error) { return mimeType, nil } +// @title gocoder - Kyoo's transcoder +// @version 1.0 +// @description Real time transcoder. + +// @contact.name Repository +// @contact.url https://github.com/zoriya/kyoo + +// @license.name GPL-3.0 +// @license.url https://www.gnu.org/licenses/gpl-3.0.en.html + +// @host kyoo.zoriya.dev +// @BasePath /video + +// @securityDefinitions.apiKey Token +// @in header +// @name Authorization + +// @securityDefinitions.apiKey Jwt +// @in header +// @name Authorization func main() { e := echo.New() - - if err := run(e); err != nil { - e.Logger.Fatal(err) - } -} - -func run(e *echo.Echo) (err error) { e.Use(middleware.Logger()) + e.GET("/video/swagger/*", echoSwagger.WrapHandler) e.HTTPErrorHandler = ErrorHandler metadata, err := src.NewMetadataService() if err != nil { - return fmt.Errorf("failed to create metadata service: %w", err) + e.Logger.Fatal("failed to create metadata service: ", err) + return } defer utils.CleanupWithErr(&err, metadata.Close, "failed to close metadata service") transcoder, err := src.NewTranscoder(metadata) if err != nil { - return fmt.Errorf("failed to create transcoder: %w", err) + e.Logger.Fatal("failed to create transcoder: ", err) + return } h := Handler{ @@ -390,48 +238,39 @@ func run(e *echo.Echo) (err error) { metadata: metadata, } - jwksUrl := "" ctx, cancel := context.WithCancel(context.Background()) defer cancel() jwks, err := jwk.NewCache(ctx, httprc.NewClient()) if err != nil { - return fmt.Errorf("failed to create jwk cache: %w", err) + e.Logger.Fatal("failed to create jwk cache: ", err) + return } - jwks.Register(ctx, jwksUrl) + jwks.Register(ctx, src.Settings.JwksUrl) g := e.Group("/video") g.Use(echojwt.WithConfig(echojwt.Config{ - KeyFunc: func(token *jwt.Token) (interface{}, error) { - return jwks.CachedSet(jwksUrl) + KeyFunc: func(token *jwt.Token) (any, error) { + return jwks.CachedSet(src.Settings.JwksUrl) // kid, ok := token.Header["kid"] // if !ok { // return nil, errors.New("missing kid in jwt") // } - // keys, err := jwks.CachedSet(jwksUrl) + // keys, err := jwks.CachedSet(src.Settings.JwksUrl) // if err != nil { // return nil, err // } // return keys.LookupKeyID(kid.(string)) }, })) - g.GET("/:path/direct", DirectStream) - g.GET("/:path/direct/:identifier", DirectStream) - g.GET("/:path/master.m3u8", h.GetMaster) - g.GET("/:path/:video/:quality/index.m3u8", h.GetVideoIndex) - g.GET("/:path/audio/:audio/index.m3u8", h.GetAudioIndex) - g.GET("/:path/:video/:quality/:chunk", h.GetVideoSegment) - g.GET("/:path/audio/:audio/:chunk", h.GetAudioSegment) g.GET("/:path/info", h.GetInfo) g.GET("/:path/thumbnails.png", h.GetThumbnails) g.GET("/:path/thumbnails.vtt", h.GetThumbnailsVtt) g.GET("/:path/attachment/:name", h.GetAttachment) g.GET("/:path/subtitle/:name", h.GetSubtitle) + api.RegisterStreamHandlers(g) api.RegisterPProfHandlers(e) - if err := e.Start(":7666"); err != nil { - return fmt.Errorf("failed to start server: %w", err) - } - return nil + e.Logger.Fatal(e.Start(":7666")) } diff --git a/transcoder/shell.nix b/transcoder/shell.nix index cd5c85e4..fafc50a7 100644 --- a/transcoder/shell.nix +++ b/transcoder/shell.nix @@ -4,6 +4,7 @@ pkgs.mkShell { go wgo go-migrate + go-swag # for psql in cli (+ pgformatter for sql files) postgresql_15 pgformatter diff --git a/transcoder/src/api/streams.go b/transcoder/src/api/streams.go new file mode 100644 index 00000000..630f8cd8 --- /dev/null +++ b/transcoder/src/api/streams.go @@ -0,0 +1,216 @@ +package api + +import ( + "net/http" + "strconv" + + "github.com/labstack/echo/v4" + "github.com/zoriya/kyoo/transcoder/src" +) + +type handler struct { + transcoder *src.Transcoder +} + +// @Summary Direct video +// +// @Description Retrieve the raw video stream, in the same container as the one on the server. +// @Description No transcoding or transmuxing is done. +// +// @Tags streams +// @Param path path string true "Base64 of a video's path" format(base64) example(L3ZpZGVvL2J1YmJsZS5ta3YK) +// @Param identifier path string false "anything, this can be used for the automatic file name when downloading from the browser" example(bubble.mkv) +// +// @Success 206 file "Video file (supports byte-requests)" +// @Router /:path/direct [get] +func DirectStream(c echo.Context) error { + path, _, err := GetPath(c) + if err != nil { + return err + } + return c.File(path) +} + +// @Summary Get master playlist +// +// @Description Get a master playlist containing all possible video qualities and audios available for this resource. +// @Description Note that the direct stream is missing (since the direct is not an hls stream) and +// @Description subtitles/fonts are not included to support more codecs than just webvtt. +// +// @Tags streams +// @Param path path string true "Base64 of a video's path" format(base64) example(L3ZpZGVvL2J1YmJsZS5ta3YK) +// +// @Success 200 file "Master playlist with all available stream qualities" +// @Router /:path/master.m3u8 [get] +func (h *handler) GetMaster(c echo.Context) error { + client, err := GetClientId(c) + if err != nil { + return err + } + path, sha, err := GetPath(c) + if err != nil { + return err + } + + ret, err := h.transcoder.GetMaster(c.Request().Context(), path, client, sha) + if err != nil { + return err + } + return c.String(http.StatusOK, ret) +} + +// Transcode video +// +// Transcode the video to the selected quality. +// This route can take a few seconds to respond since it will way for at least one segment to be +// available. +// +// Path: /:path/:video/:quality/index.m3u8 +// +// PRIVATE ROUTE (not documented in swagger, can change at any time) +// Only reached via the master.m3u8. +func (h *handler) GetVideoIndex(c echo.Context) error { + video, err := strconv.ParseInt(c.Param("video"), 10, 32) + if err != nil { + return err + } + quality, err := src.QualityFromString(c.Param("quality")) + if err != nil { + return err + } + client, err := GetClientId(c) + if err != nil { + return err + } + path, sha, err := GetPath(c) + if err != nil { + return err + } + + ret, err := h.transcoder.GetVideoIndex(c.Request().Context(), path, uint32(video), quality, client, sha) + if err != nil { + return err + } + return c.String(http.StatusOK, ret) +} + +// Transcode audio +// +// Get the selected audio +// This route can take a few seconds to respond since it will way for at least one segment to be +// available. +// +// Path: /:path/audio/:audio/index.m3u8 +// +// PRIVATE ROUTE (not documented in swagger, can change at any time) +// Only reached via the master.m3u8. +func (h *handler) GetAudioIndex(c echo.Context) error { + audio, err := strconv.ParseInt(c.Param("audio"), 10, 32) + if err != nil { + return err + } + client, err := GetClientId(c) + if err != nil { + return err + } + path, sha, err := GetPath(c) + if err != nil { + return err + } + + ret, err := h.transcoder.GetAudioIndex(c.Request().Context(), path, uint32(audio), client, sha) + if err != nil { + return err + } + return c.String(http.StatusOK, ret) +} + +// Get transmuxed chunk +// +// Retrieve a chunk of a transmuxed video. +// +// Path: /:path/:video/:quality/segments-:chunk.ts +// +// PRIVATE ROUTE (not documented in swagger, can change at any time) +// Only reached via the master.m3u8. +func (h *handler) GetVideoSegment(c echo.Context) error { + video, err := strconv.ParseInt(c.Param("video"), 10, 32) + if err != nil { + return err + } + quality, err := src.QualityFromString(c.Param("quality")) + if err != nil { + return err + } + segment, err := ParseSegment(c.Param("chunk")) + if err != nil { + return err + } + client, err := GetClientId(c) + if err != nil { + return err + } + path, sha, err := GetPath(c) + if err != nil { + return err + } + + ret, err := h.transcoder.GetVideoSegment( + c.Request().Context(), + path, + uint32(video), + quality, + segment, + client, + sha, + ) + if err != nil { + return err + } + return c.File(ret) +} + +// Get audio chunk +// +// Retrieve a chunk of a transcoded audio. +// +// Path: /:path/audio/:audio/segments-:chunk.ts +// +// PRIVATE ROUTE (not documented in swagger, can change at any time) +// Only reached via the master.m3u8. +func (h *handler) GetAudioSegment(c echo.Context) error { + audio, err := strconv.ParseInt(c.Param("audio"), 10, 32) + if err != nil { + return err + } + segment, err := ParseSegment(c.Param("chunk")) + if err != nil { + return err + } + client, err := GetClientId(c) + if err != nil { + return err + } + path, sha, err := GetPath(c) + if err != nil { + return err + } + + ret, err := h.transcoder.GetAudioSegment(c.Request().Context(), path, uint32(audio), segment, client, sha) + if err != nil { + return err + } + return c.File(ret) +} + +func RegisterStreamHandlers(e *echo.Group, transcoder *src.Transcoder) { + h := handler{transcoder} + + e.GET("/:path/direct", DirectStream) + e.GET("/:path/direct/:identifier", DirectStream) + e.GET("/:path/master.m3u8", h.GetMaster) + e.GET("/:path/:video/:quality/index.m3u8", h.GetVideoIndex) + e.GET("/:path/audio/:audio/index.m3u8", h.GetAudioIndex) + e.GET("/:path/:video/:quality/:chunk", h.GetVideoSegment) + e.GET("/:path/audio/:audio/:chunk", h.GetAudioSegment) +} diff --git a/transcoder/utils.go b/transcoder/src/api/utils.go similarity index 99% rename from transcoder/utils.go rename to transcoder/src/api/utils.go index bdd85ce2..35b3a8b2 100644 --- a/transcoder/utils.go +++ b/transcoder/src/api/utils.go @@ -1,4 +1,4 @@ -package main +package api import ( "crypto/sha1" diff --git a/transcoder/src/settings.go b/transcoder/src/settings.go index 05f40297..df180525 100644 --- a/transcoder/src/settings.go +++ b/transcoder/src/settings.go @@ -14,9 +14,11 @@ func GetEnvOr(env string, def string) string { } type SettingsT struct { - Outpath string - SafePath string - HwAccel HwAccelT + Outpath string + SafePath string + JwksUrl string + JwtIssuer string + HwAccel HwAccelT } type HwAccelT struct { @@ -29,7 +31,9 @@ type HwAccelT struct { var Settings = SettingsT{ // we manually add a folder to make sure we do not delete user data. - Outpath: path.Join(GetEnvOr("GOCODER_CACHE_ROOT", "/cache"), "kyoo_cache"), - SafePath: GetEnvOr("GOCODER_SAFE_PATH", "/video"), - HwAccel: DetectHardwareAccel(), + Outpath: path.Join(GetEnvOr("GOCODER_CACHE_ROOT", "/cache"), "kyoo_cache"), + SafePath: GetEnvOr("GOCODER_SAFE_PATH", "/video"), + JwksUrl: GetEnvOr("JWKS_URL", "http://auth:4568/.well-known/jwks.json"), + JwtIssuer: GetEnvOr("JWT_ISSUER", "http://localhost:8901"), + HwAccel: DetectHardwareAccel(), }