From 6ce4b888ba945371d45c641dfc5244683c2bdcfb Mon Sep 17 00:00:00 2001 From: partisan Date: Wed, 11 Sep 2024 12:49:06 +0200 Subject: [PATCH 01/13] fix: remove random location when lang is set --- text-google.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/text-google.go b/text-google.go index 4ea7866..006ebc0 100644 --- a/text-google.go +++ b/text-google.go @@ -66,12 +66,17 @@ func buildSearchURL(query, safe, lang string, page, resultsPerPage int) string { } langParam := "" - if lang != "" { - langParam = "&lr=lang_" + lang - } + var glParam, uuleParam string - // Generate random geolocation - glParam, uuleParam := getRandomGeoLocation() + if lang != "" { + // Use lang as the geolocation + langParam = "&lr=lang_" + lang + glParam = "&gl=" + lang + uuleParam = "" + } else { + // Use random geolocation + glParam, uuleParam = getRandomGeoLocation() + } startIndex := (page - 1) * resultsPerPage return fmt.Sprintf("https://www.google.com/search?q=%s%s%s%s%s&start=%d", From 6ed06b05b129d84fe1aaec1407576c6ba4e1a64a Mon Sep 17 00:00:00 2001 From: partisan Date: Thu, 12 Sep 2024 22:11:39 +0200 Subject: [PATCH 02/13] i need better way to cache images --- go.mod | 3 +++ go.sum | 8 ++++++++ imageproxy.go | 21 ++++++--------------- images-bing.go | 9 +++++++-- images-deviantart.go | 2 +- images-quant.go | 41 ++++++++++++++++++++++++++++++----------- images.go | 6 +++--- main.go | 2 +- 8 files changed, 59 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index f6755b1..a747de0 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,9 @@ require ( ) require ( + github.com/chai2010/webp v1.1.1 // indirect + github.com/disintegration/imaging v1.6.2 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + golang.org/x/image v0.20.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index 7fab23f..4120d74 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,16 @@ github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VP github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk= +github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU= github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732 h1:XYUCaZrW8ckGWlCRJKCSoh/iFwlpX316a8yY9IFEzv8= github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93rg= github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= +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/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= @@ -31,6 +35,10 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= +golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= 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/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/imageproxy.go b/imageproxy.go index 74a1cbb..4dd7478 100644 --- a/imageproxy.go +++ b/imageproxy.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "io" "net/http" ) @@ -14,20 +13,12 @@ func handleImageProxy(w http.ResponseWriter, r *http.Request) { return } - // Try to fetch the image from Bing first - bingURL := fmt.Sprintf("https://tse.mm.bing.net/th?q=%s", imageURL) - resp, err := http.Get(bingURL) - if err != nil || resp.StatusCode != http.StatusOK { - // If fetching from Bing fails, attempt to fetch from the original image URL - printWarn("Error fetching image from Bing, trying original URL.") - - // Attempt to fetch the image directly - resp, err = http.Get(imageURL) - if err != nil { - printWarn("Error fetching image: %v", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } + // Fetch the image from the external URL + resp, err := http.Get(imageURL) + if err != nil { + printWarn("Error fetching image: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return } defer resp.Body.Close() diff --git a/images-bing.go b/images-bing.go index d6e37c5..48ba32d 100644 --- a/images-bing.go +++ b/images-bing.go @@ -12,6 +12,7 @@ import ( "github.com/PuerkitoBio/goquery" ) +// PerformBingImageSearch performs a Bing image search and returns the results. func PerformBingImageSearch(query, safe, lang string, page int) ([]ImageSearchResult, time.Duration, error) { startTime := time.Now() @@ -66,14 +67,16 @@ func PerformBingImageSearch(query, safe, lang string, page int) ([]ImageSearchRe if err := json.Unmarshal([]byte(metadata), &data); err == nil { mediaURL, ok := data["murl"].(string) if ok { + // Apply the image proxy + proxiedURL := "/imgproxy?url=" + mediaURL results = append(results, ImageSearchResult{ Thumbnail: imgSrc, Title: strings.TrimSpace(title), Media: mediaURL, + Source: mediaURL, + ThumbProxy: proxiedURL, // Use the proxied URL Width: width, Height: height, - Source: mediaURL, - ThumbProxy: imgSrc, }) } } @@ -89,6 +92,7 @@ func PerformBingImageSearch(query, safe, lang string, page int) ([]ImageSearchRe return results, duration, nil } +// buildBingSearchURL constructs the search URL for Bing Image Search func buildBingSearchURL(query string, page int) string { baseURL := "https://www.bing.com/images/search" params := url.Values{} @@ -99,6 +103,7 @@ func buildBingSearchURL(query string, page int) string { return baseURL + "?" + params.Encode() } +// Example usage in main (commented out for clarity) // func main() { // results, duration, err := PerformBingImageSearch("kittens", "false", "en", 1) // if err != nil { diff --git a/images-deviantart.go b/images-deviantart.go index cbeeea4..3b12def 100644 --- a/images-deviantart.go +++ b/images-deviantart.go @@ -156,7 +156,7 @@ func PerformDeviantArtImageSearch(query, safe, lang string, page int) ([]ImageSe Width: 0, Height: 0, Source: resultURL, - ThumbProxy: imgSrc, + ThumbProxy: "/imgproxy?url=" + imgSrc, } } }(imgSrc, resultURL, title) diff --git a/images-quant.go b/images-quant.go index 0dccdec..8690239 100644 --- a/images-quant.go +++ b/images-quant.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "net/url" + "sync" "time" ) @@ -141,19 +142,37 @@ func PerformQwantImageSearch(query, safe, lang string, page int) ([]ImageSearchR return nil, 0, fmt.Errorf("decoding response: %v", err) } - var results []ImageSearchResult - for _, item := range apiResp.Data.Result.Items { - results = append(results, ImageSearchResult{ - Thumbnail: item.Thumbnail, - Title: item.Title, - Media: item.Media, - Source: item.Url, - ThumbProxy: "/img_proxy?url=" + url.QueryEscape(item.Media), // New proxy not exactly working as intended - Width: item.Width, - Height: item.Height, - }) + var wg sync.WaitGroup + results := make([]ImageSearchResult, len(apiResp.Data.Result.Items)) + + for i, item := range apiResp.Data.Result.Items { + wg.Add(1) + go func(i int, item struct { + Media string `json:"media"` + Thumbnail string `json:"thumbnail"` + Title string `json:"title"` + Url string `json:"url"` + Width int `json:"width"` + Height int `json:"height"` + }) { + defer wg.Done() + + // Populate the result + results[i] = ImageSearchResult{ + Thumbnail: item.Thumbnail, + Title: item.Title, + Media: item.Media, + Source: item.Url, + ThumbProxy: "/imgproxy?url=" + item.Media, + Width: item.Width, + Height: item.Height, + } + }(i, item) } + // Wait for all goroutines to complete + wg.Wait() + duration := time.Since(startTime) // Calculate the duration return results, duration, nil diff --git a/images.go b/images.go index 4c6b957..5a202c7 100755 --- a/images.go +++ b/images.go @@ -13,9 +13,9 @@ var imageSearchEngines []SearchEngine func init() { imageSearchEngines = []SearchEngine{ {Name: "Qwant", Func: wrapImageSearchFunc(PerformQwantImageSearch), Weight: 1}, - {Name: "DeviantArt", Func: wrapImageSearchFunc(PerformDeviantArtImageSearch), Weight: 2}, - {Name: "Bing", Func: wrapImageSearchFunc(PerformBingImageSearch), Weight: 2}, // Bing sometimes returns with low amount of images, this leads to danamica page loading not working - {Name: "Imgur", Func: wrapImageSearchFunc(PerformImgurImageSearch), Weight: 3}, + {Name: "Bing", Func: wrapImageSearchFunc(PerformBingImageSearch), Weight: 2}, + {Name: "DeviantArt", Func: wrapImageSearchFunc(PerformDeviantArtImageSearch), Weight: 3}, + //{Name: "Imgur", Func: wrapImageSearchFunc(PerformImgurImageSearch), Weight: 4}, // Image proxy not working } } diff --git a/main.go b/main.go index 63b60ca..930b583 100755 --- a/main.go +++ b/main.go @@ -183,7 +183,7 @@ func runServer() { http.HandleFunc("/", handleSearch) http.HandleFunc("/search", handleSearch) http.HandleFunc("/suggestions", handleSuggestions) - http.HandleFunc("/img_proxy", handleImageProxy) + http.HandleFunc("/imgproxy", handleImageProxy) http.HandleFunc("/node", handleNodeRequest) http.HandleFunc("/settings", handleSettings) http.HandleFunc("/save-settings", handleSaveSettings) From a5622b9099c6baaa29d0b16e5c343c1cf8001b45 Mon Sep 17 00:00:00 2001 From: partisan Date: Wed, 25 Sep 2024 14:07:12 +0200 Subject: [PATCH 03/13] improved search-suggestions response time --- suggestions.go | 105 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 19 deletions(-) diff --git a/suggestions.go b/suggestions.go index 73e9b02..d11f2be 100644 --- a/suggestions.go +++ b/suggestions.go @@ -6,8 +6,62 @@ import ( "io" "net/http" "net/url" + "sort" + "sync" + "time" ) +// SuggestionSource represents a search suggestion source along with its latency. +type SuggestionSource struct { + Name string + FetchFunc func(string) []string + Latency time.Duration + mu sync.Mutex +} + +// Initialize suggestion sources with default latency values. +var suggestionSources = []SuggestionSource{ + { + Name: "DuckDuckGo", + FetchFunc: fetchDuckDuckGoSuggestions, + Latency: 50 * time.Millisecond, + }, + { + Name: "Edge", + FetchFunc: fetchEdgeSuggestions, + Latency: 50 * time.Millisecond, + }, + { + Name: "Brave", + FetchFunc: fetchBraveSuggestions, + Latency: 50 * time.Millisecond, + }, + { + Name: "Ecosia", + FetchFunc: fetchEcosiaSuggestions, + Latency: 50 * time.Millisecond, + }, + { + Name: "Qwant", + FetchFunc: fetchQwantSuggestions, + Latency: 50 * time.Millisecond, + }, + { + Name: "Startpage", + FetchFunc: fetchStartpageSuggestions, + Latency: 50 * time.Millisecond, + }, + // I advise against it, but you can use it if you want to + // { + // Name: "Google", + // FetchFunc: fetchGoogleSuggestions, + // Latency: 500 * time.Millisecond, + // }, +} + +// Mutex to protect the suggestionSources during sorting. +var suggestionsMU sync.Mutex + func handleSuggestions(w http.ResponseWriter, r *http.Request) { query := r.URL.Query().Get("q") if query == "" { @@ -16,25 +70,27 @@ func handleSuggestions(w http.ResponseWriter, r *http.Request) { return } - // Define the fallback sequence with Google lower in the hierarchy - suggestionSources := []func(string) []string{ - fetchDuckDuckGoSuggestions, - fetchEdgeSuggestions, - fetchBraveSuggestions, - fetchEcosiaSuggestions, - fetchQwantSuggestions, - fetchStartpageSuggestions, - // fetchGoogleSuggestions, // I advise against it, but you can use it if you want to - } + // Sort the suggestion sources based on their latency. + suggestionsMU.Lock() + sort.Slice(suggestionSources, func(i, j int) bool { + return suggestionSources[i].Latency < suggestionSources[j].Latency + }) + suggestionsMU.Unlock() var suggestions []string - for _, fetchFunc := range suggestionSources { - suggestions = fetchFunc(query) + for i := range suggestionSources { + source := &suggestionSources[i] + start := time.Now() + suggestions = source.FetchFunc(query) + elapsed := time.Since(start) + + updateLatency(source, elapsed) + if len(suggestions) > 0 { - printDebug("Suggestions found using %T", fetchFunc) + printDebug("Suggestions found using %s", source.Name) break } else { - printWarn("%T did not return any suggestions or failed.", fetchFunc) + printWarn("%s did not return any suggestions or failed.", source.Name) } } @@ -42,11 +98,19 @@ func handleSuggestions(w http.ResponseWriter, r *http.Request) { printErr("All suggestion services failed. Returning empty response.") } - // Return the final suggestions as JSON + // Return the final suggestions as JSON. w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, `["",%s]`, toJSONStringArray(suggestions)) } +// updateLatency updates the latency of a suggestion source using an exponential moving average. +func updateLatency(source *SuggestionSource, newLatency time.Duration) { + source.mu.Lock() + defer source.mu.Unlock() + const alpha = 0.5 // Smoothing factor. + source.Latency = time.Duration(float64(source.Latency)*(1-alpha) + float64(newLatency)*alpha) +} + func fetchGoogleSuggestions(query string) []string { encodedQuery := url.QueryEscape(query) url := fmt.Sprintf("http://suggestqueries.google.com/complete/search?client=firefox&q=%s", encodedQuery) @@ -82,6 +146,7 @@ func fetchEcosiaSuggestions(query string) []string { return fetchSuggestionsFromURL(url) } +// Is this working? func fetchQwantSuggestions(query string) []string { encodedQuery := url.QueryEscape(query) url := fmt.Sprintf("https://api.qwant.com/v3/suggest?q=%s", encodedQuery) @@ -96,6 +161,7 @@ func fetchStartpageSuggestions(query string) []string { return fetchSuggestionsFromURL(url) } +// fetchSuggestionsFromURL fetches suggestions from the given URL. func fetchSuggestionsFromURL(url string) []string { resp, err := http.Get(url) if err != nil { @@ -110,17 +176,17 @@ func fetchSuggestionsFromURL(url string) []string { return []string{} } - // Log the Content-Type for debugging + // Log the Content-Type for debugging. contentType := resp.Header.Get("Content-Type") printDebug("Response Content-Type from %s: %s", url, contentType) - // Check if the body is non-empty + // Check if the body is non-empty. if len(body) == 0 { printWarn("Received empty response body from %s", url) return []string{} } - // Attempt to parse the response as JSON regardless of Content-Type + // Attempt to parse the response as JSON regardless of Content-Type. var parsedResponse []interface{} if err := json.Unmarshal(body, &parsedResponse); err != nil { printErr("Error parsing JSON from %s: %v", url, err) @@ -128,7 +194,7 @@ func fetchSuggestionsFromURL(url string) []string { return []string{} } - // Ensure the response structure is as expected + // Ensure the response structure is as expected. if len(parsedResponse) < 2 { printWarn("Unexpected response format from %v: %v", url, string(body)) return []string{} @@ -148,6 +214,7 @@ func fetchSuggestionsFromURL(url string) []string { return suggestions } +// toJSONStringArray converts a slice of strings to a JSON array string. func toJSONStringArray(strings []string) string { result := "" for i, str := range strings { From 2679b0428448a804722dec71709c3acb375af4be Mon Sep 17 00:00:00 2001 From: partisan Date: Wed, 25 Sep 2024 18:21:06 +0200 Subject: [PATCH 04/13] added Yahoo to search suggestions --- suggestions.go | 67 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/suggestions.go b/suggestions.go index d11f2be..88095fd 100644 --- a/suggestions.go +++ b/suggestions.go @@ -41,16 +41,21 @@ var suggestionSources = []SuggestionSource{ FetchFunc: fetchEcosiaSuggestions, Latency: 50 * time.Millisecond, }, - { - Name: "Qwant", - FetchFunc: fetchQwantSuggestions, - Latency: 50 * time.Millisecond, - }, + // { // Not working with fetchSuggestionsFromURL func + // Name: "Qwant", + // FetchFunc: fetchQwantSuggestions, + // Latency: 50 * time.Millisecond, + // }, { Name: "Startpage", FetchFunc: fetchStartpageSuggestions, Latency: 50 * time.Millisecond, }, + { + Name: "Yahoo", + FetchFunc: fetchYahooSuggestions, + Latency: 50 * time.Millisecond, + }, // I advise against it, but you can use it if you want to // { // Name: "Google", @@ -161,7 +166,27 @@ func fetchStartpageSuggestions(query string) []string { return fetchSuggestionsFromURL(url) } -// fetchSuggestionsFromURL fetches suggestions from the given URL. +func fetchYahooSuggestions(query string) []string { + encodedQuery := url.QueryEscape(query) + url := fmt.Sprintf("https://search.yahoo.com/sugg/gossip/gossip-us-ura/?output=fxjson&command=%s", encodedQuery) + printDebug("Fetching suggestions from Yahoo: %s", url) + return fetchSuggestionsFromURL(url) +} + +// func fetchBaiduSuggestions(query string) []string { +// encodedQuery := url.QueryEscape(query) +// url := fmt.Sprintf("https://suggestion.baidu.com/su?wd=%s", encodedQuery) +// printDebug("Fetching suggestions from Baidu: %s", url) +// return fetchSuggestionsFromURL(url) +// } + +// func fetchSogouSuggestions(query string) []string { +// encodedQuery := url.QueryEscape(query) +// url := fmt.Sprintf("https://w.sugg.sogou.com/sugg/ajaj_json.jsp?key=%s", encodedQuery) +// printDebug("Fetching suggestions from Sogou: %s", url) +// return fetchSuggestionsFromURL(url) +// } + func fetchSuggestionsFromURL(url string) []string { resp, err := http.Get(url) if err != nil { @@ -176,6 +201,9 @@ func fetchSuggestionsFromURL(url string) []string { return []string{} } + // Print the raw HTTP response for debugging + fmt.Printf("Raw response from %s:\n%s\n", url, string(body)) + // Log the Content-Type for debugging. contentType := resp.Header.Get("Content-Type") printDebug("Response Content-Type from %s: %s", url, contentType) @@ -225,3 +253,30 @@ func toJSONStringArray(strings []string) string { } return "[" + result + "]" } + +// func testSuggestionSources(query string) { +// for _, source := range suggestionSources { +// fmt.Printf("Testing %s...\n", source.Name) + +// // Fetch suggestions +// suggestions := source.FetchFunc(query) + +// // If we get results, print them +// if len(suggestions) > 0 { +// fmt.Printf("Suggestions from %s:\n", source.Name) +// for i, suggestion := range suggestions { +// fmt.Printf("%d: %s\n", i+1, suggestion) +// } +// } else { +// fmt.Printf("No suggestions from %s.\n", source.Name) +// } + +// // Small separator for clarity +// fmt.Println("--------------------------") +// } +// } + +// func main() { +// query := "test query" +// testSuggestionSources(query) +// } From afe420a0ed823b7f633b894ec135964f9cb68824 Mon Sep 17 00:00:00 2001 From: partisan Date: Wed, 25 Sep 2024 18:26:27 +0200 Subject: [PATCH 05/13] trim search suggestions if there is too many --- suggestions.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/suggestions.go b/suggestions.go index 88095fd..089ca8b 100644 --- a/suggestions.go +++ b/suggestions.go @@ -99,6 +99,9 @@ func handleSuggestions(w http.ResponseWriter, r *http.Request) { } } + // Trim the suggestions to a maximum of 8 items + suggestions = trimSuggestions(suggestions) + if len(suggestions) == 0 { printErr("All suggestion services failed. Returning empty response.") } @@ -108,6 +111,14 @@ func handleSuggestions(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, `["",%s]`, toJSONStringArray(suggestions)) } +// trimSuggestions trims the suggestion list to a maximum of 8 suggestions. +func trimSuggestions(suggestions []string) []string { + if len(suggestions) > 8 { + return suggestions[:8] + } + return suggestions +} + // updateLatency updates the latency of a suggestion source using an exponential moving average. func updateLatency(source *SuggestionSource, newLatency time.Duration) { source.mu.Lock() @@ -180,13 +191,6 @@ func fetchYahooSuggestions(query string) []string { // return fetchSuggestionsFromURL(url) // } -// func fetchSogouSuggestions(query string) []string { -// encodedQuery := url.QueryEscape(query) -// url := fmt.Sprintf("https://w.sugg.sogou.com/sugg/ajaj_json.jsp?key=%s", encodedQuery) -// printDebug("Fetching suggestions from Sogou: %s", url) -// return fetchSuggestionsFromURL(url) -// } - func fetchSuggestionsFromURL(url string) []string { resp, err := http.Get(url) if err != nil { From c094d9e6a1b40f571098718372315d5b495229aa Mon Sep 17 00:00:00 2001 From: partisan Date: Thu, 26 Sep 2024 19:54:41 +0200 Subject: [PATCH 06/13] fixed issue where search suggestions happened twice, causing weird behavior --- static/js/autocomplete.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/static/js/autocomplete.js b/static/js/autocomplete.js index 3e1cb58..3c7a2f5 100644 --- a/static/js/autocomplete.js +++ b/static/js/autocomplete.js @@ -52,6 +52,7 @@ async function getSuggestions(query) { let currentIndex = -1; // Keep track of the currently selected suggestion +// Handle click events on the type buttons let results = []; searchInput.addEventListener('input', async () => { let input = searchInput.value; @@ -128,15 +129,6 @@ function renderResults(results) { resultsWrapper.innerHTML = `
    ${content}
