updated logging, added theming, added settings button
This commit is contained in:
parent
51b2ef43bc
commit
ca15fb7ec5
25 changed files with 384 additions and 62 deletions
5
cache.go
5
cache.go
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -143,7 +142,7 @@ func (rc *ResultsCache) checkAndCleanCache() {
|
||||||
func (rc *ResultsCache) memoryUsage() float64 {
|
func (rc *ResultsCache) memoryUsage() float64 {
|
||||||
v, err := mem.VirtualMemory()
|
v, err := mem.VirtualMemory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get memory info: %v", err)
|
printErr("Failed to get memory info: %v", err)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +166,7 @@ func (rc *ResultsCache) cleanOldestItems() {
|
||||||
|
|
||||||
if oldestKey != "" {
|
if oldestKey != "" {
|
||||||
delete(rc.results, oldestKey)
|
delete(rc.results, oldestKey)
|
||||||
log.Printf("Removed oldest cache item: %s", oldestKey)
|
printDebug("Removed oldest cache item: %s", oldestKey)
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
6
files.go
6
files.go
|
@ -39,7 +39,7 @@ func initializeTorrentSites() {
|
||||||
// rutor = NewRutor()
|
// rutor = NewRutor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleFileSearch(w http.ResponseWriter, query, safe, lang string, page int) {
|
func handleFileSearch(w http.ResponseWriter, settings UserSettings, query, safe, lang string, page int) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "file"}
|
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "file"}
|
||||||
|
@ -69,6 +69,7 @@ func handleFileSearch(w http.ResponseWriter, query, safe, lang string, page int)
|
||||||
HasNextPage bool
|
HasNextPage bool
|
||||||
Page int
|
Page int
|
||||||
Settings Settings
|
Settings Settings
|
||||||
|
Theme string
|
||||||
}{
|
}{
|
||||||
Results: combinedResults,
|
Results: combinedResults,
|
||||||
Query: query,
|
Query: query,
|
||||||
|
@ -78,7 +79,8 @@ func handleFileSearch(w http.ResponseWriter, query, safe, lang string, page int)
|
||||||
HasPrevPage: page > 1,
|
HasPrevPage: page > 1,
|
||||||
HasNextPage: len(combinedResults) > 0,
|
HasNextPage: len(combinedResults) > 0,
|
||||||
Page: page,
|
Page: page,
|
||||||
Settings: Settings{UxLang: lang, Safe: safe},
|
Settings: Settings{UxLang: lang, Safe: safe}, // Now this is painful, are there two Settings variables??
|
||||||
|
Theme: settings.Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Debugging: Print results before rendering template
|
// // Debugging: Print results before rendering template
|
||||||
|
|
|
@ -98,7 +98,7 @@ func PerformRedditSearch(query string, safe string, page int) ([]ForumSearchResu
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleForumsSearch(w http.ResponseWriter, query, safe, lang string, page int) {
|
func handleForumsSearch(w http.ResponseWriter, settings UserSettings, query, safe, lang string, page int) {
|
||||||
results, err := PerformRedditSearch(query, safe, page)
|
results, err := PerformRedditSearch(query, safe, page)
|
||||||
if err != nil || len(results) == 0 { // 0 == 0 to force search by other node
|
if err != nil || len(results) == 0 { // 0 == 0 to force search by other node
|
||||||
log.Printf("No results from primary search, trying other nodes")
|
log.Printf("No results from primary search, trying other nodes")
|
||||||
|
@ -113,6 +113,7 @@ func handleForumsSearch(w http.ResponseWriter, query, safe, lang string, page in
|
||||||
Page int
|
Page int
|
||||||
HasPrevPage bool
|
HasPrevPage bool
|
||||||
HasNextPage bool
|
HasNextPage bool
|
||||||
|
Theme string
|
||||||
}{
|
}{
|
||||||
Query: query,
|
Query: query,
|
||||||
Results: results,
|
Results: results,
|
||||||
|
@ -121,6 +122,7 @@ func handleForumsSearch(w http.ResponseWriter, query, safe, lang string, page in
|
||||||
Page: page,
|
Page: page,
|
||||||
HasPrevPage: page > 1,
|
HasPrevPage: page > 1,
|
||||||
HasNextPage: len(results) == 25,
|
HasNextPage: len(results) == 25,
|
||||||
|
Theme: settings.Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
funcMap := template.FuncMap{
|
funcMap := template.FuncMap{
|
||||||
|
|
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +16,7 @@ func handleImageProxy(w http.ResponseWriter, r *http.Request) {
|
||||||
// Fetch the image from the external URL
|
// Fetch the image from the external URL
|
||||||
resp, err := http.Get(imageURL)
|
resp, err := http.Get(imageURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error fetching image: %v", err)
|
printWarn("Error fetching image: %v", err)
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -40,7 +39,7 @@ func handleImageProxy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Write the image content to the response
|
// Write the image content to the response
|
||||||
if _, err := io.Copy(w, resp.Body); err != nil {
|
if _, err := io.Copy(w, resp.Body); err != nil {
|
||||||
log.Printf("Error writing image to response: %v", err)
|
printWarn("Error writing image to response: %v", err)
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleImageSearch(w http.ResponseWriter, query, safe, lang string, page int) {
|
func handleImageSearch(w http.ResponseWriter, settings UserSettings, query, safe, lang string, page int) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "image"}
|
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "image"}
|
||||||
|
@ -41,6 +41,7 @@ func handleImageSearch(w http.ResponseWriter, query, safe, lang string, page int
|
||||||
HasPrevPage bool
|
HasPrevPage bool
|
||||||
HasNextPage bool
|
HasNextPage bool
|
||||||
NoResults bool
|
NoResults bool
|
||||||
|
Theme string
|
||||||
}{
|
}{
|
||||||
Results: combinedResults,
|
Results: combinedResults,
|
||||||
Query: query,
|
Query: query,
|
||||||
|
@ -51,6 +52,7 @@ func handleImageSearch(w http.ResponseWriter, query, safe, lang string, page int
|
||||||
HasPrevPage: page > 1,
|
HasPrevPage: page > 1,
|
||||||
HasNextPage: len(combinedResults) >= 50,
|
HasNextPage: len(combinedResults) >= 50,
|
||||||
NoResults: len(combinedResults) == 0,
|
NoResults: len(combinedResults) == 0,
|
||||||
|
Theme: settings.Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tmpl.Execute(w, data)
|
err = tmpl.Execute(w, data)
|
||||||
|
|
47
main.go
47
main.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -13,6 +14,8 @@ type LanguageOption struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var settings UserSettings
|
||||||
|
|
||||||
var languageOptions = []LanguageOption{
|
var languageOptions = []LanguageOption{
|
||||||
{Code: "", Name: "Any Language"},
|
{Code: "", Name: "Any Language"},
|
||||||
{Code: "lang_en", Name: "English"},
|
{Code: "lang_en", Name: "English"},
|
||||||
|
@ -66,27 +69,55 @@ var languageOptions = []LanguageOption{
|
||||||
func handleSearch(w http.ResponseWriter, r *http.Request) {
|
func handleSearch(w http.ResponseWriter, r *http.Request) {
|
||||||
query, safe, lang, searchType, page := parseSearchParams(r)
|
query, safe, lang, searchType, page := parseSearchParams(r)
|
||||||
|
|
||||||
|
// Load user settings
|
||||||
|
settings = loadUserSettings(r)
|
||||||
|
|
||||||
|
// Update the theme, safe search, and language based on query parameters or use existing settings
|
||||||
|
theme := r.URL.Query().Get("theme")
|
||||||
|
if theme != "" {
|
||||||
|
settings.Theme = theme
|
||||||
|
saveUserSettings(w, settings)
|
||||||
|
} else if settings.Theme == "" {
|
||||||
|
settings.Theme = "dark" // Default theme
|
||||||
|
}
|
||||||
|
|
||||||
|
if safe != "" {
|
||||||
|
settings.SafeSearch = safe
|
||||||
|
saveUserSettings(w, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lang != "" {
|
||||||
|
settings.Language = lang
|
||||||
|
saveUserSettings(w, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the search page template if no query
|
||||||
if query == "" {
|
if query == "" {
|
||||||
http.ServeFile(w, r, "templates/search.html")
|
tmpl := template.Must(template.ParseFiles("templates/search.html"))
|
||||||
|
tmpl.Execute(w, settings)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings := loadUserSettings(r)
|
||||||
|
|
||||||
|
// Handle search based on the type
|
||||||
switch searchType {
|
switch searchType {
|
||||||
case "image":
|
case "image":
|
||||||
handleImageSearch(w, query, safe, lang, page)
|
handleImageSearch(w, settings, query, safe, lang, page)
|
||||||
case "video":
|
case "video":
|
||||||
handleVideoSearch(w, query, safe, lang, page)
|
handleVideoSearch(w, settings, query, safe, lang, page)
|
||||||
case "map":
|
case "map":
|
||||||
handleMapSearch(w, query, safe)
|
handleMapSearch(w, settings, query, safe)
|
||||||
case "forum":
|
case "forum":
|
||||||
handleForumsSearch(w, query, safe, lang, page)
|
handleForumsSearch(w, settings, query, safe, lang, page)
|
||||||
case "file":
|
case "file":
|
||||||
handleFileSearch(w, query, safe, lang, page)
|
handleFileSearch(w, settings, query, safe, lang, page)
|
||||||
case "text":
|
case "text":
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
HandleTextSearch(w, query, safe, lang, page)
|
HandleTextSearch(w, settings, query, safe, lang, page)
|
||||||
}
|
}
|
||||||
|
// This is immeasurably stupid it passes safe and language then it passes settings with safe and lang again
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSearchParams(r *http.Request) (query, safe, lang, searchType string, page int) {
|
func parseSearchParams(r *http.Request) (query, safe, lang, searchType string, page int) {
|
||||||
|
@ -139,7 +170,7 @@ func runServer() {
|
||||||
config := loadConfig()
|
config := loadConfig()
|
||||||
generateOpenSearchXML(config)
|
generateOpenSearchXML(config)
|
||||||
|
|
||||||
fmt.Printf("Server is listening on http://localhost:%d\n", config.Port)
|
printMessage("Server is listening on http://localhost:%d", config.Port)
|
||||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", config.Port), nil))
|
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", config.Port), nil))
|
||||||
|
|
||||||
// Start automatic update checker
|
// Start automatic update checker
|
||||||
|
|
8
map.go
8
map.go
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
@ -44,11 +43,11 @@ func geocodeQuery(query string) (latitude, longitude string, found bool, err err
|
||||||
return "", "", false, nil
|
return "", "", false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMapSearch(w http.ResponseWriter, query string, lang string) {
|
func handleMapSearch(w http.ResponseWriter, settings UserSettings, query string, lang string) {
|
||||||
// Geocode the query to get coordinates
|
// Geocode the query to get coordinates
|
||||||
latitude, longitude, found, err := geocodeQuery(query)
|
latitude, longitude, found, err := geocodeQuery(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error geocoding query: %s, error: %v", query, err)
|
printDebug("Error geocoding query: %s, error: %v", query, err)
|
||||||
http.Error(w, "Failed to find location", http.StatusInternalServerError)
|
http.Error(w, "Failed to find location", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -59,11 +58,12 @@ func handleMapSearch(w http.ResponseWriter, query string, lang string) {
|
||||||
"Latitude": latitude,
|
"Latitude": latitude,
|
||||||
"Longitude": longitude,
|
"Longitude": longitude,
|
||||||
"Found": found,
|
"Found": found,
|
||||||
|
"Theme": settings.Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl, err := template.ParseFiles("templates/map.html")
|
tmpl, err := template.ParseFiles("templates/map.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error loading map template: %v", err)
|
printErr("Error loading map template: %v", err)
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
5
node.go
5
node.go
|
@ -6,7 +6,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -111,9 +110,9 @@ func startNodeClient() {
|
||||||
|
|
||||||
err := sendMessage(peerAddr, msg)
|
err := sendMessage(peerAddr, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error sending message to %s: %v", peerAddr, err)
|
printWarn("Error sending message to %s: %v", peerAddr, err)
|
||||||
} else {
|
} else {
|
||||||
log.Println("Message sent successfully to", peerAddr)
|
printInfo("Message sent successfully to: %s", peerAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
|
|
|
@ -40,10 +40,10 @@ func printMessage(format string, args ...interface{}) {
|
||||||
|
|
||||||
// logMessage handles the actual logging logic without using the default logger's timestamp.
|
// logMessage handles the actual logging logic without using the default logger's timestamp.
|
||||||
func logMessage(level string, format string, args ...interface{}) {
|
func logMessage(level string, format string, args ...interface{}) {
|
||||||
timestamp := time.Now().Format("2006-01-02|15:04:05")
|
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||||||
message := fmt.Sprintf(format, args...)
|
message := fmt.Sprintf(format, args...)
|
||||||
if level != "" {
|
if level != "" {
|
||||||
fmt.Printf("[%s|%s] %s\n", timestamp, level, message)
|
fmt.Printf("[%s %s] %s\n", timestamp, level, message)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("[%s] %s\n", timestamp, message)
|
fmt.Printf("[%s] %s\n", timestamp, message)
|
||||||
}
|
}
|
||||||
|
|
74
static/css/dark.css
Normal file
74
static/css/dark.css
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
:root {
|
||||||
|
--html-bg: #1c1c1c;
|
||||||
|
--font-fg: #f1f3f4;
|
||||||
|
--fg: #BABCBE;
|
||||||
|
|
||||||
|
--search-bg: #161616;
|
||||||
|
--search-bg-input: #333333;
|
||||||
|
--search-bg-input-border: #3C4043;
|
||||||
|
--search-select: #282828;
|
||||||
|
|
||||||
|
--border: #303134;
|
||||||
|
|
||||||
|
--link: #8ab4f8;
|
||||||
|
--link-visited: #c58af9;
|
||||||
|
|
||||||
|
--snip-border: #303134;
|
||||||
|
--snip-background: #282828;
|
||||||
|
--snip-text: #f1f3f4;
|
||||||
|
|
||||||
|
--settings-border: #5f6368;
|
||||||
|
--button: #333333;
|
||||||
|
|
||||||
|
--footer-bg: #161616;
|
||||||
|
--footer-font: #999da2;
|
||||||
|
|
||||||
|
--highlight: #bcc0c3;
|
||||||
|
|
||||||
|
--blue: #8ab4f8;
|
||||||
|
|
||||||
|
--green: #31b06e;
|
||||||
|
|
||||||
|
--search-button: #BABCBE;
|
||||||
|
|
||||||
|
--image-view: #161616;
|
||||||
|
--image-view-titlebar: #161616;
|
||||||
|
--view-image-color: #000000;
|
||||||
|
--image-select: #303030;
|
||||||
|
--fff: #fff;
|
||||||
|
|
||||||
|
--publish-info: #7f869e;
|
||||||
|
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-btn:hover {
|
||||||
|
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-btn-2:hover {
|
||||||
|
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-btn-2 {
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||||
|
transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc-btn {
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||||
|
transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calc {
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-image-search {
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||||
|
transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-image-search:hover {
|
||||||
|
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
|
||||||
|
}
|
72
static/css/light.css
Normal file
72
static/css/light.css
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
:root {
|
||||||
|
--html-bg: #ffffff;
|
||||||
|
--font-fg: #000000;
|
||||||
|
--fg: #202124;
|
||||||
|
|
||||||
|
--search-bg: #ffffff;
|
||||||
|
--search-bg-input: #f6f6f6;
|
||||||
|
--search-bg-input-border: #dadce0;
|
||||||
|
--search-select: #eeeeee;
|
||||||
|
|
||||||
|
--border: #dadce0;
|
||||||
|
|
||||||
|
--link: #1a0dab;
|
||||||
|
--link-visited: #681da8;
|
||||||
|
|
||||||
|
--snip-border: #dadce0;
|
||||||
|
--snip-background: #ffffff;
|
||||||
|
--snip-text: #000000;
|
||||||
|
|
||||||
|
--settings-border: #5f6368;
|
||||||
|
--button: #f6f6f6;
|
||||||
|
|
||||||
|
--footer-bg: #f6f6f6;
|
||||||
|
--footer-font: #353535;
|
||||||
|
|
||||||
|
--highlight: #202124;
|
||||||
|
|
||||||
|
--blue: #4285f4;
|
||||||
|
|
||||||
|
--green: #202124;
|
||||||
|
|
||||||
|
--image-view: #ffffff;
|
||||||
|
--image-view-titlebar: #ffffff;
|
||||||
|
--view-image-color: #f1f3f4;
|
||||||
|
--image-select: #f6f6f6;
|
||||||
|
--fff: #fff;
|
||||||
|
|
||||||
|
--publish-info: #202124;
|
||||||
|
|
||||||
|
--search-button: #202124;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper-results:hover,
|
||||||
|
.wrapper-results:focus-within,
|
||||||
|
.wrapper:hover,
|
||||||
|
.wrapper:focus-within {
|
||||||
|
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08) !important;
|
||||||
|
transition: all 0.3s cubic-bezier(.25, .8, .25, 1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check p {
|
||||||
|
color: var(--highlight) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image_view {
|
||||||
|
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08) !important;
|
||||||
|
transition: all 0.3s cubic-bezier(.25, .8, .25, 1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-menu {
|
||||||
|
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-image-search {
|
||||||
|
box-shadow: none;
|
||||||
|
transition: all 0.3s cubic-bezier(.25, .8, .25, 1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-image-search:hover {
|
||||||
|
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08) !important;
|
||||||
|
transition: all 0.3s cubic-bezier(.25, .8, .25, 1) !important;
|
||||||
|
}
|
|
@ -120,6 +120,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.clean {
|
.clean {
|
||||||
|
margin-top: 30px;
|
||||||
max-width: 775px;
|
max-width: 775px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +557,7 @@ hr {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
border: 2px solid var(--search-bg-input-border);
|
border: 1px solid var(--search-bg-input-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper input {
|
.wrapper input {
|
||||||
|
@ -689,7 +690,8 @@ hr {
|
||||||
}
|
}
|
||||||
|
|
||||||
.kno_wiki_show {
|
.kno_wiki_show {
|
||||||
display: initial !important;
|
display: initial !important;
|
||||||
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.open-in-new-tab * {
|
.open-in-new-tab * {
|
||||||
|
@ -796,6 +798,7 @@ form.torrent-sort {
|
||||||
transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
|
transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-menu h2 {
|
.search-menu h2 {
|
||||||
|
@ -1164,7 +1167,7 @@ p {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
margin-right: 10px;
|
margin-right: 14px;
|
||||||
color: var(--search-button);
|
color: var(--search-button);
|
||||||
margin-top: 72px;
|
margin-top: 72px;
|
||||||
padding-bottom: 11px;
|
padding-bottom: 11px;
|
||||||
|
@ -1218,6 +1221,10 @@ p {
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
.results a:visited h3,
|
.results a:visited h3,
|
||||||
.result_sublink a:visited h3 {
|
.result_sublink a:visited h3 {
|
||||||
color: var(--link-visited);
|
color: var(--link-visited);
|
||||||
|
@ -1236,6 +1243,7 @@ p {
|
||||||
.results p,
|
.results p,
|
||||||
.result_sublink p {
|
.result_sublink p {
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
color: var(--font-fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.results a,
|
.results a,
|
||||||
|
@ -1245,6 +1253,7 @@ p {
|
||||||
.result_sublink a:hover,
|
.result_sublink a:hover,
|
||||||
.result_sublink a:visited {
|
.result_sublink a:visited {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
/* color: #ebecf7; */
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1287,6 +1296,12 @@ p {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wiki_known_for {
|
||||||
|
margin: 0px;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.snip img {
|
.snip img {
|
||||||
float: right;
|
float: right;
|
||||||
max-width: 30%;
|
max-width: 30%;
|
||||||
|
@ -1295,8 +1310,7 @@ p {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snip a {
|
.snip .wiki_link {
|
||||||
display: block;
|
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--link);
|
color: var(--link);
|
||||||
|
@ -1310,6 +1324,28 @@ p {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.snip .about {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.factpoint {
|
||||||
|
color: var(--fg);
|
||||||
|
font-weight: bold;
|
||||||
|
vertical-align: text-top;
|
||||||
|
text-align: left;
|
||||||
|
padding-right: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fact a {
|
||||||
|
color: var(--link);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.fact a:visited {
|
||||||
|
color: var(--link-visited);
|
||||||
|
}
|
||||||
|
|
||||||
.snipp {
|
.snipp {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
|
@ -1690,7 +1726,7 @@ body, h1, p, a, input, button {
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
background-color: var(--search-bg);
|
/* background-color: var(--search-bg); */
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-none {
|
.mobile-none {
|
||||||
|
@ -1860,8 +1896,7 @@ body, h1, p, a, input, button {
|
||||||
color: var(--link) !important;
|
color: var(--link) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
/* Variables for light theme */
|
|
||||||
:root {
|
:root {
|
||||||
--background-color: #ffffff;
|
--background-color: #ffffff;
|
||||||
--text-color: #000000;
|
--text-color: #000000;
|
||||||
|
@ -1890,7 +1925,6 @@ body, h1, p, a, input, button {
|
||||||
--box-shadow: #00000020;
|
--box-shadow: #00000020;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles for dark theme */
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--background-color: #202124;
|
--background-color: #202124;
|
||||||
|
@ -1919,4 +1953,4 @@ body, h1, p, a, input, button {
|
||||||
--green: #8ab4f8;
|
--green: #8ab4f8;
|
||||||
--box-shadow: #ffffff20;
|
--box-shadow: #ffffff20;
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
BIN
static/images/dark.webp
Normal file
BIN
static/images/dark.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
BIN
static/images/light.webp
Normal file
BIN
static/images/light.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.8 KiB |
|
@ -4,7 +4,8 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{.Query}} - Ocásek</title>
|
<title>{{.Query}} - Ocásek</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/{{.Theme}}.css">
|
||||||
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -74,7 +75,7 @@
|
||||||
</form>
|
</form>
|
||||||
<div class="clean">
|
<div class="clean">
|
||||||
{{ range .Results }}
|
{{ range .Results }}
|
||||||
<div class="results">
|
<div class="results" id="results">
|
||||||
{{ if .Error }}
|
{{ if .Error }}
|
||||||
<div class="error">{{ .Error }}</div>
|
<div class="error">{{ .Error }}</div>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{.Query}} - Ocásek</title>
|
<title>{{.Query}} - Ocásek</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/{{.Theme}}.css">
|
||||||
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -61,7 +62,7 @@
|
||||||
{{if .Results}}
|
{{if .Results}}
|
||||||
{{range .Results}}
|
{{range .Results}}
|
||||||
<div class="result_item">
|
<div class="result_item">
|
||||||
<a href="{{.URL}}">{{.URL}}</a>
|
<a id="link" href="{{.URL}}">{{.URL}}</a>
|
||||||
<a href="{{.URL}}"><h3>{{.Header}}</h3></a>
|
<a href="{{.URL}}"><h3>{{.Header}}</h3></a>
|
||||||
<p>{{.Description}}</p>
|
<p>{{.Description}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{.Query}} - Ocásek</title>
|
<title>{{.Query}} - Ocásek</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/{{.Theme}}.css">
|
||||||
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{ .Query }} - Ocásek</title>
|
<title>{{ .Query }} - Ocásek</title>
|
||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/{{.Theme}}.css">
|
||||||
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css" />
|
||||||
|
|
|
@ -5,11 +5,56 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Search with Ocásek</title>
|
<title>Search with Ocásek</title>
|
||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/{{.Theme}}.css">
|
||||||
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const settingsIcon = document.querySelector('.settings-icon-link-search');
|
||||||
|
const searchMenu = document.querySelector('.search-menu');
|
||||||
|
|
||||||
|
settingsIcon.addEventListener('click', function () {
|
||||||
|
searchMenu.classList.toggle('settings-menu-hidden');
|
||||||
|
searchMenu.classList.toggle('settings-menu-visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Theme change event listeners
|
||||||
|
document.getElementById('dark_theme').addEventListener('click', function () {
|
||||||
|
window.location.href = '/search?theme=dark';
|
||||||
|
});
|
||||||
|
document.getElementById('light_theme').addEventListener('click', function () {
|
||||||
|
window.location.href = '/search?theme=light';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<div class="settings-search-div settings-search-div-search">
|
<div class="settings-search-div settings-search-div-search">
|
||||||
<a class="material-icons-round clickable" href="/settings">tune</a>
|
<button class="material-icons-round clickable settings-icon-link settings-icon-link-search">menu</button>
|
||||||
|
</div>
|
||||||
|
<div class="search-menu settings-menu-hidden">
|
||||||
|
<h2>Settings</h2>
|
||||||
|
<div class="settings-content">
|
||||||
|
<button id="settingsButton" onclick="window.location.href='/settings'">All settings</button> <!-- Well its unessesary to use js here but this menu will not work without js anyway -->
|
||||||
|
<div class="theme-settings">
|
||||||
|
<p><span class="highlight">Theme: </span> <span id="theme_name">Default Theme</span></p>
|
||||||
|
<div class="themes-settings-menu">
|
||||||
|
<div><img class="view-image-search clickable" id="dark_theme" alt="Dark Theme" src="/static/images/dark.webp"></div>
|
||||||
|
<div><img class="view-image-search clickable" id="light_theme" alt="Light Theme" src="/static/images/light.webp"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<select class="lang" name="lang">
|
||||||
|
<option value="en" selected>English</option>
|
||||||
|
<option value="fr">Français</option>
|
||||||
|
<option value="es">Español</option>
|
||||||
|
<!-- Add other languages as needed -->
|
||||||
|
</select>
|
||||||
|
<select class="domain" name="safe">
|
||||||
|
<option value="active" selected>Safe search on</option>
|
||||||
|
<option value="">Safe search off</option>
|
||||||
|
<!-- Add other domains as needed -->
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form action="/search" class="search-container" method="post" autocomplete="off">
|
<form action="/search" class="search-container" method="post" autocomplete="off">
|
||||||
<h1>Ocásek</h1>
|
<h1>Ocásek</h1>
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Settings - Ocásek</title>
|
<title>Settings - Ocásek</title>
|
||||||
<link rel="stylesheet" type="text/css" href="static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/{{.Theme}}.css">
|
||||||
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{.Query}} - Ocásek</title>
|
<title>{{.Query}} - Ocásek</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/{{.Theme}}.css">
|
||||||
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -61,7 +62,7 @@
|
||||||
{{if .Results}}
|
{{if .Results}}
|
||||||
{{range .Results}}
|
{{range .Results}}
|
||||||
<div class="result_item">
|
<div class="result_item">
|
||||||
<a href="{{.URL}}">{{.URL}}</a>
|
<a id="link" href="{{.URL}}">{{.URL}}</a>
|
||||||
<a href="{{.URL}}"><h3>{{.Header}}</h3></a>
|
<a href="{{.URL}}"><h3>{{.Header}}</h3></a>
|
||||||
<p>{{.Description}}</p>
|
<p>{{.Description}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{.Query}} - Ocásek</title>
|
<title>{{.Query}} - Ocásek</title>
|
||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/{{.Theme}}.css">
|
||||||
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
<link rel="search" type="application/opensearchdescription+xml" title="Ocásek" href="/opensearch.xml">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
25
text.go
25
text.go
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -20,7 +19,7 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleTextSearch(w http.ResponseWriter, query, safe, lang string, page int) {
|
func HandleTextSearch(w http.ResponseWriter, settings UserSettings, query, safe, lang string, page int) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "text"}
|
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "text"}
|
||||||
|
@ -39,7 +38,7 @@ func HandleTextSearch(w http.ResponseWriter, query, safe, lang string, page int)
|
||||||
elapsedTime := time.Since(startTime)
|
elapsedTime := time.Since(startTime)
|
||||||
tmpl, err := template.New("text.html").Funcs(funcs).ParseFiles("templates/text.html")
|
tmpl, err := template.New("text.html").Funcs(funcs).ParseFiles("templates/text.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error parsing template: %v", err)
|
printErr("Error parsing template: %v", err)
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -54,6 +53,7 @@ func HandleTextSearch(w http.ResponseWriter, query, safe, lang string, page int)
|
||||||
HasPrevPage bool
|
HasPrevPage bool
|
||||||
HasNextPage bool
|
HasNextPage bool
|
||||||
NoResults bool
|
NoResults bool
|
||||||
|
Theme string
|
||||||
}{
|
}{
|
||||||
Results: combinedResults,
|
Results: combinedResults,
|
||||||
Query: query,
|
Query: query,
|
||||||
|
@ -64,11 +64,12 @@ func HandleTextSearch(w http.ResponseWriter, query, safe, lang string, page int)
|
||||||
HasPrevPage: page > 1,
|
HasPrevPage: page > 1,
|
||||||
HasNextPage: len(combinedResults) >= 50,
|
HasNextPage: len(combinedResults) >= 50,
|
||||||
NoResults: len(combinedResults) == 0,
|
NoResults: len(combinedResults) == 0,
|
||||||
|
Theme: settings.Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tmpl.Execute(w, data)
|
err = tmpl.Execute(w, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error executing template: %v", err)
|
printErr("Error executing template: %v", err)
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,10 +81,10 @@ func getTextResultsFromCacheOrFetch(cacheKey CacheKey, query, safe, lang string,
|
||||||
go func() {
|
go func() {
|
||||||
results, exists := resultsCache.Get(cacheKey)
|
results, exists := resultsCache.Get(cacheKey)
|
||||||
if exists {
|
if exists {
|
||||||
log.Println("Cache hit")
|
printInfo("Cache hit")
|
||||||
cacheChan <- results
|
cacheChan <- results
|
||||||
} else {
|
} else {
|
||||||
log.Println("Cache miss")
|
printInfo("Cache miss")
|
||||||
cacheChan <- nil
|
cacheChan <- nil
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -100,7 +101,7 @@ func getTextResultsFromCacheOrFetch(cacheKey CacheKey, query, safe, lang string,
|
||||||
combinedResults = textResults
|
combinedResults = textResults
|
||||||
}
|
}
|
||||||
case <-time.After(2 * time.Second):
|
case <-time.After(2 * time.Second):
|
||||||
log.Println("Cache check timeout")
|
printInfo("Cache check timeout")
|
||||||
combinedResults = fetchTextResults(query, safe, lang, page)
|
combinedResults = fetchTextResults(query, safe, lang, page)
|
||||||
if len(combinedResults) > 0 {
|
if len(combinedResults) > 0 {
|
||||||
resultsCache.Set(cacheKey, convertToSearchResults(combinedResults))
|
resultsCache.Set(cacheKey, convertToSearchResults(combinedResults))
|
||||||
|
@ -113,13 +114,13 @@ func getTextResultsFromCacheOrFetch(cacheKey CacheKey, query, safe, lang string,
|
||||||
func prefetchPage(query, safe, lang string, page int) {
|
func prefetchPage(query, safe, lang string, page int) {
|
||||||
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "text"}
|
cacheKey := CacheKey{Query: query, Page: page, Safe: safe == "true", Lang: lang, Type: "text"}
|
||||||
if _, exists := resultsCache.Get(cacheKey); !exists {
|
if _, exists := resultsCache.Get(cacheKey); !exists {
|
||||||
log.Printf("Page %d not cached, caching now...", page)
|
printInfo("Page %d not cached, caching now...", page)
|
||||||
pageResults := fetchTextResults(query, safe, lang, page)
|
pageResults := fetchTextResults(query, safe, lang, page)
|
||||||
if len(pageResults) > 0 {
|
if len(pageResults) > 0 {
|
||||||
resultsCache.Set(cacheKey, convertToSearchResults(pageResults))
|
resultsCache.Set(cacheKey, convertToSearchResults(pageResults))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Page %d already cached", page)
|
printInfo("Page %d already cached", page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,12 +128,12 @@ func fetchTextResults(query, safe, lang string, page int) []TextSearchResult {
|
||||||
var results []TextSearchResult
|
var results []TextSearchResult
|
||||||
|
|
||||||
for _, engine := range textSearchEngines {
|
for _, engine := range textSearchEngines {
|
||||||
log.Printf("Using search engine: %s", engine.Name)
|
printInfo("Using search engine: %s", engine.Name)
|
||||||
|
|
||||||
searchResults, duration, err := engine.Func(query, safe, lang, page)
|
searchResults, duration, err := engine.Func(query, safe, lang, page)
|
||||||
updateEngineMetrics(&engine, duration, err == nil)
|
updateEngineMetrics(&engine, duration, err == nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error performing search with %s: %v", engine.Name, err)
|
printWarn("Error performing search with %s: %v", engine.Name, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +147,7 @@ func fetchTextResults(query, safe, lang string, page int) []TextSearchResult {
|
||||||
|
|
||||||
// If no results found after trying all engines
|
// If no results found after trying all engines
|
||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
log.Printf("No text results found for query: %s, trying other nodes", query)
|
printWarn("No text results found for query: %s, trying other nodes", query)
|
||||||
results = tryOtherNodesForTextSearch(query, safe, lang, page, []string{hostID})
|
results = tryOtherNodesForTextSearch(query, safe, lang, page, []string{hostID})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
54
user-settings.go
Normal file
54
user-settings.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
type UserSettings struct {
|
||||||
|
Theme string
|
||||||
|
Language string
|
||||||
|
SafeSearch string
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadUserSettings(r *http.Request) UserSettings {
|
||||||
|
var settings UserSettings
|
||||||
|
|
||||||
|
// Load theme
|
||||||
|
if cookie, err := r.Cookie("theme"); err == nil {
|
||||||
|
settings.Theme = cookie.Value
|
||||||
|
} else {
|
||||||
|
settings.Theme = "dark" // Default theme
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load language
|
||||||
|
if cookie, err := r.Cookie("language"); err == nil {
|
||||||
|
settings.Language = cookie.Value
|
||||||
|
} else {
|
||||||
|
settings.Language = "en" // Default language
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load safe search
|
||||||
|
if cookie, err := r.Cookie("safe"); err == nil {
|
||||||
|
settings.SafeSearch = cookie.Value
|
||||||
|
} else {
|
||||||
|
settings.SafeSearch = "" // Default safe search off
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveUserSettings(w http.ResponseWriter, settings UserSettings) {
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: "theme",
|
||||||
|
Value: settings.Theme,
|
||||||
|
Path: "/",
|
||||||
|
})
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: "language",
|
||||||
|
Value: settings.Language,
|
||||||
|
Path: "/",
|
||||||
|
})
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: "safe",
|
||||||
|
Value: settings.SafeSearch,
|
||||||
|
Path: "/",
|
||||||
|
})
|
||||||
|
}
|
9
video.go
9
video.go
|
@ -149,13 +149,13 @@ func makeHTMLRequest(query, safe, lang string, page int) (*VideoAPIResponse, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleVideoSearch adapted from the Python `videoResults`, handles video search requests
|
// handleVideoSearch adapted from the Python `videoResults`, handles video search requests
|
||||||
func handleVideoSearch(w http.ResponseWriter, query, safe, lang string, page int) {
|
func handleVideoSearch(w http.ResponseWriter, settings UserSettings, query, safe, lang string, page int) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
results := fetchVideoResults(query, safe, lang, page)
|
results := fetchVideoResults(query, settings.SafeSearch, settings.Language, page)
|
||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
log.Printf("No results from primary search, trying other nodes")
|
log.Printf("No results from primary search, trying other nodes")
|
||||||
results = tryOtherNodesForVideoSearch(query, safe, lang, page, []string{hostID})
|
results = tryOtherNodesForVideoSearch(query, settings.SafeSearch, settings.Language, page, []string{hostID})
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
|
@ -172,7 +172,8 @@ func handleVideoSearch(w http.ResponseWriter, query, safe, lang string, page int
|
||||||
"Fetched": fmt.Sprintf("%.2f seconds", elapsed.Seconds()),
|
"Fetched": fmt.Sprintf("%.2f seconds", elapsed.Seconds()),
|
||||||
"Page": page,
|
"Page": page,
|
||||||
"HasPrevPage": page > 1,
|
"HasPrevPage": page > 1,
|
||||||
"HasNextPage": len(results) > 0, // assuming you have a way to determine if there are more pages
|
"HasNextPage": len(results) > 0, // no
|
||||||
|
"Theme": settings.Theme,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error executing template: %v", err)
|
log.Printf("Error executing template: %v", err)
|
||||||
|
|
Loading…
Add table
Reference in a new issue