Frontend UX improvements #37
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
|
|
||||||
@ -15,17 +17,21 @@ class CurrentTripLoadingIndicator extends StatefulWidget {
|
|||||||
State<CurrentTripLoadingIndicator> createState() => _CurrentTripLoadingIndicatorState();
|
State<CurrentTripLoadingIndicator> createState() => _CurrentTripLoadingIndicatorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CurrentTripLoadingIndicatorState extends State<CurrentTripLoadingIndicator> {
|
|
||||||
@override
|
Widget bottomLoadingIndicator = Container(
|
||||||
Widget build(BuildContext context) => Center(
|
height: 20.0, // Increase the height to take up more vertical space
|
||||||
child: FutureBuilder(
|
|
||||||
future: widget.trip.cityName,
|
child: ImageFiltered(
|
||||||
|
imageFilter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), // Apply blur effect
|
||||||
|
child: Padding(padding: EdgeInsets.all(10), child: CircularProgressIndicator(),)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Widget loadingText(Trip trip) => FutureBuilder(
|
||||||
|
future: trip.cityName,
|
||||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||||
Widget greeter;
|
Widget greeter;
|
||||||
Widget loadingIndicator = const Padding(
|
|
||||||
padding: EdgeInsets.only(top: 10),
|
|
||||||
child: CircularProgressIndicator()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
greeter = AutoSizeText(
|
greeter = AutoSizeText(
|
||||||
@ -47,14 +53,24 @@ class _CurrentTripLoadingIndicatorState extends State<CurrentTripLoadingIndicato
|
|||||||
style: greeterStyle,
|
style: greeterStyle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Column(
|
return greeter;
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class _CurrentTripLoadingIndicatorState extends State<CurrentTripLoadingIndicator> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
greeter,
|
Center(child: loadingText(widget.trip)),
|
||||||
loadingIndicator,
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: bottomLoadingIndicator,
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
@ -36,7 +36,7 @@ class _CurrentTripPanelState extends State<CurrentTripPanel> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
// reuse the exact same height as the panel has when collapsed
|
// reuse the exact same height as the panel has when collapsed
|
||||||
// this way the greeter will be centered when the panel is collapsed
|
// this way the greeter will be centered when the panel is collapsed
|
||||||
height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT - 20,
|
height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT,
|
||||||
child: CurrentTripErrorMessage(trip: widget.trip)
|
child: CurrentTripErrorMessage(trip: widget.trip)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -46,19 +46,20 @@ class _CurrentTripPanelState extends State<CurrentTripPanel> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
// reuse the exact same height as the panel has when collapsed
|
// reuse the exact same height as the panel has when collapsed
|
||||||
// this way the greeter will be centered when the panel is collapsed
|
// this way the greeter will be centered when the panel is collapsed
|
||||||
height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT - 20,
|
height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT,
|
||||||
child: CurrentTripLoadingIndicator(trip: widget.trip),
|
child: CurrentTripLoadingIndicator(trip: widget.trip),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return ListView(
|
return ListView(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
padding: const EdgeInsets.only(bottom: 30),
|
padding: const EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 30),
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
// reuse the exact same height as the panel has when collapsed
|
// reuse the exact same height as the panel has when collapsed
|
||||||
// this way the greeter will be centered when the panel is collapsed
|
// this way the greeter will be centered when the panel is collapsed
|
||||||
height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT - 20,
|
// note that we need to account for the padding above
|
||||||
|
height: MediaQuery.of(context).size.height * TRIP_PANEL_MIN_HEIGHT - 10,
|
||||||
child: CurrentTripGreeter(trip: widget.trip),
|
child: CurrentTripGreeter(trip: widget.trip),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -38,8 +38,6 @@ class _LandmarkCardState extends State<LandmarkCard> {
|
|||||||
imageUrl: widget.landmark.imageURL ?? '',
|
imageUrl: widget.landmark.imageURL ?? '',
|
||||||
placeholder: (context, url) => Center(child: CircularProgressIndicator()),
|
placeholder: (context, url) => Center(child: CircularProgressIndicator()),
|
||||||
errorWidget: (context, error, stackTrace) => Icon(Icons.question_mark_outlined),
|
errorWidget: (context, error, stackTrace) => Icon(Icons.question_mark_outlined),
|
||||||
// TODO: make this a switch statement to load a placeholder if null
|
|
||||||
// cover the whole container meaning the image will be cropped
|
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -103,15 +101,15 @@ class _LandmarkCardState extends State<LandmarkCard> {
|
|||||||
icon: Icon(Icons.link),
|
icon: Icon(Icons.link),
|
||||||
label: Text('Website'),
|
label: Text('Website'),
|
||||||
),
|
),
|
||||||
if (widget.landmark.wikipediaURL != null)
|
// if (widget.landmark.wikipediaURL != null)
|
||||||
TextButton.icon(
|
// TextButton.icon(
|
||||||
onPressed: () async {
|
// onPressed: () async {
|
||||||
// open a browser with the wikipedia link
|
// // open a browser with the wikipedia link
|
||||||
await launchUrl(Uri.parse(widget.landmark.wikipediaURL!));
|
// await launchUrl(Uri.parse(widget.landmark.wikipediaURL!));
|
||||||
},
|
// },
|
||||||
icon: Icon(Icons.book),
|
// icon: Icon(Icons.book),
|
||||||
label: Text('Wikipedia'),
|
// label: Text('Wikipedia'),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -9,6 +9,15 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
const Map<String, List> debugLocations = {
|
||||||
|
'paris': [48.8575, 2.3514],
|
||||||
|
'london': [51.5074, -0.1278],
|
||||||
|
'new york': [40.7128, -74.0060],
|
||||||
|
'tokyo': [35.6895, 139.6917],
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NewTripLocationSearch extends StatefulWidget {
|
class NewTripLocationSearch extends StatefulWidget {
|
||||||
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
|
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
|
||||||
Trip trip;
|
Trip trip;
|
||||||
@ -27,26 +36,35 @@ class _NewTripLocationSearchState extends State<NewTripLocationSearch> {
|
|||||||
|
|
||||||
setTripLocation (String query) async {
|
setTripLocation (String query) async {
|
||||||
List<Location> locations = [];
|
List<Location> locations = [];
|
||||||
|
Location startLocation;
|
||||||
log('Searching for: $query');
|
log('Searching for: $query');
|
||||||
|
if (GeocodingPlatform.instance != null) {
|
||||||
try{
|
locations.addAll(await locationFromAddress(query));
|
||||||
locations = await locationFromAddress(query);
|
|
||||||
} catch (e) {
|
|
||||||
log('No results found for: $query : $e');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (locations.isNotEmpty) {
|
if (locations.isNotEmpty) {
|
||||||
Location location = locations.first;
|
startLocation = locations.first;
|
||||||
|
} else {
|
||||||
|
log('No results found for: $query. Is geocoding available?');
|
||||||
|
log('Setting Fallback location');
|
||||||
|
List coordinates = debugLocations[query.toLowerCase()] ?? [48.8575, 2.3514];
|
||||||
|
startLocation = Location(
|
||||||
|
latitude: coordinates[0],
|
||||||
|
longitude: coordinates[1],
|
||||||
|
timestamp: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
widget.trip.landmarks.clear();
|
widget.trip.landmarks.clear();
|
||||||
widget.trip.addLandmark(
|
widget.trip.addLandmark(
|
||||||
Landmark(
|
Landmark(
|
||||||
uuid: 'pending',
|
uuid: 'pending',
|
||||||
name: query,
|
name: query,
|
||||||
location: [location.latitude, location.longitude],
|
location: [startLocation.latitude, startLocation.longitude],
|
||||||
type: typeStart
|
type: typeStart
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
late Widget locationSearchBar = SearchBar(
|
late Widget locationSearchBar = SearchBar(
|
||||||
|
@ -26,7 +26,7 @@ class _NewTripMapState extends State<NewTripMap> {
|
|||||||
target: LatLng(48.8566, 2.3522),
|
target: LatLng(48.8566, 2.3522),
|
||||||
zoom: 11.0,
|
zoom: 11.0,
|
||||||
);
|
);
|
||||||
late GoogleMapController _mapController;
|
GoogleMapController? _mapController;
|
||||||
final Set<Marker> _markers = <Marker>{};
|
final Set<Marker> _markers = <Marker>{};
|
||||||
|
|
||||||
_onLongPress(LatLng location) {
|
_onLongPress(LatLng location) {
|
||||||
@ -56,11 +56,15 @@ class _NewTripMapState extends State<NewTripMap> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
_mapController.moveCamera(
|
// check if the controller is ready
|
||||||
|
|
||||||
|
if (_mapController != null) {
|
||||||
|
_mapController!.animateCamera(
|
||||||
CameraUpdate.newLatLng(
|
CameraUpdate.newLatLng(
|
||||||
LatLng(landmark.location[0], landmark.location[1])
|
LatLng(landmark.location[0], landmark.location[1])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class _TripPageState extends State<TripPage> {
|
|||||||
maxHeight: MediaQuery.of(context).size.height * TRIP_PANEL_MAX_HEIGHT,
|
maxHeight: MediaQuery.of(context).size.height * TRIP_PANEL_MAX_HEIGHT,
|
||||||
// padding in this context is annoying: it offsets the notion of vertical alignment.
|
// padding in this context is annoying: it offsets the notion of vertical alignment.
|
||||||
// children that want to be centered vertically need to have their size adjusted by 2x the padding
|
// children that want to be centered vertically need to have their size adjusted by 2x the padding
|
||||||
padding: const EdgeInsets.all(10.0),
|
// padding: const EdgeInsets.all(10.0),
|
||||||
// Panel snapping should not be disabled because it significantly improves the user experience
|
// Panel snapping should not be disabled because it significantly improves the user experience
|
||||||
// panelSnapping: false
|
// panelSnapping: false
|
||||||
borderRadius: const BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)),
|
borderRadius: const BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)),
|
||||||
|
@ -24,8 +24,7 @@ final class Landmark extends LinkedListEntry<Landmark>{
|
|||||||
// description to be shown in the overview
|
// description to be shown in the overview
|
||||||
final String? nameEN;
|
final String? nameEN;
|
||||||
final String? websiteURL;
|
final String? websiteURL;
|
||||||
final String? wikipediaURL;
|
String? imageURL; // not final because it can be patched
|
||||||
final String? imageURL;
|
|
||||||
final String? description;
|
final String? description;
|
||||||
final Duration? duration;
|
final Duration? duration;
|
||||||
final bool? visited;
|
final bool? visited;
|
||||||
@ -44,7 +43,6 @@ final class Landmark extends LinkedListEntry<Landmark>{
|
|||||||
|
|
||||||
this.nameEN,
|
this.nameEN,
|
||||||
this.websiteURL,
|
this.websiteURL,
|
||||||
this.wikipediaURL,
|
|
||||||
this.imageURL,
|
this.imageURL,
|
||||||
this.description,
|
this.description,
|
||||||
this.duration,
|
this.duration,
|
||||||
@ -70,7 +68,6 @@ final class Landmark extends LinkedListEntry<Landmark>{
|
|||||||
final isSecondary = json['is_secondary'] as bool?;
|
final isSecondary = json['is_secondary'] as bool?;
|
||||||
final nameEN = json['name_en'] as String?;
|
final nameEN = json['name_en'] as String?;
|
||||||
final websiteURL = json['website_url'] as String?;
|
final websiteURL = json['website_url'] as String?;
|
||||||
final wikipediaURL = json['wikipedia_url'] as String?;
|
|
||||||
final imageURL = json['image_url'] as String?;
|
final imageURL = json['image_url'] as String?;
|
||||||
final description = json['description'] as String?;
|
final description = json['description'] as String?;
|
||||||
var duration = Duration(minutes: json['duration'] ?? 0) as Duration?;
|
var duration = Duration(minutes: json['duration'] ?? 0) as Duration?;
|
||||||
@ -85,7 +82,6 @@ final class Landmark extends LinkedListEntry<Landmark>{
|
|||||||
isSecondary: isSecondary,
|
isSecondary: isSecondary,
|
||||||
nameEN: nameEN,
|
nameEN: nameEN,
|
||||||
websiteURL: websiteURL,
|
websiteURL: websiteURL,
|
||||||
wikipediaURL: wikipediaURL,
|
|
||||||
imageURL: imageURL,
|
imageURL: imageURL,
|
||||||
description: description,
|
description: description,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
@ -112,7 +108,6 @@ final class Landmark extends LinkedListEntry<Landmark>{
|
|||||||
'is_secondary': isSecondary,
|
'is_secondary': isSecondary,
|
||||||
'name_en': nameEN,
|
'name_en': nameEN,
|
||||||
'website_url': websiteURL,
|
'website_url': websiteURL,
|
||||||
'wikipedia_url': wikipediaURL,
|
|
||||||
'image_url': imageURL,
|
'image_url': imageURL,
|
||||||
'description': description,
|
'description': description,
|
||||||
'duration': duration?.inMinutes,
|
'duration': duration?.inMinutes,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import "dart:convert";
|
import "dart:convert";
|
||||||
import "dart:developer";
|
import "dart:developer";
|
||||||
|
import "package:anyway/utils/load_landmark_image.dart";
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
import 'package:anyway/constants.dart';
|
import 'package:anyway/constants.dart';
|
||||||
@ -85,6 +86,15 @@ fetchTrip(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
patchLandmarkImage(Landmark landmark) async {
|
||||||
|
// patch the landmark to include an image from an external source
|
||||||
|
if (landmark.imageURL == null) {
|
||||||
|
String? newUrl = await getImageUrlFromName(landmark.name);
|
||||||
|
if (newUrl != null) {
|
||||||
|
landmark.imageURL = newUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<(Landmark, String?)> fetchLandmark(String uuid) async {
|
Future<(Landmark, String?)> fetchLandmark(String uuid) async {
|
||||||
final response = await dio.get(
|
final response = await dio.get(
|
||||||
@ -101,5 +111,7 @@ Future<(Landmark, String?)> fetchLandmark(String uuid) async {
|
|||||||
log(response.data.toString());
|
log(response.data.toString());
|
||||||
Map<String, dynamic> json = response.data;
|
Map<String, dynamic> json = response.data;
|
||||||
String? nextUUID = json["next_uuid"];
|
String? nextUUID = json["next_uuid"];
|
||||||
return (Landmark.fromJson(json), nextUUID);
|
Landmark landmark = Landmark.fromJson(json);
|
||||||
|
patchLandmarkImage(landmark);
|
||||||
|
return (landmark, nextUUID);
|
||||||
}
|
}
|
||||||
|
60
frontend/lib/utils/load_landmark_image.dart
Normal file
60
frontend/lib/utils/load_landmark_image.dart
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:fuzzywuzzy/model/extracted_result.dart';
|
||||||
|
|
||||||
|
const String baseUrl = "https://en.wikipedia.org/w/api.php";
|
||||||
|
final Dio dio = Dio();
|
||||||
|
|
||||||
|
Future<int?> bestPageMatch(String title) async {
|
||||||
|
final response = await dio.get(baseUrl, queryParameters: {
|
||||||
|
"action": "query",
|
||||||
|
"format": "json",
|
||||||
|
"list": "prefixsearch",
|
||||||
|
"pssearch": title,
|
||||||
|
});
|
||||||
|
|
||||||
|
final data = jsonDecode(response.toString());
|
||||||
|
log(data.toString());
|
||||||
|
final List<dynamic> results = data["query"]["prefixsearch"] ?? {};
|
||||||
|
final Map<String, int> titlesAndIds = {
|
||||||
|
for (var d in results) d["title"]: d["pageid"]
|
||||||
|
};
|
||||||
|
if (titlesAndIds.isEmpty) {
|
||||||
|
log("No pages found for $title");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// after the empty check, we can safely assume that there is a best match
|
||||||
|
final ExtractedResult<String> bestMatch = extractOne(
|
||||||
|
query: title,
|
||||||
|
choices: titlesAndIds.keys.toList(),
|
||||||
|
cutoff: 70,
|
||||||
|
);
|
||||||
|
return titlesAndIds[bestMatch.choice];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getImageUrl(int pageId) async {
|
||||||
|
final response = await dio.get(baseUrl, queryParameters: {
|
||||||
|
"action": "query",
|
||||||
|
"format": "json",
|
||||||
|
"prop": "pageimages",
|
||||||
|
"pageids": pageId,
|
||||||
|
"pithumbsize": 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
final data = jsonDecode(response.toString());
|
||||||
|
final pageData = data["query"]["pages"][pageId.toString()];
|
||||||
|
return pageData["thumbnail"]?["source"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getImageUrlFromName(String title) async {
|
||||||
|
int? pageId = await bestPageMatch(title);
|
||||||
|
if (pageId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return await getImageUrl(pageId);
|
||||||
|
}
|
@ -232,6 +232,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
fuzzywuzzy:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fuzzywuzzy
|
||||||
|
sha256: "3004379ffd6e7f476a0c2091f38f16588dc45f67de7adf7c41aa85dec06b432c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
geocoding:
|
geocoding:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -51,6 +51,7 @@ dependencies:
|
|||||||
flutter_launcher_icons: ^0.13.1
|
flutter_launcher_icons: ^0.13.1
|
||||||
permission_handler: ^11.3.1
|
permission_handler: ^11.3.1
|
||||||
geolocator: ^13.0.1
|
geolocator: ^13.0.1
|
||||||
|
fuzzywuzzy: ^1.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
50
testing_image_query.py
Normal file
50
testing_image_query.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import httpx
|
||||||
|
import json
|
||||||
|
|
||||||
|
base_url = "https://en.wikipedia.org/w/api.php"
|
||||||
|
|
||||||
|
def best_page_match(title) -> int:
|
||||||
|
params = {
|
||||||
|
"action": "query",
|
||||||
|
"format": "json",
|
||||||
|
"list": "prefixsearch",
|
||||||
|
"pssearch": title,
|
||||||
|
}
|
||||||
|
response = httpx.get(base_url, params=params)
|
||||||
|
data = response.json()
|
||||||
|
data = data.get("query", {}).get("prefixsearch", [])
|
||||||
|
titles_and_ids = {d["title"]: d["pageid"] for d in data}
|
||||||
|
|
||||||
|
for t in titles_and_ids:
|
||||||
|
if title.lower() == t.lower():
|
||||||
|
print("Matched")
|
||||||
|
return titles_and_ids[t]
|
||||||
|
|
||||||
|
def get_image_url(page_id) -> str:
|
||||||
|
# https://en.wikipedia.org/w/api.php?action=query&titles=K%C3%B6lner%20Dom&prop=imageinfo&iiprop=url&format=json
|
||||||
|
params = {
|
||||||
|
"action": "query",
|
||||||
|
"format": "json",
|
||||||
|
"prop": "pageimages",
|
||||||
|
"pageids": page_id,
|
||||||
|
"pithumbsize": 500,
|
||||||
|
}
|
||||||
|
response = httpx.get(base_url, params=params)
|
||||||
|
data = response.json()
|
||||||
|
data = data.get("query", {}).get("pages", {})
|
||||||
|
data = data.get(str(page_id), {})
|
||||||
|
return data.get("thumbnail", {}).get("source")
|
||||||
|
|
||||||
|
def get_image_url_from_title(title) -> str:
|
||||||
|
page_id = best_page_match(title)
|
||||||
|
if page_id is None:
|
||||||
|
return None
|
||||||
|
return get_image_url(page_id)
|
||||||
|
|
||||||
|
|
||||||
|
print(get_image_url_from_title("kölner dom"))
|
||||||
|
print(get_image_url_from_title("grossmünster"))
|
||||||
|
print(get_image_url_from_title("eiffel tower"))
|
||||||
|
print(get_image_url_from_title("taj mahal"))
|
||||||
|
print(get_image_url_from_title("big ben"))
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user