mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-13 17:22:08 -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 {
|
func NewClient() Client {
|
||||||
geoclueClient, err := newGeoClueClient()
|
geoclueClient, err := newGeoClueClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Failed to initialize GeoClue2 client: %v", err)
|
log.Warnf("GeoClue2 unavailable: %v", err)
|
||||||
log.Info("Falling back to IP location")
|
return newSeededIpClient()
|
||||||
return newIpClient()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loc, _ := geoclueClient.GetLocation()
|
loc, _ := geoclueClient.GetLocation()
|
||||||
if loc.Latitude != 0 || loc.Longitude != 0 {
|
if loc.Latitude != 0 || loc.Longitude != 0 {
|
||||||
|
log.Info("Using GeoClue2 location")
|
||||||
return geoclueClient
|
return geoclueClient
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("GeoClue2 has no fix yet, seeding with IP location")
|
log.Info("GeoClue2 has no fix yet, seeding with IP location")
|
||||||
ipClient := newIpClient()
|
ipLoc, err := fetchIPLocation()
|
||||||
if ipLoc, err := ipClient.GetLocation(); err == nil {
|
if err != nil {
|
||||||
geoclueClient.SeedLocation(ipLoc)
|
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
|
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"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type IpClient struct {
|
type IpClient struct {
|
||||||
currLocation *Location
|
currLocation *Location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ipLocationResult struct {
|
||||||
|
Location
|
||||||
|
City string
|
||||||
|
}
|
||||||
|
|
||||||
type ipAPIResponse struct {
|
type ipAPIResponse struct {
|
||||||
Lat float64 `json:"lat"`
|
Status string `json:"status"`
|
||||||
Lon float64 `json:"lon"`
|
Lat float64 `json:"lat"`
|
||||||
City string `json:"city"`
|
Lon float64 `json:"lon"`
|
||||||
|
City string `json:"city"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIpClient() *IpClient {
|
func newIpClient() *IpClient {
|
||||||
return &IpClient{
|
return &IpClient{
|
||||||
currLocation: &Location{
|
currLocation: &Location{},
|
||||||
Latitude: 0.0,
|
|
||||||
Longitude: 0.0,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,55 +35,57 @@ func (c *IpClient) Subscribe(id string) chan Location {
|
|||||||
if location, err := c.GetLocation(); err == nil {
|
if location, err := c.GetLocation(); err == nil {
|
||||||
ch <- location
|
ch <- location
|
||||||
}
|
}
|
||||||
|
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *IpClient) Unsubscribe(id string) {
|
func (c *IpClient) Unsubscribe(id string) {}
|
||||||
// Stub
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *IpClient) Close() {
|
func (c *IpClient) Close() {}
|
||||||
// Stub
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *IpClient) GetLocation() (Location, error) {
|
func (c *IpClient) GetLocation() (Location, error) {
|
||||||
client := &http.Client{
|
if c.currLocation.Latitude != 0 || c.currLocation.Longitude != 0 {
|
||||||
Timeout: 10 * time.Second,
|
return *c.currLocation, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
result := Location{
|
result, err := fetchIPLocation()
|
||||||
Latitude: 0.0,
|
if err != nil {
|
||||||
Longitude: 0.0,
|
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/")
|
resp, err := client.Get("http://ip-api.com/json/")
|
||||||
if err != nil {
|
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()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
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)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
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
|
var data ipAPIResponse
|
||||||
if err := json.Unmarshal(body, &data); err != nil {
|
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 {
|
if data.Status == "fail" || (data.Lat == 0 && data.Lon == 0) {
|
||||||
return result, fmt.Errorf("missing location data in response")
|
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)
|
return ipLocationResult{
|
||||||
result.Latitude = data.Lat
|
Location: Location{Latitude: data.Lat, Longitude: data.Lon},
|
||||||
result.Longitude = data.Lon
|
City: data.City,
|
||||||
|
}, nil
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,9 @@ Singleton {
|
|||||||
|
|
||||||
signal locationChanged(var data)
|
signal locationChanged(var data)
|
||||||
|
|
||||||
readonly property var lowPriorityCmd: ["nice", "-n", "19", "ionice", "-c3"]
|
onLocationAvailableChanged: {
|
||||||
readonly property var curlBaseCmd: ["curl", "-sS", "--fail", "--connect-timeout", "3", "--max-time", "6", "--limit-rate", "100k", "--compressed"]
|
if (locationAvailable && !valid)
|
||||||
|
getState();
|
||||||
Component.onCompleted: {
|
|
||||||
getState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -46,50 +44,12 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getState() {
|
function getState() {
|
||||||
if (!locationAvailable) {
|
if (!locationAvailable)
|
||||||
fetchIPLocation();
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
DMSService.sendRequest("location.getState", null, response => {
|
DMSService.sendRequest("location.getState", null, response => {
|
||||||
if (response.result && (response.result.latitude !== 0 || response.result.longitude !== 0)) {
|
if (response.result)
|
||||||
handleStateUpdate(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