`; } -// Function to handle search input -searchInput.addEventListener('input', async () => { - let input = searchInput.value; - if (input.length) { - const results = await getSuggestions(input); - renderResults(results); - } -}); - // Handle click events on the type buttons const typeButtons = document.querySelectorAll('[name="t"]'); typeButtons.forEach(button => { From eabc0674294a50c3910f7c9a75657aed2b2604a4 Mon Sep 17 00:00:00 2001 From: partisan Date: Thu, 26 Sep 2024 21:18:52 +0200 Subject: [PATCH 07/13] experimenting with main page redesign --- static/css/style.css | 101 ++++++++++++++++++++++++++++++++++++-- static/js/autocomplete.js | 16 +++--- templates/search.html | 56 +++++++++++++++------ 3 files changed, 146 insertions(+), 27 deletions(-) diff --git a/static/css/style.css b/static/css/style.css index 782163e..c1ce038 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -67,13 +67,10 @@ /* Support for all WebKit browsers. */ -webkit-font-feature-settings: 'liga'; -webkit-font-smoothing: antialiased; - /* Support for Safari and Chrome. */ text-rendering: optimizeLegibility; - /* Support for Firefox. */ -moz-osx-font-smoothing: grayscale; - /* Support for IE. */ font-feature-settings: 'liga'; } @@ -549,8 +546,9 @@ hr { margin: 0 auto; background: var(--search-bg-input); border-radius: 22px; - position: absolute; - width: 520px; + position: relative; + width: 100%; + max-width: 600px; overflow: hidden; margin-left: auto; margin-right: auto; @@ -1891,6 +1889,99 @@ body, h1, p, a, input, button { color: var(--link) !important; } } + +/* Center the entire search page content */ +.search-page-content { + display: flex; + flex-direction: column; + align-items: center; +} + +/* Center the logo */ +.search-page-content h1 { + text-align: center; + margin-bottom: 20px; +} + +/* Style for the search input */ +#search-input { + width: 100%; + padding: 12px; + font-size: 16px; +} + +/* Style for the search button inside the search bar */ +#search-wrapper-ico { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + font-size: 24px; + background: none; + border: none; + cursor: pointer; +} + +/* Styles for the search type icons */ +.search-type-icons { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 30px; + margin-top: 30px; +} + +/* Style for each icon button */ +.icon-button { + display: flex; + flex-direction: column; + align-items: center; + background: none; + border: none; + cursor: pointer; + text-align: center; +} + +/* Style for the icons */ +.icon-button .material-icons-round { + font-size: 48px; /* Adjust the size of the icons */ + color: var(--sub-search-wrapper-ico); +} + +/* Style for the labels under icons */ +.icon-button p { + margin-top: 8px; + font-size: 14px; + color: var(--sub-search-wrapper-ico); +} + +/* Hover effects */ +.icon-button:hover .material-icons-round { + color: var(--blue); +} + +.icon-button:hover p { + color: var(--blue); +} + +/* Remove button default focus outline */ +.icon-button button:focus { + outline: none; +} + +.material-icons-round { + font-family: 'Material Icons Round'; + font-weight: normal; + font-style: normal; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; +} + /* :root { --background-color: #ffffff; diff --git a/static/js/autocomplete.js b/static/js/autocomplete.js index 3c7a2f5..354f254 100644 --- a/static/js/autocomplete.js +++ b/static/js/autocomplete.js @@ -170,11 +170,11 @@ document.addEventListener('click', (event) => { } }); -// Update visual feedback for selected type on page load -document.addEventListener("DOMContentLoaded", () => { - const activeButton = document.querySelector(`[name="t"][value="${selectedType}"]`); - if (activeButton) { - typeButtons.forEach(btn => btn.classList.remove('search-active')); - activeButton.classList.add('search-active'); - } -}); +// // Update visual feedback for selected type on page load +// document.addEventListener("DOMContentLoaded", () => { +// const activeButton = document.querySelector(`[name="t"][value="${selectedType}"]`); +// if (activeButton) { +// typeButtons.forEach(btn => btn.classList.remove('search-active')); +// activeButton.classList.add('search-active'); +// } +// }); diff --git a/templates/search.html b/templates/search.html index 61fbb0d..dde47de 100755 --- a/templates/search.html +++ b/templates/search.html @@ -83,21 +83,49 @@
-

Ocásek

-
- - -
-
    -
+
+

Ocásek

+
+ + +
+
    +
    +
    +
    + + +
    + +

    Web

    +
    + +
    + +

    Images

    +
    + +
    + +

    Videos

    +
    + +
    + +

    Forums

    +
    + +
    + +

    Maps

    +
    + +
    + +

    Files

    +
    - -
    -
    - - -
    - + \ No newline at end of file From bfaf4c11538e63f5cb110b1e7e2daa4c5577b9bc Mon Sep 17 00:00:00 2001 From: partisan Date: Thu, 26 Sep 2024 22:00:27 +0200 Subject: [PATCH 08/13] mobile button fix --- static/css/style.css | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/static/css/style.css b/static/css/style.css index c1ce038..9fc2778 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1862,12 +1862,32 @@ body, h1, p, a, input, button { display: none; } + .icon-button { + margin-top: 30px; + } + + .icon-button button { + margin-top: 30px; + } + + .icon-button p { + margin-top: 30px; + } + #clearSearch { top: 6px; } } + /* This is really bad */ +@media only screen and (max-width: 400px) { + + .icon-button { + padding: 5% + } +} + /* Ensuring dark theme compliance */ @media (prefers-color-scheme: dark) { .leaflet-control-locate, From d780e8210ba433bbe23aad6d83f520f631a6d5d0 Mon Sep 17 00:00:00 2001 From: partisan Date: Fri, 27 Sep 2024 12:13:16 +0200 Subject: [PATCH 09/13] I hate .css --- static/css/style-search.css | 61 +++++++++++++++++++++ static/css/style.css | 106 +++++------------------------------- templates/search.html | 3 +- 3 files changed, 77 insertions(+), 93 deletions(-) create mode 100644 static/css/style-search.css diff --git a/static/css/style-search.css b/static/css/style-search.css new file mode 100644 index 0000000..adda3d2 --- /dev/null +++ b/static/css/style-search.css @@ -0,0 +1,61 @@ + + +.search-page-content { + display: flex; + flex-direction: column; + align-items: center; +} + +.search-page-content h1 { + text-align: center; + margin-bottom: 20px; +} + +#search-input { + width: 100%; + padding: 12px; + font-size: 16px; +} + +.search-type-icons { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 30px; + margin-top: 30px; +} + +.icon-button { + display: flex; + flex-direction: column; + align-items: center; + background: none; + border: none; + cursor: pointer; + text-align: center; +} + +.icon-button .material-icons-round { + font-size: 48px; + color: var(--sub-search-wrapper-ico); +} + +.icon-button p { + margin-top: 8px; + font-size: 14px; + color: var(--sub-search-wrapper-ico); +} + +.icon-button:hover .material-icons-round { + transition: all .3s ease; + color: var(--blue); +} + +.icon-button:hover p { + transition: all .3s ease; + color: var(--blue); +} + +.icon-button button:focus { + outline: none; +} \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css index 9fc2778..af1b700 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1538,6 +1538,20 @@ body, h1, p, a, input, button { } } +/* +.material-icons-round { + font-family: 'Material Icons Round'; + font-weight: normal; + font-style: normal; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; +} */ + @media only screen and (max-width: 1220px) { .snip { @@ -1910,98 +1924,6 @@ body, h1, p, a, input, button { } } -/* Center the entire search page content */ -.search-page-content { - display: flex; - flex-direction: column; - align-items: center; -} - -/* Center the logo */ -.search-page-content h1 { - text-align: center; - margin-bottom: 20px; -} - -/* Style for the search input */ -#search-input { - width: 100%; - padding: 12px; - font-size: 16px; -} - -/* Style for the search button inside the search bar */ -#search-wrapper-ico { - position: absolute; - right: 10px; - top: 50%; - transform: translateY(-50%); - font-size: 24px; - background: none; - border: none; - cursor: pointer; -} - -/* Styles for the search type icons */ -.search-type-icons { - display: flex; - justify-content: center; - flex-wrap: wrap; - gap: 30px; - margin-top: 30px; -} - -/* Style for each icon button */ -.icon-button { - display: flex; - flex-direction: column; - align-items: center; - background: none; - border: none; - cursor: pointer; - text-align: center; -} - -/* Style for the icons */ -.icon-button .material-icons-round { - font-size: 48px; /* Adjust the size of the icons */ - color: var(--sub-search-wrapper-ico); -} - -/* Style for the labels under icons */ -.icon-button p { - margin-top: 8px; - font-size: 14px; - color: var(--sub-search-wrapper-ico); -} - -/* Hover effects */ -.icon-button:hover .material-icons-round { - color: var(--blue); -} - -.icon-button:hover p { - color: var(--blue); -} - -/* Remove button default focus outline */ -.icon-button button:focus { - outline: none; -} - -.material-icons-round { - font-family: 'Material Icons Round'; - font-weight: normal; - font-style: normal; - line-height: 1; - letter-spacing: normal; - text-transform: none; - display: inline-block; - white-space: nowrap; - word-wrap: normal; - direction: ltr; -} - /* :root { --background-color: #ffffff; diff --git a/templates/search.html b/templates/search.html index dde47de..fd68aa2 100755 --- a/templates/search.html +++ b/templates/search.html @@ -8,6 +8,7 @@ {{ end }} Search with Ocásek + @@ -122,7 +123,7 @@
    -

    Files

    +

    Torrents

    From 9f71666df3fe17abcf9ccaee46ebe2fb29b87394 Mon Sep 17 00:00:00 2001 From: partisan Date: Fri, 27 Sep 2024 13:16:36 +0200 Subject: [PATCH 10/13] fixed bug for irrelevant results (caused by showing only results in the user's language) --- files.go | 6 +-- forums.go | 4 +- images.go | 6 +-- main.go | 59 +++++++++++++-------------- text-google.go | 4 ++ text.go | 10 ++--- user-settings.go | 101 ++++++++++++++++++++++++++++------------------- video.go | 6 +-- 8 files changed, 110 insertions(+), 86 deletions(-) diff --git a/files.go b/files.go index 931daaa..fb849cd 100755 --- a/files.go +++ b/files.go @@ -41,8 +41,8 @@ func initializeTorrentSites() { func handleFileSearch(w http.ResponseWriter, settings UserSettings, query string, page int) { startTime := time.Now() - cacheKey := CacheKey{Query: query, Page: page, Safe: settings.SafeSearch == "active", Lang: settings.Language, Type: "file"} - combinedResults := getFileResultsFromCacheOrFetch(cacheKey, query, settings.SafeSearch, settings.Language, page) + cacheKey := CacheKey{Query: query, Page: page, Safe: settings.SafeSearch == "active", Lang: settings.SearchLanguage, Type: "file"} + combinedResults := getFileResultsFromCacheOrFetch(cacheKey, query, settings.SafeSearch, settings.SearchLanguage, page) sort.Slice(combinedResults, func(i, j int) bool { return combinedResults[i].Seeders > combinedResults[j].Seeders }) @@ -82,7 +82,7 @@ func handleFileSearch(w http.ResponseWriter, settings UserSettings, query string HasPrevPage: page > 1, HasNextPage: len(combinedResults) > 0, LanguageOptions: languageOptions, - CurrentLang: settings.Language, + CurrentLang: settings.SearchLanguage, Theme: settings.Theme, Safe: settings.SafeSearch, IsThemeDark: settings.IsThemeDark, diff --git a/forums.go b/forums.go index 4c15490..e9fa982 100755 --- a/forums.go +++ b/forums.go @@ -102,7 +102,7 @@ func handleForumsSearch(w http.ResponseWriter, settings UserSettings, query stri results, err := PerformRedditSearch(query, settings.SafeSearch, page) if err != nil || len(results) == 0 { // 0 == 0 to force search by other node log.Printf("No results from primary search, trying other nodes") - results = tryOtherNodesForForumSearch(query, settings.SafeSearch, settings.Language, page) + results = tryOtherNodesForForumSearch(query, settings.SafeSearch, settings.SearchLanguage, page) } data := struct { @@ -123,7 +123,7 @@ func handleForumsSearch(w http.ResponseWriter, settings UserSettings, query stri HasPrevPage: page > 1, HasNextPage: len(results) == 25, LanguageOptions: languageOptions, - CurrentLang: settings.Language, + CurrentLang: settings.SearchLanguage, Theme: settings.Theme, Safe: settings.SafeSearch, IsThemeDark: settings.IsThemeDark, diff --git a/images.go b/images.go index 5a202c7..7bd55a4 100755 --- a/images.go +++ b/images.go @@ -22,8 +22,8 @@ func init() { func handleImageSearch(w http.ResponseWriter, settings UserSettings, query string, page int) { startTime := time.Now() - cacheKey := CacheKey{Query: query, Page: page, Safe: settings.SafeSearch == "active", Lang: settings.Language, Type: "image"} - combinedResults := getImageResultsFromCacheOrFetch(cacheKey, query, settings.SafeSearch, settings.Language, page) + cacheKey := CacheKey{Query: query, Page: page, Safe: settings.SafeSearch == "active", Lang: settings.SearchLanguage, Type: "image"} + combinedResults := getImageResultsFromCacheOrFetch(cacheKey, query, settings.SafeSearch, settings.SearchLanguage, page) elapsedTime := time.Since(startTime) tmpl, err := template.New("images.html").Funcs(funcs).ParseFiles("templates/images.html") @@ -55,7 +55,7 @@ func handleImageSearch(w http.ResponseWriter, settings UserSettings, query strin HasNextPage: len(combinedResults) >= 50, NoResults: len(combinedResults) == 0, LanguageOptions: languageOptions, - CurrentLang: settings.Language, + CurrentLang: settings.SearchLanguage, Theme: settings.Theme, Safe: settings.SafeSearch, IsThemeDark: settings.IsThemeDark, diff --git a/main.go b/main.go index 930b583..2a692fb 100755 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ type LanguageOption struct { var settings UserSettings var languageOptions = []LanguageOption{ - {Code: "", Name: "Auto-detect"}, + {Code: "", Name: "Any"}, {Code: "en", Name: "English"}, {Code: "af", Name: "Afrikaans"}, {Code: "ar", Name: "العربية (Arabic)"}, @@ -69,35 +69,37 @@ var languageOptions = []LanguageOption{ func handleSearch(w http.ResponseWriter, r *http.Request) { query, safe, lang, searchType, page := parseSearchParams(r) - // Load user settings settings = loadUserSettings(w, r) - // Update theme if provided, or use existing settings theme := r.URL.Query().Get("theme") if theme != "" { settings.Theme = theme - saveUserSettings(w, settings) // Save if theme is updated + saveUserSettings(w, settings) } else if settings.Theme == "" { - settings.Theme = "dark" // Default theme + settings.Theme = "dark" } - // Update safe search if provided, or use existing settings if safe != "" && safe != settings.SafeSearch { settings.SafeSearch = safe - saveUserSettings(w, settings) // Save if safe search is updated + saveUserSettings(w, settings) } - // Update language if provided, or use existing settings - if lang != "" && lang != settings.Language { - settings.Language = lang - saveUserSettings(w, settings) // Save if language is updated - } else if settings.Language == "" { - // If no language set, auto-detect from browser or default to "en" - settings.Language = normalizeLangCode(r.Header.Get("Accept-Language")) - saveUserSettings(w, settings) // Save if language is auto-detected + // Update site language if provided, or use existing settings + if lang != "" && lang != settings.SiteLanguage { + settings.SiteLanguage = lang + saveUserSettings(w, settings) + } else if settings.SiteLanguage == "" { + settings.SiteLanguage = normalizeLangCode(r.Header.Get("Accept-Language")) + saveUserSettings(w, settings) + } + + // Update search language (can be empty) + searchLang := r.URL.Query().Get("search_lang") + if searchLang != settings.SearchLanguage { + settings.SearchLanguage = searchLang + saveUserSettings(w, settings) } - // This will do for now (to handle Dark Reader addon) switch settings.Theme { case "dark", "black", "night", "latte": settings.IsThemeDark = true @@ -105,21 +107,21 @@ func handleSearch(w http.ResponseWriter, r *http.Request) { settings.IsThemeDark = false } - // Check if there is a search query if query == "" { - // If no query is provided, render the search page template data := struct { - LanguageOptions []LanguageOption - CurrentLang string - Theme string - Safe string - IsThemeDark bool + LanguageOptions []LanguageOption + CurrentLang string + CurrentSearchLang string + Theme string + Safe string + IsThemeDark bool }{ - LanguageOptions: languageOptions, - CurrentLang: settings.Language, - Theme: settings.Theme, - Safe: settings.SafeSearch, - IsThemeDark: settings.IsThemeDark, + LanguageOptions: languageOptions, + CurrentLang: settings.SiteLanguage, + CurrentSearchLang: settings.SearchLanguage, + Theme: settings.Theme, + Safe: settings.SafeSearch, + IsThemeDark: settings.IsThemeDark, } tmpl := template.Must(template.ParseFiles("templates/search.html")) @@ -127,7 +129,6 @@ func handleSearch(w http.ResponseWriter, r *http.Request) { return } - // Handle search based on the type switch searchType { case "image": handleImageSearch(w, settings, query, page) diff --git a/text-google.go b/text-google.go index 006ebc0..a147743 100644 --- a/text-google.go +++ b/text-google.go @@ -79,6 +79,10 @@ func buildSearchURL(query, safe, lang string, page, resultsPerPage int) string { } startIndex := (page - 1) * resultsPerPage + + printDebug(fmt.Sprintf("https://www.google.com/search?q=%s%s%s%s%s&start=%d", + url.QueryEscape(query), safeParam, langParam, glParam, uuleParam, startIndex)) + return fmt.Sprintf("https://www.google.com/search?q=%s%s%s%s%s&start=%d", url.QueryEscape(query), safeParam, langParam, glParam, uuleParam, startIndex) } diff --git a/text.go b/text.go index 7ea602d..c77bd62 100755 --- a/text.go +++ b/text.go @@ -22,17 +22,17 @@ func init() { func HandleTextSearch(w http.ResponseWriter, settings UserSettings, query string, page int) { startTime := time.Now() - cacheKey := CacheKey{Query: query, Page: page, Safe: settings.SafeSearch == "active", Lang: settings.Language, Type: "text"} - combinedResults := getTextResultsFromCacheOrFetch(cacheKey, query, settings.SafeSearch, settings.Language, page) + cacheKey := CacheKey{Query: query, Page: page, Safe: settings.SafeSearch == "active", Lang: settings.SearchLanguage, Type: "text"} + combinedResults := getTextResultsFromCacheOrFetch(cacheKey, query, settings.SafeSearch, settings.SearchLanguage, page) hasPrevPage := page > 1 // dupe //displayResults(w, combinedResults, query, lang, time.Since(startTime).Seconds(), page, hasPrevPage, hasNextPage) // Prefetch next and previous pages - go prefetchPage(query, settings.SafeSearch, settings.Language, page+1) + go prefetchPage(query, settings.SafeSearch, settings.SearchLanguage, page+1) if hasPrevPage { - go prefetchPage(query, settings.SafeSearch, settings.Language, page-1) + go prefetchPage(query, settings.SafeSearch, settings.SearchLanguage, page-1) } elapsedTime := time.Since(startTime) @@ -65,7 +65,7 @@ func HandleTextSearch(w http.ResponseWriter, settings UserSettings, query string HasNextPage: len(combinedResults) >= 50, NoResults: len(combinedResults) == 0, LanguageOptions: languageOptions, - CurrentLang: settings.Language, + CurrentLang: settings.SearchLanguage, Theme: settings.Theme, Safe: settings.SafeSearch, IsThemeDark: settings.IsThemeDark, diff --git a/user-settings.go b/user-settings.go index bd2f02f..6143554 100755 --- a/user-settings.go +++ b/user-settings.go @@ -8,52 +8,52 @@ import ( ) type UserSettings struct { - Theme string - Language string - SafeSearch string - IsThemeDark bool + Theme string + SiteLanguage string + SearchLanguage string + SafeSearch string + IsThemeDark bool } func loadUserSettings(w http.ResponseWriter, r *http.Request) UserSettings { var settings UserSettings - saveRequired := false // Track if we need to save settings back + saveRequired := false // Load theme if cookie, err := r.Cookie("theme"); err == nil { settings.Theme = cookie.Value } else { - settings.Theme = "dark" // Default theme - saveRequired = true // No cookie found, need to save this later + settings.Theme = "dark" + saveRequired = true } - // Load language - if cookie, err := r.Cookie("language"); err == nil { - settings.Language = cookie.Value + // Load site language + if cookie, err := r.Cookie("site_language"); err == nil { + settings.SiteLanguage = cookie.Value } else { - settings.Language = "" // Set language to empty, handled later - } - - // If language is empty, get it from the Accept-Language header - if settings.Language == "" { + // If no site language is set, use Accept-Language or default to "en" acceptLang := r.Header.Get("Accept-Language") if acceptLang != "" { - // Get the first language from Accept-Language header and normalize - settings.Language = normalizeLangCode(strings.Split(acceptLang, ",")[0]) + settings.SiteLanguage = normalizeLangCode(strings.Split(acceptLang, ",")[0]) } else { - settings.Language = "en" // Default language if Accept-Language is not present + settings.SiteLanguage = "en" // Default language } - saveRequired = true // No language cookie found, need to save + saveRequired = true + } + + // Load search language (can be empty) + if cookie, err := r.Cookie("search_language"); err == nil { + settings.SearchLanguage = cookie.Value } // Load safe search if cookie, err := r.Cookie("safe"); err == nil { settings.SafeSearch = cookie.Value } else { - settings.SafeSearch = "" // Default safe search off - saveRequired = true // No safe search cookie found, need to save + settings.SafeSearch = "" + saveRequired = true } - // Save settings if required (no cookie found for any of the settings) if saveRequired { saveUserSettings(w, settings) } @@ -62,19 +62,27 @@ func loadUserSettings(w http.ResponseWriter, r *http.Request) UserSettings { } func saveUserSettings(w http.ResponseWriter, settings UserSettings) { - expiration := time.Now().Add(90 * 24 * time.Hour) // 90 days from now + expiration := time.Now().Add(90 * 24 * time.Hour) http.SetCookie(w, &http.Cookie{ Name: "theme", Value: settings.Theme, Path: "/", - Expires: expiration, // Expiration time needs to be set otherwise it will expire immediately - Secure: true, // Ensure cookie is sent over HTTPS only + Expires: expiration, + Secure: true, SameSite: http.SameSiteStrictMode, }) http.SetCookie(w, &http.Cookie{ - Name: "language", - Value: settings.Language, + Name: "site_language", + Value: settings.SiteLanguage, + Path: "/", + Expires: expiration, + Secure: true, + SameSite: http.SameSiteStrictMode, + }) + http.SetCookie(w, &http.Cookie{ + Name: "search_language", + Value: settings.SearchLanguage, Path: "/", Expires: expiration, Secure: true, @@ -101,15 +109,24 @@ func handleSaveSettings(w http.ResponseWriter, r *http.Request) { if theme := r.FormValue("theme"); theme != "" { settings.Theme = theme } - if lang := r.FormValue("lang"); lang != "" { - settings.Language = lang + + // Update site language if provided + if siteLang := r.FormValue("site_lang"); siteLang != "" { + settings.SiteLanguage = siteLang } else { - // If lang is empty, try to get from Accept-Language header + // If site_lang is empty, try to get from Accept-Language header acceptLang := r.Header.Get("Accept-Language") if acceptLang != "" { - settings.Language = strings.Split(acceptLang, ",")[0] + settings.SiteLanguage = strings.Split(acceptLang, ",")[0] } } + + // Update search language if provided + if searchLang := r.FormValue("search_lang"); searchLang != "" { + settings.SearchLanguage = searchLang + } + + // Update safe search if provided if safe := r.FormValue("safe"); safe != "" { settings.SafeSearch = safe } @@ -127,17 +144,19 @@ func handleSettings(w http.ResponseWriter, r *http.Request) { settings = loadUserSettings(w, r) data := struct { - LanguageOptions []LanguageOption - CurrentLang string - Theme string - Safe string - IsThemeDark bool + LanguageOptions []LanguageOption + CurrentSiteLang string + CurrentSearchLang string + Theme string + Safe string + IsThemeDark bool }{ - LanguageOptions: languageOptions, - CurrentLang: settings.Language, - Theme: settings.Theme, - Safe: settings.SafeSearch, - IsThemeDark: settings.IsThemeDark, + LanguageOptions: languageOptions, + CurrentSiteLang: settings.SiteLanguage, + CurrentSearchLang: settings.SearchLanguage, + Theme: settings.Theme, + Safe: settings.SafeSearch, + IsThemeDark: settings.IsThemeDark, } printDebug("Rendering settings with data: %+v", data) diff --git a/video.go b/video.go index 15c83a3..9c7dfa3 100644 --- a/video.go +++ b/video.go @@ -151,10 +151,10 @@ func makeHTMLRequest(query, safe, lang string, page int) (*VideoAPIResponse, err func handleVideoSearch(w http.ResponseWriter, settings UserSettings, query string, page int) { start := time.Now() - results := fetchVideoResults(query, settings.SafeSearch, settings.Language, page) + results := fetchVideoResults(query, settings.SafeSearch, settings.SearchLanguage, page) if len(results) == 0 { printWarn("No results from primary search, trying other nodes") - results = tryOtherNodesForVideoSearch(query, settings.SafeSearch, settings.Language, page, []string{hostID}) + results = tryOtherNodesForVideoSearch(query, settings.SafeSearch, settings.SearchLanguage, page, []string{hostID}) } elapsed := time.Since(start) @@ -173,7 +173,7 @@ func handleVideoSearch(w http.ResponseWriter, settings UserSettings, query strin "HasPrevPage": page > 1, "HasNextPage": len(results) > 0, "LanguageOptions": languageOptions, - "CurrentLang": settings.Language, + "CurrentLang": settings.SearchLanguage, "Theme": settings.Theme, "Safe": settings.SafeSearch, "IsThemeDark": settings.IsThemeDark, From 3c37bbec87d88d08afbdaf45762ad3babe4f0691 Mon Sep 17 00:00:00 2001 From: partisan Date: Fri, 27 Sep 2024 17:27:16 +0200 Subject: [PATCH 11/13] removed text "Type to search..." from search bar --- templates/files.html | 2 +- templates/forums.html | 2 +- templates/images.html | 2 +- templates/map.html | 2 +- templates/search.html | 16 ++++++++++++---- templates/settings.html | 19 ++++++++++++++----- templates/text.html | 2 +- templates/videos.html | 2 +- 8 files changed, 32 insertions(+), 15 deletions(-) diff --git a/templates/files.html b/templates/files.html index e119424..4b1b246 100755 --- a/templates/files.html +++ b/templates/files.html @@ -15,7 +15,7 @@

    Ocásek

    - +
      diff --git a/templates/forums.html b/templates/forums.html index e833d4f..3fbdea7 100755 --- a/templates/forums.html +++ b/templates/forums.html @@ -15,7 +15,7 @@

      Ocásek

      - +
        diff --git a/templates/images.html b/templates/images.html index ccf08ae..8f82a2b 100755 --- a/templates/images.html +++ b/templates/images.html @@ -15,7 +15,7 @@

        Ocásek

        - +
          diff --git a/templates/map.html b/templates/map.html index 5ce8c62..d0d9fa3 100644 --- a/templates/map.html +++ b/templates/map.html @@ -30,7 +30,7 @@

          Ocásek

          - +
            diff --git a/templates/search.html b/templates/search.html index fd68aa2..0c320ad 100755 --- a/templates/search.html +++ b/templates/search.html @@ -53,9 +53,17 @@ }); // Event listener for Language Selection - document.getElementById('languageSelect').addEventListener('change', function () { - updateSettings('lang', this.value); - }); + if (siteLanguageSelect) { + siteLanguageSelect.addEventListener('change', function () { + updateSettings('site_lang', this.value); + }); + } + + // if (searchLanguageSelect) { + // searchLanguageSelect.addEventListener('change', function () { + // updateSettings('search_lang', this.value); + // }); + // } });
          - +

          Safe Search

          +

          Site Language

          + +
          + +
          +

          Search Language

          +
          diff --git a/templates/text.html b/templates/text.html index 2be2d2b..3ce3d13 100755 --- a/templates/text.html +++ b/templates/text.html @@ -15,7 +15,7 @@

          Ocásek

          - +
            diff --git a/templates/videos.html b/templates/videos.html index 3be0bc0..cca9407 100644 --- a/templates/videos.html +++ b/templates/videos.html @@ -15,7 +15,7 @@

            Ocásek

            - +
              From 83a7726422b3eb6b61268bc4296a02b9be7f368a Mon Sep 17 00:00:00 2001 From: partisan Date: Sat, 28 Sep 2024 20:11:17 +0200 Subject: [PATCH 12/13] aligned logo on results page and made various visual fixes --- main.go | 2 +- static/css/style.css | 2 +- templates/settings.html | 2 +- user-settings.go | 3 +++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 2a692fb..e3b102e 100755 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ type LanguageOption struct { var settings UserSettings var languageOptions = []LanguageOption{ - {Code: "", Name: "Any"}, + {Code: "", Name: "Any Language"}, {Code: "en", Name: "English"}, {Code: "af", Name: "Afrikaans"}, {Code: "ar", Name: "العربية (Arabic)"}, diff --git a/static/css/style.css b/static/css/style.css index af1b700..22f2f58 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1156,7 +1156,7 @@ p { position: absolute; margin-top: 0px; top: 20px; - left: 38px; + left: 28px; } .sub-search-button-wrapper button { diff --git a/templates/settings.html b/templates/settings.html index 4606a53..67a7cde 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -92,7 +92,7 @@
            -

            |

            +

            diff --git a/user-settings.go b/user-settings.go index 6143554..71685d6 100755 --- a/user-settings.go +++ b/user-settings.go @@ -27,6 +27,9 @@ func loadUserSettings(w http.ResponseWriter, r *http.Request) UserSettings { saveRequired = true } + // Determine if the selected theme is dark + settings.IsThemeDark = settings.Theme == "dark" || settings.Theme == "night" || settings.Theme == "black" + // Load site language if cookie, err := r.Cookie("site_language"); err == nil { settings.SiteLanguage = cookie.Value From 18d4f7c6db5a078032b4cf53f68c1e5db9881e51 Mon Sep 17 00:00:00 2001 From: partisan Date: Sun, 29 Sep 2024 08:33:49 +0200 Subject: [PATCH 13/13] settings page visual improvements --- static/css/style-settings.css | 46 ++++++++++++++++++++++++++++++++--- static/css/style.css | 21 +++++++++++++--- templates/search.html | 2 +- templates/settings.html | 2 +- user-settings.go | 2 +- 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/static/css/style-settings.css b/static/css/style-settings.css index 0dda333..81a40b6 100644 --- a/static/css/style-settings.css +++ b/static/css/style-settings.css @@ -53,10 +53,48 @@ gap: 10px; } -@media (max-width: 600px) { - .theme-link { - width: 100%; - } +#searchLanguageSelect, +#safeSearchSelect, +#siteLanguageSelect { + border-radius: 4px; + padding: 6px; + font-size: 15px; + border: 1px solid var(--border); + color: var(--font-fg); + width: 160px; + background: var(--button); + float: right; + cursor: pointer; + transition: all 0.3s ease; + text-align: center; + box-sizing: border-box; /* Ensures consistent width with padding */ +} + +#searchLanguageSelect:hover, +#safeSearchSelect:hover, +#siteLanguageSelect:hover { + border: 1px solid #5f6368; + /* background-color: var(--button-hover); */ +} + +.save.save-settings-page { + padding: 6px; + width: 160px; + height: 40px; +} + +/* Ensure correct aligment */ +.settings-row { + display: flex; + align-items: center; + justify-content: space-between; +} + +.settings-row select, +.settings-row button { + width: 160px; + height: 40px; + box-sizing: border-box; } /* --- */ diff --git a/static/css/style.css b/static/css/style.css index 22f2f58..e7a394c 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -652,7 +652,7 @@ hr { .settings-nav { max-width: 100%; - height: 40px; + height: 50px; background-color: var(--search-bg); border-bottom: 1px solid var(--border); padding: 10px; @@ -672,6 +672,7 @@ hr { .settings-row { display: flex; align-items: center; + justify-content: flex-end; padding: 2px; padding-left: 10px; padding-right: 10px; @@ -680,7 +681,9 @@ hr { .settings-row select, .settings-row button { - margin-left: auto; + width: 160px; + height: 40px; + margin: 0; } .kno_wiki { @@ -771,6 +774,8 @@ form.torrent-sort { #settingsButton { transition: all .3s ease; + /* width: 283px+6px; + height: 31px; */ } .settings-icon-link { @@ -822,6 +827,12 @@ form.torrent-sort { cursor: pointer; } +.search-menu select:hover { + border: 1px solid #5f6368; + cursor: pointer; + transition: all .3s ease; +} + .settings-content { display: flex; flex-direction: column; @@ -865,7 +876,7 @@ form.torrent-sort { .theme-settings { margin-top: 10px; - width: 90%; + width: 100%-4px; border: 1px solid var(--snip-border); background: var(--snip-background); color: var(--fg); @@ -879,6 +890,10 @@ form.torrent-sort { margin-left: 3%; } +.theme-mini-settings { + width: 90%; +} + .settings-search-div:hover p { color: #8ab4f8; } diff --git a/templates/search.html b/templates/search.html index 0c320ad..4f75c28 100755 --- a/templates/search.html +++ b/templates/search.html @@ -73,7 +73,7 @@

            Settings

            -
            +

            Current theme: {{.Theme}}

            Dark Theme
            diff --git a/templates/settings.html b/templates/settings.html index 67a7cde..39e82af 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -91,7 +91,7 @@
            -
            +

            diff --git a/user-settings.go b/user-settings.go index 71685d6..b9f5dd5 100755 --- a/user-settings.go +++ b/user-settings.go @@ -28,7 +28,7 @@ func loadUserSettings(w http.ResponseWriter, r *http.Request) UserSettings { } // Determine if the selected theme is dark - settings.IsThemeDark = settings.Theme == "dark" || settings.Theme == "night" || settings.Theme == "black" + settings.IsThemeDark = settings.Theme == "dark" || settings.Theme == "night" || settings.Theme == "black" || settings.Theme == "latte" // Load site language if cookie, err := r.Cookie("site_language"); err == nil {