From 8b0f3f46564ea4a071ca8a8e2707303260edde71 Mon Sep 17 00:00:00 2001 From: Victor Hang Date: Mon, 30 Sep 2024 21:47:04 +0200 Subject: [PATCH] =?UTF-8?q?feat=20=E2=9C=A8:=20add=20ability=20to=20config?= =?UTF-8?q?ure=20a=20socks5=20or=20http=20proxy=20in=20config=20file,=20cl?= =?UTF-8?q?ose=20#40?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Victor Hang --- README.md | 5 +++- cmd/query_search.go | 2 +- cmd/query_subscribed.go | 4 +-- go.mod | 1 + go.sum | 2 ++ pkg/config/config.go | 1 + pkg/youtube/invidious_search.go | 47 +++++++++++++++++++++++++++++---- pkg/youtube/subscribed.go | 8 +++--- 8 files changed, 57 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 903bba4..437267c 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ download_dir: ~/Videos/YouTube history: enable: true invidious: + proxy: '' instance: invidious.jing.rocks loglevel: info youtube: @@ -140,7 +141,7 @@ youtube: specified in the configuration file for subscribed channels. If you prefer to use your Youtube user-subscribed channels, set this to `false`. -- `channels.subscribed: []` is a list of channel Ids. To be used with `local: true`. +- **`channels.subscribed: []`** is a list of channel Ids. To be used with `local: true`. - **OAuth** - You need to enable OAuth authentication with YouTube to access your subscribed channels. @@ -148,6 +149,8 @@ youtube: The following scope is also required: `https://www.googleapis.com/auth/youtube.readonly` +- **`invidious.proxy:`** - Must be set with either `socks5://:1234` or `http://:4567`. Leave empty to disable. + ## Files - **`watched_history.json`** - This file, located in `$HOME/.config/ytui/`, diff --git a/cmd/query_search.go b/cmd/query_search.go index c469ce4..030d762 100644 --- a/cmd/query_search.go +++ b/cmd/query_search.go @@ -46,7 +46,7 @@ Press enter to run any of the videos.`, } utils.Logger.Debug("Config file read successfully.", zap.String("config_file", configPath)) - result, err := youtube.SearchVideos(query, false) + result, err := youtube.SearchVideos(query, viper.GetString("invidious.proxy"), false) if err != nil { utils.Logger.Fatal("Error searching for videos.", zap.Error(err)) os.Exit(1) diff --git a/cmd/query_subscribed.go b/cmd/query_subscribed.go index 151aa8a..b025bb8 100644 --- a/cmd/query_subscribed.go +++ b/cmd/query_subscribed.go @@ -56,14 +56,14 @@ It will also only pick from the 50 most relevants subscribed channels in your Yo } yt := <-apiChan utils.Logger.Info("YouTube API authenticated successfully.") - result, err = yt.GetAllSubscribedChannelsVideos() + result, err = yt.GetAllSubscribedChannelsVideos(viper.GetString("invidious.proxy")) if err != nil { utils.Logger.Fatal("Failed to get all subscribed channels videos.", zap.Error(err)) os.Exit(1) } } else { utils.Logger.Info("Using local configuration for subscribed channels.") - result, err = youtube.GetLocalSubscribedChannelsVideos() + result, err = youtube.GetLocalSubscribedChannelsVideos(viper.GetString("invidious.proxy")) if err != nil { utils.Logger.Fatal("Failed to get local subscribed channels videos.", zap.Error(err)) os.Exit(1) diff --git a/go.mod b/go.mod index 0074b30..7e7d36f 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 go.uber.org/zap v1.27.0 + golang.org/x/net v0.23.0 golang.org/x/oauth2 v0.23.0 ) diff --git a/go.sum b/go.sum index 8539812..bbefabf 100644 --- a/go.sum +++ b/go.sum @@ -101,6 +101,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/pkg/config/config.go b/pkg/config/config.go index 9da9921..1eac03b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -24,6 +24,7 @@ func CreateDefaultConfigFile(filePath string) { viper.SetDefault("download_dir", downloadDir) viper.SetDefault("logLevel", "info") viper.SetDefault("invidious", map[string]interface{}{ + "proxy": "", "instance": "invidious.jing.rocks", }) viper.SetDefault("history", map[string]interface{}{ diff --git a/pkg/youtube/invidious_search.go b/pkg/youtube/invidious_search.go index eb5fa41..4111d68 100644 --- a/pkg/youtube/invidious_search.go +++ b/pkg/youtube/invidious_search.go @@ -11,6 +11,7 @@ import ( "github.com/spf13/viper" "go.uber.org/zap" + "golang.org/x/net/proxy" "github.com/Banh-Canh/ytui/pkg/config" "github.com/Banh-Canh/ytui/pkg/utils" @@ -84,7 +85,7 @@ func getInvidiousInstance() (string, error) { return invidiousInstance, nil } -func SearchVideos(query string, subscription bool) (*[]SearchResultItem, error) { +func SearchVideos(query, proxyURLString string, subscription bool) (*[]SearchResultItem, error) { var baseURL string invidiousInstance, err := getInvidiousInstance() if err != nil { @@ -98,10 +99,46 @@ func SearchVideos(query string, subscription bool) (*[]SearchResultItem, error) baseURL = fmt.Sprintf("https://%s/api/v1/channels/%s/videos", invidiousInstance, query) utils.Logger.Debug("Subscription URL for channel videos constructed.", zap.String("subscription_url", baseURL)) - resp, err := http.Get(baseURL) - if err != nil { - utils.Logger.Error("Error fetching data from Invidious API.", zap.String("url", baseURL), zap.Error(err)) - return nil, fmt.Errorf("error fetching data from YouTube API: %v", err) + var resp *http.Response + if proxyURLString != "" { + proxyURL, err := url.Parse(proxyURLString) + utils.Logger.Info("A proxy is configured and will be used.", zap.String("proxy_url", proxyURLString)) + if err != nil { + return nil, fmt.Errorf("error parsing proxy URL: %v", err) + } + + var transport *http.Transport + switch proxyURL.Scheme { + case "socks5": + dialer, err := proxy.FromURL(proxyURL, proxy.Direct) + if err != nil { + return nil, fmt.Errorf("error creating proxy dialer: %v", err) + } + transport = &http.Transport{Dial: dialer.Dial} + + case "http", "https": + transport = &http.Transport{Proxy: http.ProxyURL(proxyURL)} + + default: + return nil, fmt.Errorf("unsupported proxy scheme: %s", proxyURL.Scheme) + } + + // Create a client with the transport + client := &http.Client{Transport: transport} + + // Make the request + resp, err = client.Get(baseURL) + if err != nil { + utils.Logger.Error("Error fetching data from Invidious API.", zap.String("url", baseURL), zap.Error(err)) + return nil, fmt.Errorf("error creating the request: %v", err) + } + } else { + // Make the request without a proxy + resp, err = http.Get(baseURL) + if err != nil { + utils.Logger.Error("Error fetching data from Invidious API.", zap.String("url", baseURL), zap.Error(err)) + return nil, fmt.Errorf("error fetching data from YouTube API: %v", err) + } } defer resp.Body.Close() diff --git a/pkg/youtube/subscribed.go b/pkg/youtube/subscribed.go index a357cec..8cd2297 100644 --- a/pkg/youtube/subscribed.go +++ b/pkg/youtube/subscribed.go @@ -107,7 +107,7 @@ func GetLocalSubscribedChannels() ([]string, error) { return subscribed, nil } -func (yt *YouTubeAPI) GetAllSubscribedChannelsVideos() (*[]SearchResultItem, error) { +func (yt *YouTubeAPI) GetAllSubscribedChannelsVideos(proxyURLString string) (*[]SearchResultItem, error) { utils.Logger.Info("Starting to fetch videos for all subscribed channels.") channelIds, err := yt.GetSubscribedChannels() @@ -121,7 +121,7 @@ func (yt *YouTubeAPI) GetAllSubscribedChannelsVideos() (*[]SearchResultItem, err for _, channelId := range channelIds { utils.Logger.Info("Fetching videos for channel.", zap.String("channel_id", channelId)) - videosResponse, err := SearchVideos(channelId, true) + videosResponse, err := SearchVideos(channelId, proxyURLString, true) if err != nil { utils.Logger.Error("Failed to fetch videos for channel.", zap.String("channel_id", channelId), zap.Error(err)) return nil, err @@ -155,7 +155,7 @@ func (yt *YouTubeAPI) GetAllSubscribedChannelsVideos() (*[]SearchResultItem, err return aggregatedResponse, nil } -func GetLocalSubscribedChannelsVideos() (*[]SearchResultItem, error) { +func GetLocalSubscribedChannelsVideos(proxyURLString string) (*[]SearchResultItem, error) { utils.Logger.Info("Starting to fetch videos for local subscribed channels.") // Get local subscribed channels @@ -170,7 +170,7 @@ func GetLocalSubscribedChannelsVideos() (*[]SearchResultItem, error) { for _, channelId := range channelIds { utils.Logger.Debug("Fetching videos for channel.", zap.String("channel_id", channelId)) - videosResponse, err := SearchVideos(channelId, true) + videosResponse, err := SearchVideos(channelId, proxyURLString, true) if err != nil { utils.Logger.Error("Failed to fetch videos for channel.", zap.String("channel_id", channelId), zap.Error(err)) return nil, err