Clean up + add webserver

This commit is contained in:
William Bouzourène 2026-02-15 20:50:58 +01:00
parent 2eed16376f
commit 67e2e4d306
Signed by: bouzoure
GPG key ID: 423440D735B56BE2
7 changed files with 216 additions and 68 deletions

File diff suppressed because one or more lines are too long

21
go.mod
View file

@ -3,8 +3,25 @@ module git.readonly.ch/bouzoure/gpx-downloader
go 1.25.7
require (
github.com/golang-io/requests v0.0.0-20260112012319-11ff3d588020 // indirect
github.com/tkrajina/gpxgo v1.4.0 // indirect
github.com/gofiber/fiber/v2 v2.52.11
github.com/gofiber/helmet/v2 v2.2.26
github.com/golang-io/requests v0.0.0-20260112012319-11ff3d588020
github.com/tkrajina/gpxgo v1.4.0
)
require (
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.69.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
)

29
go.sum
View file

@ -1,16 +1,45 @@
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofiber/fiber/v2 v2.52.11 h1:5f4yzKLcBcF8ha1GQTWB+mpblWz3Vz6nSAbTL31HkWs=
github.com/gofiber/fiber/v2 v2.52.11/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/helmet/v2 v2.2.26 h1:KreQVUpCIGppPQ6Yt8qQMaIR4fVXMnvBdsda0dJSsO8=
github.com/gofiber/helmet/v2 v2.2.26/go.mod h1:XE0DF4cgf0M5xIt7qyAK5zOi8jJblhxfSDv9DAmEEQo=
github.com/golang-io/requests v0.0.0-20260112012319-11ff3d588020 h1:NsY7l0CzD6sL4YDmi2ug1XJ2syK6HYtE4Vu38+S59Kc=
github.com/golang-io/requests v0.0.0-20260112012319-11ff3d588020/go.mod h1:axo3gO6bWOpJNUipkqkmAH7WwdeLg5phLVI3N1dFBnM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tkrajina/gpxgo v1.4.0 h1:cSD5uSwy3VZuNFieTEZLyRnuIwhonQEkGPkPGW4XNag=
github.com/tkrajina/gpxgo v1.4.0/go.mod h1:BXSMfUAvKiEhMEXAFM2NvNsbjsSvp394mOvdcNjettg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=

82
main.go
View file

@ -1,75 +1,29 @@
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"log"
"github.com/golang-io/requests"
"github.com/tkrajina/gpxgo/gpx"
"git.readonly.ch/bouzoure/gpx-downloader/routes"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/helmet/v2"
)
type SuisseMobileGeometry struct {
Segments [][][]float64 `json:"coordinates"`
}
type SuisseMobileProperties struct {
Name string `json:"name"`
}
type SuisseMobileResponse struct {
Properties SuisseMobileProperties `json:"properties"`
Geometry SuisseMobileGeometry `json:"geometry"`
}
func main() {
sess := requests.New(requests.URL("https://schweizmobil.ch"))
resp, _ := sess.DoRequest(context.Background(),
requests.Path("/api/6/tracks/1856337628"),
)
// Create fiber config
appConfig := fiber.Config{}
var response SuisseMobileResponse
err := json.Unmarshal(resp.Content.Bytes(), &response)
// Init fiber webapp with config
app := fiber.New(appConfig)
// Apply middlewares
app.Use(helmet.New())
// Routes
app.Get("/", routes.Index)
app.Post("/fetch", routes.Fetch)
err := app.Listen("127.0.0.1:3000")
if err != nil {
panic(err)
}
var gpxFile gpx.GPX
var gpxTrack gpx.GPXTrack
gpxFile.Name = response.Properties.Name
for _, segments := range response.Geometry.Segments {
var gpxTrackSegment gpx.GPXTrackSegment
for _, point := range segments {
fmt.Println(point)
var gpxPoint gpx.GPXPoint
gpxPoint.Longitude = point[0]
gpxPoint.Latitude = point[1]
gpxPoint.Elevation = *gpx.NewNullableFloat64(point[2])
gpxTrackSegment.AppendPoint(&gpxPoint)
}
gpxTrack.AppendSegment(&gpxTrackSegment)
}
gpxFile.AppendTrack(&gpxTrack)
xml, err := gpxFile.ToXml(gpx.ToXmlParams{})
if err != nil {
panic(err)
}
file, err := os.Create("1856337628.gpx")
if err != nil {
panic(err)
}
_, err = file.Write(xml)
if err != nil {
panic(err)
log.Fatal(err)
}
}

