chore: automated housekeeping...

This commit is contained in:
Pieter Vander Vennet 2025-04-15 18:18:44 +02:00
parent 79b6927b56
commit 42ded4c1b1
328 changed files with 4062 additions and 1284 deletions

View file

@ -132,7 +132,7 @@ export default class AllImageProviders {
const singleSource = tags.bindD((tags) => imageProvider.getRelevantUrls(tags, prefixes))
allSources.push(singleSource)
}
const source = Stores.fromStoresArray(allSources).map(result => {
const source = Stores.fromStoresArray(allSources).map((result) => {
const all = [].concat(...result)
return Utils.DedupOnId(all, (i) => i?.id ?? i?.url)
})

View file

@ -24,12 +24,12 @@ export interface ProvidedImage {
}
export interface PanoramaView {
url: string,
url: string
/**
* 0 - 359
* Degrees in which the picture is taken, with north = 0; going clockwise
*/
northOffset?: number,
northOffset?: number
pitchOffset?: number
}
@ -54,7 +54,6 @@ export interface HotspotProperties {
pitch: number | "auto"
gotoPanorama: Feature<Point, PanoramaView>
}
export default abstract class ImageProvider {
@ -125,7 +124,9 @@ export default abstract class ImageProvider {
public abstract apiUrls(): string[]
public abstract getPanoramaInfo(image: { id: string }): Promise<Feature<Point, PanoramaView>> | undefined;
public abstract getPanoramaInfo(image: {
id: string
}): Promise<Feature<Point, PanoramaView>> | undefined
public static async offerImageAsDownload(image: ProvidedImage) {
const response = await fetch(image.url_hd ?? image.url)
@ -134,5 +135,4 @@ export default abstract class ImageProvider {
mimetype: "image/jpg",
})
}
}

View file

@ -36,12 +36,16 @@ export class ImageUploadManager {
* Keeps track of the _features_ for which an upload failed. Only used to give an indication to the user.
* Every time an image upload fails, the featureID is added to the list. Not persisted (and should not be)
*/
private readonly _fails: UIEventSource<ImageUploadArguments[]> = new UIEventSource<ImageUploadArguments[]>([])
public readonly fails: Store<string[]> = this._fails.map(args => args.map(a => a.featureId))
private readonly _fails: UIEventSource<ImageUploadArguments[]> = new UIEventSource<
ImageUploadArguments[]
>([])
public readonly fails: Store<string[]> = this._fails.map((args) => args.map((a) => a.featureId))
/**
* FeatureIDs of queued items
*/
public readonly queued: Store<string[]> = this._queue.imagesInQueue.map(queue => queue.map(q => q.featureId))
public readonly queued: Store<string[]> = this._queue.imagesInQueue.map((queue) =>
queue.map((q) => q.featureId)
)
public readonly queuedArgs = this._queue.imagesInQueue
/**
* The feature for which an upload is currently running
@ -79,7 +83,7 @@ export class ImageUploadManager {
if (sizeInBytes > this._uploader.maxFileSizeInMegabytes * 1000000) {
const error = Translations.t.image.toBig.Subs({
actual_size: Math.floor(sizeInBytes / 1000000) + "MB",
max_size: this._uploader.maxFileSizeInMegabytes + "MB"
max_size: this._uploader.maxFileSizeInMegabytes + "MB",
})
return { error }
}
@ -118,7 +122,6 @@ export class ImageUploadManager {
const tags: OsmTags = tagsStore.data
const featureId = <OsmId | NoteId>tags.id
const author = this._osmConnection?.userDetails?.data?.name ?? "Anonymous" // Might be a note upload
/**
@ -134,13 +137,16 @@ export class ImageUploadManager {
location,
date: new Date().getTime(),
layoutId: this._theme.id,
author, blob: file, featureId, noblur, targetKey
author,
blob: file,
featureId,
noblur,
targetKey,
}
console.log("Args are", args)
this._queue.add(args)
this.uploadQueue()
}
/**
@ -201,23 +207,29 @@ export class ImageUploadManager {
this._fails.ping()
return
}
this._fails.set(this._fails.data.filter(a => a !== args))
let properties: UIEventSource<Record<string, string>> = this._featureProperties.getStore(args.featureId)
this._fails.set(this._fails.data.filter((a) => a !== args))
let properties: UIEventSource<Record<string, string>> = this._featureProperties.getStore(
args.featureId
)
if (args.featureId.startsWith("note/")) {
// This is an OSM-note
const url = result.absoluteUrl
await this._osmConnection.addCommentToNote(args.featureId, url)
const properties: UIEventSource<Record<string, string>> = this._featureProperties.getStore(args.featureId)
const properties: UIEventSource<Record<string, string>> =
this._featureProperties.getStore(args.featureId)
if (properties) {
// Properties will not be defined if the note isn't loaded, but that is no problem as the below code is only relevant if the note is shown
NoteCommentElement.addCommentTo(url, properties, {
osmConnection: this._osmConnection
osmConnection: this._osmConnection,
})
}
} else {
if (properties === undefined) {
const downloaded = await new OsmObjectDownloader(this._osmConnection.Backend(), this._changes).DownloadObjectAsync(args.featureId)
const downloaded = await new OsmObjectDownloader(
this._osmConnection.Backend(),
this._changes
).DownloadObjectAsync(args.featureId)
if (downloaded === "deleted") {
this._queue.delete(args)
return
@ -232,7 +244,7 @@ export class ImageUploadManager {
properties,
{
theme: properties?.data?.["_orig_theme"] ?? this._theme.id,
changeType: "add-image"
changeType: "add-image",
}
)
await this._changes.applyAction(action)
@ -240,7 +252,6 @@ export class ImageUploadManager {
}
this._queue.delete(args)
}
/**
@ -259,23 +270,15 @@ export class ImageUploadManager {
* @private
*/
private async attemptSingleUpload(
{
featureId,
author,
blob,
targetKey,
noblur,
location
}: ImageUploadArguments,
{ featureId, author, blob, targetKey, noblur, location }: ImageUploadArguments,
reportOnFail: boolean
): Promise<UploadResult | undefined> {
let key: string
let value: string
let absoluteUrl: string
try {
({ key, value, absoluteUrl } = await this._uploader.uploadImage(
;({ key, value, absoluteUrl } = await this._uploader.uploadImage(
blob,
location,
author,
@ -284,14 +287,13 @@ export class ImageUploadManager {
} catch (e) {
console.error("Could again not upload image due to", e)
if (reportOnFail) {
await this._reportError(
e,
JSON.stringify({
ctx: "While uploading an image in the Image Upload Manager",
featureId,
author,
targetKey
targetKey,
})
)
}
@ -304,5 +306,4 @@ export class ImageUploadManager {
}
return { key, absoluteUrl, value }
}
}

View file

@ -2,12 +2,12 @@ import { IdbLocalStorage } from "../Web/IdbLocalStorage"
import { Store, UIEventSource } from "../UIEventSource"
export interface ImageUploadArguments {
featureId: string,
readonly author: string,
readonly blob: File,
readonly targetKey: string | undefined,
readonly noblur: boolean,
readonly location: [number, number],
featureId: string
readonly author: string
readonly blob: File
readonly targetKey: string | undefined
readonly noblur: boolean
readonly location: [number, number]
readonly layoutId: string
readonly date: number
}
@ -17,14 +17,15 @@ export interface ImageUploadArguments {
* It is backed up in the indexedDB as to not drop images in case of connection problems
*/
export default class ImageUploadQueue {
public static readonly singleton = new ImageUploadQueue()
private readonly _imagesInQueue: UIEventSource<ImageUploadArguments[]>
public readonly imagesInQueue: Store<ImageUploadArguments[]>
private constructor() {
this._imagesInQueue = IdbLocalStorage.Get<ImageUploadArguments[]>("failed-images-backup", { defaultValue: [] })
this._imagesInQueue = IdbLocalStorage.Get<ImageUploadArguments[]>("failed-images-backup", {
defaultValue: [],
})
this.imagesInQueue = this._imagesInQueue
}
@ -44,7 +45,6 @@ export default class ImageUploadQueue {
}
applyRemapping(oldId: string, newId: string) {
let hasChange = false
for (const img of this._imagesInQueue.data) {
if (img.featureId === oldId) {

View file

@ -32,7 +32,7 @@ export class Imgur extends ImageProvider {
key: key,
provider: this,
id: value,
isSpherical: false
isSpherical: false,
},
]
}

View file

@ -17,7 +17,7 @@ export class Mapillary extends ImageProvider {
"http://mapillary.com",
"https://mapillary.com",
"http://www.mapillary.com",
"https://www.mapillary.com"
"https://www.mapillary.com",
]
defaultKeyPrefixes = ["mapillary", "image"]
@ -70,7 +70,7 @@ export class Mapillary extends ImageProvider {
lat: location?.lat,
lng: location?.lon,
z: location === undefined ? undefined : Math.max((zoom ?? 2) - 1, 1),
pKey
pKey,
}
const baselink = `https://www.mapillary.com/app/?`
const paramsStr = Utils.NoNull(
@ -140,41 +140,39 @@ export class Mapillary extends ImageProvider {
return [img]
}
/**
* Download data necessary for the 360°-viewer
* @param pkey
* @constructor
*/
public async getPanoramaInfo(image: { id: number | string }): Promise<Feature<Point, PanoramaView>> {
public async getPanoramaInfo(image: {
id: number | string
}): Promise<Feature<Point, PanoramaView>> {
const pkey = image.id
const metadataUrl =
"https://graph.mapillary.com/" +
pkey +
"?fields=computed_compass_angle,geometry,is_pano,thumb_2048_url,thumb_original_url&access_token=" +
Constants.mapillary_client_token_v4
const response = await Utils.downloadJsonCached<
{
computed_compass_angle: number,
geometry: Point,
const response = await Utils.downloadJsonCached<{
computed_compass_angle: number
geometry: Point
is_pano: boolean,
thumb_2048_url: string,
thumb_original_url: string,
id: string,
}>(metadataUrl, 60 * 60)
is_pano: boolean
thumb_2048_url: string
thumb_original_url: string
id: string
}>(metadataUrl, 60 * 60)
return {
type: "Feature",
geometry: response.geometry,
properties: {
url: response.thumb_2048_url,
northOffset: response.computed_compass_angle
}
northOffset: response.computed_compass_angle,
},
}
}
public async DownloadAttribution(providedImage: { id: string }): Promise<LicenseInfo> {
const mapillaryId = providedImage.id
const metadataUrl =
@ -183,7 +181,10 @@ export class Mapillary extends ImageProvider {
"?fields=thumb_1024_url,thumb_original_url,captured_at,creator&access_token=" +
Constants.mapillary_client_token_v4
const response = await Utils.downloadJsonCached<{
thumb_1024_url: string, thumb_original_url: string, captured_at, creator: string
thumb_1024_url: string
thumb_original_url: string
captured_at
creator: string
}>(metadataUrl, 60 * 60)
const license = new LicenseInfo()
@ -207,13 +208,13 @@ export class Mapillary extends ImageProvider {
"?fields=thumb_1024_url,thumb_original_url,captured_at,compass_angle,geometry,computed_geometry,creator,camera_type&access_token=" +
Constants.mapillary_client_token_v4
const response = await Utils.downloadJsonCached<{
thumb_1024_url: string,
thumb_original_url: string,
captured_at,
compass_angle: number,
creator: string,
computed_geometry: Point,
geometry: Point,
thumb_1024_url: string
thumb_original_url: string
captured_at
compass_angle: number
creator: string
computed_geometry: Point
geometry: Point
camera_type: "equirectangular" | "spherical" | string
}>(metadataUrl, 60 * 60)
const url = <string>response["thumb_1024_url"]
@ -230,9 +231,10 @@ export class Mapillary extends ImageProvider {
date,
key,
rotation,
isSpherical: response.camera_type === "spherical" || response.camera_type === "equirectangular",
isSpherical:
response.camera_type === "spherical" || response.camera_type === "equirectangular",
lat: geometry.coordinates[1],
lon: geometry.coordinates[0]
lon: geometry.coordinates[0],
}
}
}

View file

@ -191,7 +191,9 @@ export default class PanoramaxImageProvider extends ImageProvider {
return new Panoramax(host)
}
public async getPanoramaInfo(image: { id: string }): Promise<Feature<Point, PanoramaView>> | undefined {
public async getPanoramaInfo(image: {
id: string
}): Promise<Feature<Point, PanoramaView>> | undefined {
const imageInfo = await PanoramaxImageProvider.xyz.imageInfo(image.id)
const url = (imageInfo.assets.sd ?? imageInfo.assets.thumb ?? imageInfo.assets.hd).href
const northOffset = imageInfo.properties["view:azimuth"]
@ -200,8 +202,10 @@ export default class PanoramaxImageProvider extends ImageProvider {
type: "Feature",
geometry: imageInfo.geometry,
properties: {
url, northOffset, pitchOffset
}
url,
northOffset,
pitchOffset,
},
}
}
}

View file

@ -8,14 +8,12 @@ import { Utils } from "../../Utils"
import { Feature, Point } from "geojson"
export class WikidataImageProvider extends ImageProvider {
public static readonly singleton = new WikidataImageProvider()
public readonly defaultKeyPrefixes = ["wikidata"]
public readonly name = "Wikidata"
private static readonly keyBlacklist: ReadonlySet<string> = new Set([
"mapillary",
...Utils.Times((i) => "mapillary:" + i, 10)
...Utils.Times((i) => "mapillary:" + i, 10),
])
private constructor() {

View file

@ -189,7 +189,7 @@ export class WikimediaImageProvider extends ImageProvider {
key: undefined,
provider: this,
id: image,
isSpherical: false
isSpherical: false,
}
}