mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-03 20:32:07 -04:00
weather: fix geoclue IP fallback
This commit is contained in:
@@ -5,21 +5,38 @@ import "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||
func NewClient() Client {
|
||||
geoclueClient, err := newGeoClueClient()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to initialize GeoClue2 client: %v", err)
|
||||
log.Info("Falling back to IP location")
|
||||
return newIpClient()
|
||||
log.Warnf("GeoClue2 unavailable: %v", err)
|
||||
return newSeededIpClient()
|
||||
}
|
||||
|
||||
loc, _ := geoclueClient.GetLocation()
|
||||
if loc.Latitude != 0 || loc.Longitude != 0 {
|
||||
log.Info("Using GeoClue2 location")
|
||||
return geoclueClient
|
||||
}
|
||||
|
||||
log.Info("GeoClue2 has no fix yet, seeding with IP location")
|
||||
ipClient := newIpClient()
|
||||
if ipLoc, err := ipClient.GetLocation(); err == nil {
|
||||
geoclueClient.SeedLocation(ipLoc)
|
||||
ipLoc, err := fetchIPLocation()
|
||||
if err != nil {
|
||||
log.Warnf("IP location seed failed: %v", err)
|
||||
return geoclueClient
|
||||
}
|
||||
|
||||
log.Info("Seeded GeoClue2 with IP location")
|
||||
geoclueClient.SeedLocation(Location{Latitude: ipLoc.Latitude, Longitude: ipLoc.Longitude})
|
||||
return geoclueClient
|
||||
}
|
||||
|
||||
func newSeededIpClient() *IpClient {
|
||||
client := newIpClient()
|
||||
ipLoc, err := fetchIPLocation()
|
||||
if err != nil {
|
||||
log.Warnf("IP location also failed: %v", err)
|
||||
return client
|
||||
}
|
||||
|
||||
log.Info("Using IP location")
|
||||
client.currLocation.Latitude = ipLoc.Latitude
|
||||
client.currLocation.Longitude = ipLoc.Longitude
|
||||
return client
|
||||
}
|
||||
|
||||
@@ -6,26 +6,27 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||
)
|
||||
|
||||
type IpClient struct {
|
||||
currLocation *Location
|
||||
}
|
||||
|
||||
type ipLocationResult struct {
|
||||
Location
|
||||
City string
|
||||
}
|
||||
|
||||
type ipAPIResponse struct {
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
City string `json:"city"`
|
||||
Status string `json:"status"`
|
||||
Lat float64 `json:"lat"`
|
||||
Lon float64 `json:"lon"`
|
||||
City string `json:"city"`
|
||||
}
|
||||
|
||||
func newIpClient() *IpClient {
|
||||
return &IpClient{
|
||||
currLocation: &Location{
|
||||
Latitude: 0.0,
|
||||
Longitude: 0.0,
|
||||
},
|
||||
currLocation: &Location{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,55 +35,57 @@ func (c *IpClient) Subscribe(id string) chan Location {
|
||||
if location, err := c.GetLocation(); err == nil {
|
||||
ch <- location
|
||||
}
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func (c *IpClient) Unsubscribe(id string) {
|
||||
// Stub
|
||||
}
|
||||
func (c *IpClient) Unsubscribe(id string) {}
|
||||
|
||||
func (c *IpClient) Close() {
|
||||
// Stub
|
||||
}
|
||||
func (c *IpClient) Close() {}
|
||||
|
||||
func (c *IpClient) GetLocation() (Location, error) {
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
if c.currLocation.Latitude != 0 || c.currLocation.Longitude != 0 {
|
||||
return *c.currLocation, nil
|
||||
}
|
||||
|
||||
result := Location{
|
||||
Latitude: 0.0,
|
||||
Longitude: 0.0,
|
||||
result, err := fetchIPLocation()
|
||||
if err != nil {
|
||||
return Location{}, err
|
||||
}
|
||||
|
||||
c.currLocation.Latitude = result.Latitude
|
||||
c.currLocation.Longitude = result.Longitude
|
||||
return *c.currLocation, nil
|
||||
}
|
||||
|
||||
func fetchIPLocation() (ipLocationResult, error) {
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
|
||||
resp, err := client.Get("http://ip-api.com/json/")
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("failed to fetch IP location: %w", err)
|
||||
return ipLocationResult{}, fmt.Errorf("failed to fetch IP location: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return result, fmt.Errorf("ip-api.com returned status %d", resp.StatusCode)
|
||||
return ipLocationResult{}, fmt.Errorf("ip-api.com returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("failed to read response: %w", err)
|
||||
return ipLocationResult{}, fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
var data ipAPIResponse
|
||||
if err := json.Unmarshal(body, &data); err != nil {
|
||||
return result, fmt.Errorf("failed to parse response: %w", err)
|
||||
return ipLocationResult{}, fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
if data.Lat == 0 && data.Lon == 0 {
|
||||
return result, fmt.Errorf("missing location data in response")
|
||||
if data.Status == "fail" || (data.Lat == 0 && data.Lon == 0) {
|
||||
return ipLocationResult{}, fmt.Errorf("ip-api.com returned no location data")
|
||||
}
|
||||
|
||||
log.Infof("Fetched IP-based location: %s (%.4f, %.4f)", data.City, data.Lat, data.Lon)
|
||||
result.Latitude = data.Lat
|
||||
result.Longitude = data.Lon
|
||||
|
||||
return result, nil
|
||||
return ipLocationResult{
|
||||
Location: Location{Latitude: data.Lat, Longitude: data.Lon},
|
||||
City: data.City,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -17,11 +17,9 @@ Singleton {
|
||||
|
||||
signal locationChanged(var data)
|
||||
|
||||
readonly property var lowPriorityCmd: ["nice", "-n", "19", "ionice", "-c3"]
|
||||
readonly property var curlBaseCmd: ["curl", "-sS", "--fail", "--connect-timeout", "3", "--max-time", "6", "--limit-rate", "100k", "--compressed"]
|
||||
|
||||
Component.onCompleted: {
|
||||
getState();
|
||||
onLocationAvailableChanged: {
|
||||
if (locationAvailable && !valid)
|
||||
getState();
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -46,50 +44,12 @@ Singleton {
|
||||
}
|
||||
|
||||
function getState() {
|
||||
if (!locationAvailable) {
|
||||
fetchIPLocation();
|
||||
if (!locationAvailable)
|
||||
return;
|
||||
}
|
||||
|
||||
DMSService.sendRequest("location.getState", null, response => {
|
||||
if (response.result && (response.result.latitude !== 0 || response.result.longitude !== 0)) {
|
||||
if (response.result)
|
||||
handleStateUpdate(response.result);
|
||||
return;
|
||||
}
|
||||
fetchIPLocation();
|
||||
});
|
||||
}
|
||||
|
||||
function fetchIPLocation() {
|
||||
if (root.valid)
|
||||
return;
|
||||
ipLocationFetcher.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: ipLocationFetcher
|
||||
command: root.lowPriorityCmd.concat(root.curlBaseCmd).concat(["http://ip-api.com/json/"])
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const raw = text.trim();
|
||||
if (!raw || raw[0] !== "{")
|
||||
return;
|
||||
|
||||
try {
|
||||
const data = JSON.parse(raw);
|
||||
if (data.status === "fail")
|
||||
return;
|
||||
|
||||
const lat = parseFloat(data.lat);
|
||||
const lon = parseFloat(data.lon);
|
||||
if (isNaN(lat) || isNaN(lon) || (lat === 0 && lon === 0))
|
||||
return;
|
||||
|
||||
root.handleStateUpdate({ latitude: lat, longitude: lon });
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user