View file

@ -0,0 +1,84 @@
package providers
import (
"context"
"encoding/json"
"fmt"
"regexp"
"strconv"
"github.com/golang-io/requests"
"github.com/tkrajina/gpxgo/gpx"
)
func SuisseMobileCheckURL(url string) int {
var id int
re := regexp.MustCompile(`https?://(?:www\.)?schweizmobil\.ch/[^/]+/tour/(\d+)`)
matches := re.FindStringSubmatch(url)
if len(matches) > 1 {
id, _ = strconv.Atoi(matches[1])
}
return id
}
type SuisseMobileGeometry struct {
Segments [][][]float64 `json:"coordinates"`
}
type SuisseMobileProperties struct {
Name string `json:"name"`
}
type SuisseMobileResponse struct {
Properties SuisseMobileProperties `json:"properties"`
Geometry SuisseMobileGeometry `json:"geometry"`
}
func SuisseMobileFetch(id int) (gpx.GPX, error) {
var gpxFile gpx.GPX
var gpxTrack gpx.GPXTrack
sess := requests.New(
requests.URL("https://schweizmobil.ch"),
)
resp, err := sess.DoRequest(
context.Background(),
requests.Path(fmt.Sprintf(
"/api/6/tracks/%d", id,
)),
)
if err != nil {
return gpxFile, err
}
var response SuisseMobileResponse
err = json.Unmarshal(resp.Content.Bytes(), &response)
if err != nil {
return gpxFile, err
}
gpxFile.Name = response.Properties.Name
for _, segments := range response.Geometry.Segments {
var gpxTrackSegment gpx.GPXTrackSegment
for _, point := range segments {
var gpxPoint gpx.GPXPoint
gpxPoint.Longitude = point[0]
gpxPoint.Latitude = point[1]
gpxPoint.Elevation = *gpx.NewNullableFloat64(point[2])
gpxTrackSegment.AppendPoint(&gpxPoint)
}
gpxTrack.AppendSegment(&gpxTrackSegment)
}
gpxFile.AppendTrack(&gpxTrack)
return gpxFile, nil
}

53
routes/fetch.go Normal file
View file

@ -0,0 +1,53 @@
package routes
import (
"fmt"
"git.readonly.ch/bouzoure/gpx-downloader/providers"
"github.com/gofiber/fiber/v2"
"github.com/tkrajina/gpxgo/gpx"
)
func Fetch(c *fiber.Ctx) error {
sourceUrl := c.FormValue("url")
if len(sourceUrl) <= 0 {
c.SendStatus(422)
return c.SendString("Error: URL is empty")
}
var gpxFile gpx.GPX
var err error
var filename string
// Check if link is Suisse Mobile
suisseMobileId := providers.SuisseMobileCheckURL(sourceUrl)
if suisseMobileId > 0 {
filename = fmt.Sprintf(
"suisse_mobile_%d",
suisseMobileId,
)
gpxFile, err = providers.SuisseMobileFetch(suisseMobileId)
if err != nil {
return err
}
}
if len(filename) <= 0 {
c.SendStatus(422)
return c.SendString("Error: URL is not supported")
}
xml, err := gpxFile.ToXml(gpx.ToXmlParams{})
if err != nil {
return err
}
c.Set("Content-Type", "octet-stream")
c.Set("Content-Disposition", fmt.Sprintf(
"attechment,filename=%s.gpx",
filename,
))
return c.Send(xml)
}

13
routes/index.go Normal file
View file

@ -0,0 +1,13 @@
package routes
import "github.com/gofiber/fiber/v2"
func Index(c *fiber.Ctx) error {
c.Set("Content-Type", "text/html")
return c.SendString(`
<form method="post" action="/fetch">
<input name="url" required />
<button type="submit">Fetch</button>
</form>
`)
}