forked from MapComplete/MapComplete
Reformat all files with prettier
This commit is contained in:
parent
e22d189376
commit
b541d3eab4
382 changed files with 50893 additions and 35566 deletions
|
@ -1,45 +1,52 @@
|
|||
import {Mapillary} from "./Mapillary";
|
||||
import {WikimediaImageProvider} from "./WikimediaImageProvider";
|
||||
import {Imgur} from "./Imgur";
|
||||
import GenericImageProvider from "./GenericImageProvider";
|
||||
import {Store, UIEventSource} from "../UIEventSource";
|
||||
import ImageProvider, {ProvidedImage} from "./ImageProvider";
|
||||
import {WikidataImageProvider} from "./WikidataImageProvider";
|
||||
import { Mapillary } from "./Mapillary"
|
||||
import { WikimediaImageProvider } from "./WikimediaImageProvider"
|
||||
import { Imgur } from "./Imgur"
|
||||
import GenericImageProvider from "./GenericImageProvider"
|
||||
import { Store, UIEventSource } from "../UIEventSource"
|
||||
import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
||||
import { WikidataImageProvider } from "./WikidataImageProvider"
|
||||
|
||||
/**
|
||||
* A generic 'from the interwebz' image picker, without attribution
|
||||
*/
|
||||
export default class AllImageProviders {
|
||||
|
||||
public static ImageAttributionSource: ImageProvider[] = [
|
||||
Imgur.singleton,
|
||||
Mapillary.singleton,
|
||||
WikidataImageProvider.singleton,
|
||||
WikimediaImageProvider.singleton,
|
||||
new GenericImageProvider(
|
||||
[].concat(...Imgur.defaultValuePrefix, ...WikimediaImageProvider.commonsPrefixes, ...Mapillary.valuePrefixes)
|
||||
)
|
||||
[].concat(
|
||||
...Imgur.defaultValuePrefix,
|
||||
...WikimediaImageProvider.commonsPrefixes,
|
||||
...Mapillary.valuePrefixes
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
private static providersByName= {
|
||||
"imgur": Imgur.singleton,
|
||||
"mapillary": Mapillary.singleton,
|
||||
"wikidata": WikidataImageProvider.singleton,
|
||||
"wikimedia": WikimediaImageProvider.singleton
|
||||
private static providersByName = {
|
||||
imgur: Imgur.singleton,
|
||||
mapillary: Mapillary.singleton,
|
||||
wikidata: WikidataImageProvider.singleton,
|
||||
wikimedia: WikimediaImageProvider.singleton,
|
||||
}
|
||||
|
||||
public static byName(name: string){
|
||||
|
||||
public static byName(name: string) {
|
||||
return AllImageProviders.providersByName[name.toLowerCase()]
|
||||
}
|
||||
|
||||
public static defaultKeys = [].concat(AllImageProviders.ImageAttributionSource.map(provider => provider.defaultKeyPrefixes))
|
||||
public static defaultKeys = [].concat(
|
||||
AllImageProviders.ImageAttributionSource.map((provider) => provider.defaultKeyPrefixes)
|
||||
)
|
||||
|
||||
|
||||
private static _cache: Map<string, UIEventSource<ProvidedImage[]>> = new Map<string, UIEventSource<ProvidedImage[]>>()
|
||||
private static _cache: Map<string, UIEventSource<ProvidedImage[]>> = new Map<
|
||||
string,
|
||||
UIEventSource<ProvidedImage[]>
|
||||
>()
|
||||
|
||||
public static LoadImagesFor(tags: Store<any>, tagKey?: string[]): Store<ProvidedImage[]> {
|
||||
if (tags.data.id === undefined) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
const cacheKey = tags.data.id + tagKey
|
||||
|
@ -48,23 +55,21 @@ export default class AllImageProviders {
|
|||
return cached
|
||||
}
|
||||
|
||||
|
||||
const source = new UIEventSource([])
|
||||
this._cache.set(cacheKey, source)
|
||||
const allSources = []
|
||||
for (const imageProvider of AllImageProviders.ImageAttributionSource) {
|
||||
|
||||
let prefixes = imageProvider.defaultKeyPrefixes
|
||||
if (tagKey !== undefined) {
|
||||
prefixes = tagKey
|
||||
}
|
||||
|
||||
const singleSource = imageProvider.GetRelevantUrls(tags, {
|
||||
prefixes: prefixes
|
||||
prefixes: prefixes,
|
||||
})
|
||||
allSources.push(singleSource)
|
||||
singleSource.addCallbackAndRunD(_ => {
|
||||
const all: ProvidedImage[] = [].concat(...allSources.map(source => source.data))
|
||||
singleSource.addCallbackAndRunD((_) => {
|
||||
const all: ProvidedImage[] = [].concat(...allSources.map((source) => source.data))
|
||||
const uniq = []
|
||||
const seen = new Set<string>()
|
||||
for (const img of all) {
|
||||
|
@ -77,7 +82,6 @@ export default class AllImageProviders {
|
|||
source.setData(uniq)
|
||||
})
|
||||
}
|
||||
return source;
|
||||
return source
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
import ImageProvider, {ProvidedImage} from "./ImageProvider";
|
||||
import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
||||
|
||||
export default class GenericImageProvider extends ImageProvider {
|
||||
public defaultKeyPrefixes: string[] = ["image"];
|
||||
public defaultKeyPrefixes: string[] = ["image"]
|
||||
|
||||
private readonly _valuePrefixBlacklist: string[];
|
||||
private readonly _valuePrefixBlacklist: string[]
|
||||
|
||||
public constructor(valuePrefixBlacklist: string[]) {
|
||||
super();
|
||||
this._valuePrefixBlacklist = valuePrefixBlacklist;
|
||||
super()
|
||||
this._valuePrefixBlacklist = valuePrefixBlacklist
|
||||
}
|
||||
|
||||
async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
||||
|
||||
if (this._valuePrefixBlacklist.some(prefix => value.startsWith(prefix))) {
|
||||
if (this._valuePrefixBlacklist.some((prefix) => value.startsWith(prefix))) {
|
||||
return []
|
||||
}
|
||||
|
||||
|
@ -23,20 +22,20 @@ export default class GenericImageProvider extends ImageProvider {
|
|||
return []
|
||||
}
|
||||
|
||||
return [Promise.resolve({
|
||||
key: key,
|
||||
url: value,
|
||||
provider: this
|
||||
})]
|
||||
return [
|
||||
Promise.resolve({
|
||||
key: key,
|
||||
url: value,
|
||||
provider: this,
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
SourceIcon(backlinkSource?: string) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
public DownloadAttribution(url: string) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +1,53 @@
|
|||
import {Store, UIEventSource} from "../UIEventSource";
|
||||
import BaseUIElement from "../../UI/BaseUIElement";
|
||||
import {LicenseInfo} from "./LicenseInfo";
|
||||
import {Utils} from "../../Utils";
|
||||
import { Store, UIEventSource } from "../UIEventSource"
|
||||
import BaseUIElement from "../../UI/BaseUIElement"
|
||||
import { LicenseInfo } from "./LicenseInfo"
|
||||
import { Utils } from "../../Utils"
|
||||
|
||||
export interface ProvidedImage {
|
||||
url: string,
|
||||
key: string,
|
||||
url: string
|
||||
key: string
|
||||
provider: ImageProvider
|
||||
}
|
||||
|
||||
export default abstract class ImageProvider {
|
||||
|
||||
public abstract readonly defaultKeyPrefixes: string[]
|
||||
|
||||
public abstract SourceIcon(backlinkSource?: string): BaseUIElement;
|
||||
|
||||
public abstract SourceIcon(backlinkSource?: string): BaseUIElement
|
||||
|
||||
/**
|
||||
* Given a properies object, maps it onto _all_ the available pictures for this imageProvider
|
||||
*/
|
||||
public GetRelevantUrls(allTags: Store<any>, options?: {
|
||||
prefixes?: string[]
|
||||
}): UIEventSource<ProvidedImage[]> {
|
||||
public GetRelevantUrls(
|
||||
allTags: Store<any>,
|
||||
options?: {
|
||||
prefixes?: string[]
|
||||
}
|
||||
): UIEventSource<ProvidedImage[]> {
|
||||
const prefixes = options?.prefixes ?? this.defaultKeyPrefixes
|
||||
if (prefixes === undefined) {
|
||||
throw "No `defaultKeyPrefixes` defined by this image provider"
|
||||
}
|
||||
const relevantUrls = new UIEventSource<{ url: string; key: string; provider: ImageProvider }[]>([])
|
||||
const relevantUrls = new UIEventSource<
|
||||
{ url: string; key: string; provider: ImageProvider }[]
|
||||
>([])
|
||||
const seenValues = new Set<string>()
|
||||
allTags.addCallbackAndRunD(tags => {
|
||||
allTags.addCallbackAndRunD((tags) => {
|
||||
for (const key in tags) {
|
||||
if (!prefixes.some(prefix => key.startsWith(prefix))) {
|
||||
if (!prefixes.some((prefix) => key.startsWith(prefix))) {
|
||||
continue
|
||||
}
|
||||
const values = Utils.NoEmpty(tags[key]?.split(";")?.map(v => v.trim()) ?? [])
|
||||
const values = Utils.NoEmpty(tags[key]?.split(";")?.map((v) => v.trim()) ?? [])
|
||||
for (const value of values) {
|
||||
|
||||
if (seenValues.has(value)) {
|
||||
continue
|
||||
}
|
||||
seenValues.add(value)
|
||||
this.ExtractUrls(key, value).then(promises => {
|
||||
this.ExtractUrls(key, value).then((promises) => {
|
||||
for (const promise of promises ?? []) {
|
||||
if (promise === undefined) {
|
||||
continue
|
||||
}
|
||||
promise.then(providedImage => {
|
||||
promise.then((providedImage) => {
|
||||
if (providedImage === undefined) {
|
||||
return
|
||||
}
|
||||
|
@ -54,15 +57,12 @@ export default abstract class ImageProvider {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
return relevantUrls
|
||||
}
|
||||
|
||||
public abstract ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]>;
|
||||
public abstract ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]>
|
||||
|
||||
public abstract DownloadAttribution(url: string): Promise<LicenseInfo>;
|
||||
|
||||
}
|
||||
public abstract DownloadAttribution(url: string): Promise<LicenseInfo>
|
||||
}
|
||||
|
|
|
@ -1,93 +1,105 @@
|
|||
import ImageProvider, { ProvidedImage } from "./ImageProvider";
|
||||
import BaseUIElement from "../../UI/BaseUIElement";
|
||||
import {Utils} from "../../Utils";
|
||||
import Constants from "../../Models/Constants";
|
||||
import {LicenseInfo} from "./LicenseInfo";
|
||||
import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
||||
import BaseUIElement from "../../UI/BaseUIElement"
|
||||
import { Utils } from "../../Utils"
|
||||
import Constants from "../../Models/Constants"
|
||||
import { LicenseInfo } from "./LicenseInfo"
|
||||
|
||||
export class Imgur extends ImageProvider {
|
||||
|
||||
public static readonly defaultValuePrefix = ["https://i.imgur.com"]
|
||||
public static readonly singleton = new Imgur();
|
||||
public readonly defaultKeyPrefixes: string[] = ["image"];
|
||||
public static readonly singleton = new Imgur()
|
||||
public readonly defaultKeyPrefixes: string[] = ["image"]
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
super()
|
||||
}
|
||||
|
||||
static uploadMultiple(
|
||||
title: string, description: string, blobs: FileList,
|
||||
handleSuccessfullUpload: ((imageURL: string) => Promise<void>),
|
||||
allDone: (() => void),
|
||||
onFail: ((reason: string) => void),
|
||||
offset: number = 0) {
|
||||
|
||||
title: string,
|
||||
description: string,
|
||||
blobs: FileList,
|
||||
handleSuccessfullUpload: (imageURL: string) => Promise<void>,
|
||||
allDone: () => void,
|
||||
onFail: (reason: string) => void,
|
||||
offset: number = 0
|
||||
) {
|
||||
if (blobs.length == offset) {
|
||||
allDone();
|
||||
return;
|
||||
allDone()
|
||||
return
|
||||
}
|
||||
const blob = blobs.item(offset);
|
||||
const self = this;
|
||||
this.uploadImage(title, description, blob,
|
||||
const blob = blobs.item(offset)
|
||||
const self = this
|
||||
this.uploadImage(
|
||||
title,
|
||||
description,
|
||||
blob,
|
||||
async (imageUrl) => {
|
||||
await handleSuccessfullUpload(imageUrl);
|
||||
await handleSuccessfullUpload(imageUrl)
|
||||
self.uploadMultiple(
|
||||
title, description, blobs,
|
||||
title,
|
||||
description,
|
||||
blobs,
|
||||
handleSuccessfullUpload,
|
||||
allDone,
|
||||
onFail,
|
||||
offset + 1);
|
||||
offset + 1
|
||||
)
|
||||
},
|
||||
onFail
|
||||
);
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
static uploadImage(title: string, description: string, blob: File,
|
||||
handleSuccessfullUpload: ((imageURL: string) => Promise<void>),
|
||||
onFail: (reason: string) => void) {
|
||||
static uploadImage(
|
||||
title: string,
|
||||
description: string,
|
||||
blob: File,
|
||||
handleSuccessfullUpload: (imageURL: string) => Promise<void>,
|
||||
onFail: (reason: string) => void
|
||||
) {
|
||||
const apiUrl = "https://api.imgur.com/3/image"
|
||||
const apiKey = Constants.ImgurApiKey
|
||||
|
||||
const apiUrl = 'https://api.imgur.com/3/image';
|
||||
const apiKey = Constants.ImgurApiKey;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('image', blob);
|
||||
formData.append("title", title);
|
||||
const formData = new FormData()
|
||||
formData.append("image", blob)
|
||||
formData.append("title", title)
|
||||
formData.append("description", description)
|
||||
|
||||
const settings: RequestInit = {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: formData,
|
||||
redirect: 'follow',
|
||||
redirect: "follow",
|
||||
headers: new Headers({
|
||||
Authorization: `Client-ID ${apiKey}`,
|
||||
Accept: 'application/json',
|
||||
Accept: "application/json",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// Response contains stringified JSON
|
||||
// Image URL available at response.data.link
|
||||
fetch(apiUrl, settings).then(async function (response) {
|
||||
const content = await response.json()
|
||||
await handleSuccessfullUpload(content.data.link);
|
||||
}).catch((reason) => {
|
||||
console.log("Uploading to IMGUR failed", reason);
|
||||
// @ts-ignore
|
||||
onFail(reason);
|
||||
});
|
||||
fetch(apiUrl, settings)
|
||||
.then(async function (response) {
|
||||
const content = await response.json()
|
||||
await handleSuccessfullUpload(content.data.link)
|
||||
})
|
||||
.catch((reason) => {
|
||||
console.log("Uploading to IMGUR failed", reason)
|
||||
// @ts-ignore
|
||||
onFail(reason)
|
||||
})
|
||||
}
|
||||
|
||||
SourceIcon(): BaseUIElement {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
public async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
||||
if (Imgur.defaultValuePrefix.some(prefix => value.startsWith(prefix))) {
|
||||
return [Promise.resolve({
|
||||
url: value,
|
||||
key: key,
|
||||
provider: this
|
||||
})]
|
||||
if (Imgur.defaultValuePrefix.some((prefix) => value.startsWith(prefix))) {
|
||||
return [
|
||||
Promise.resolve({
|
||||
url: value,
|
||||
key: key,
|
||||
provider: this,
|
||||
}),
|
||||
]
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
@ -103,29 +115,27 @@ export class Imgur extends ImageProvider {
|
|||
* expected.artist = "Pieter Vander Vennet"
|
||||
* licenseInfo // => expected
|
||||
*/
|
||||
public async DownloadAttribution (url: string) : Promise<LicenseInfo> {
|
||||
const hash = url.substr("https://i.imgur.com/".length).split(".jpg")[0];
|
||||
public async DownloadAttribution(url: string): Promise<LicenseInfo> {
|
||||
const hash = url.substr("https://i.imgur.com/".length).split(".jpg")[0]
|
||||
|
||||
const apiUrl = 'https://api.imgur.com/3/image/' + hash;
|
||||
const response = await Utils.downloadJsonCached(apiUrl, 365*24*60*60,
|
||||
{Authorization: 'Client-ID ' + Constants.ImgurApiKey})
|
||||
const apiUrl = "https://api.imgur.com/3/image/" + hash
|
||||
const response = await Utils.downloadJsonCached(apiUrl, 365 * 24 * 60 * 60, {
|
||||
Authorization: "Client-ID " + Constants.ImgurApiKey,
|
||||
})
|
||||
|
||||
const descr: string = response.data.description ?? "";
|
||||
const data: any = {};
|
||||
const descr: string = response.data.description ?? ""
|
||||
const data: any = {}
|
||||
for (const tag of descr.split("\n")) {
|
||||
const kv = tag.split(":");
|
||||
const k = kv[0];
|
||||
data[k] = kv[1]?.replace(/\r/g, "");
|
||||
const kv = tag.split(":")
|
||||
const k = kv[0]
|
||||
data[k] = kv[1]?.replace(/\r/g, "")
|
||||
}
|
||||
|
||||
const licenseInfo = new LicenseInfo()
|
||||
|
||||
const licenseInfo = new LicenseInfo();
|
||||
|
||||
licenseInfo.licenseShortName = data.license;
|
||||
licenseInfo.artist = data.author;
|
||||
licenseInfo.licenseShortName = data.license
|
||||
licenseInfo.artist = data.author
|
||||
|
||||
return licenseInfo
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import {UIEventSource} from "../UIEventSource";
|
||||
import {Imgur} from "./Imgur";
|
||||
import { UIEventSource } from "../UIEventSource"
|
||||
import { Imgur } from "./Imgur"
|
||||
|
||||
export default class ImgurUploader {
|
||||
|
||||
public readonly queue: UIEventSource<string[]> = new UIEventSource<string[]>([]);
|
||||
public readonly failed: UIEventSource<string[]> = new UIEventSource<string[]>([]);
|
||||
public readonly success: UIEventSource<string[]> = new UIEventSource<string[]>([]);
|
||||
public maxFileSizeInMegabytes = 10;
|
||||
private readonly _handleSuccessUrl: (string) => Promise<void>;
|
||||
public readonly queue: UIEventSource<string[]> = new UIEventSource<string[]>([])
|
||||
public readonly failed: UIEventSource<string[]> = new UIEventSource<string[]>([])
|
||||
public readonly success: UIEventSource<string[]> = new UIEventSource<string[]>([])
|
||||
public maxFileSizeInMegabytes = 10
|
||||
private readonly _handleSuccessUrl: (string) => Promise<void>
|
||||
|
||||
constructor(handleSuccessUrl: (string) => Promise<void>) {
|
||||
this._handleSuccessUrl = handleSuccessUrl;
|
||||
this._handleSuccessUrl = handleSuccessUrl
|
||||
}
|
||||
|
||||
public uploadMany(title: string, description: string, files: FileList): void {
|
||||
|
@ -19,25 +18,26 @@ export default class ImgurUploader {
|
|||
}
|
||||
this.queue.ping()
|
||||
|
||||
const self = this;
|
||||
const self = this
|
||||
this.queue.setData([...self.queue.data])
|
||||
Imgur.uploadMultiple(title,
|
||||
Imgur.uploadMultiple(
|
||||
title,
|
||||
description,
|
||||
files,
|
||||
async function (url) {
|
||||
console.log("File saved at", url);
|
||||
console.log("File saved at", url)
|
||||
self.success.data.push(url)
|
||||
self.success.ping();
|
||||
await self._handleSuccessUrl(url);
|
||||
self.success.ping()
|
||||
await self._handleSuccessUrl(url)
|
||||
},
|
||||
function () {
|
||||
console.log("All uploads completed");
|
||||
console.log("All uploads completed")
|
||||
},
|
||||
|
||||
function (failReason) {
|
||||
console.log("Upload failed due to ", failReason)
|
||||
self.failed.setData([...self.failed.data, failReason])
|
||||
}
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
export class LicenseInfo {
|
||||
title: string = ""
|
||||
artist: string = "";
|
||||
license: string = undefined;
|
||||
licenseShortName: string = "";
|
||||
usageTerms: string = "";
|
||||
attributionRequired: boolean = false;
|
||||
copyrighted: boolean = false;
|
||||
credit: string = "";
|
||||
description: string = "";
|
||||
artist: string = ""
|
||||
license: string = undefined
|
||||
licenseShortName: string = ""
|
||||
usageTerms: string = ""
|
||||
attributionRequired: boolean = false
|
||||
copyrighted: boolean = false
|
||||
credit: string = ""
|
||||
description: string = ""
|
||||
informationLocation: URL = undefined
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
import ImageProvider, {ProvidedImage} from "./ImageProvider";
|
||||
import BaseUIElement from "../../UI/BaseUIElement";
|
||||
import Svg from "../../Svg";
|
||||
import {Utils} from "../../Utils";
|
||||
import {LicenseInfo} from "./LicenseInfo";
|
||||
import Constants from "../../Models/Constants";
|
||||
import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
||||
import BaseUIElement from "../../UI/BaseUIElement"
|
||||
import Svg from "../../Svg"
|
||||
import { Utils } from "../../Utils"
|
||||
import { LicenseInfo } from "./LicenseInfo"
|
||||
import Constants from "../../Models/Constants"
|
||||
|
||||
export class Mapillary extends ImageProvider {
|
||||
|
||||
public static readonly singleton = new Mapillary();
|
||||
public static readonly singleton = new Mapillary()
|
||||
private static readonly valuePrefix = "https://a.mapillary.com"
|
||||
public static readonly valuePrefixes = [Mapillary.valuePrefix, "http://mapillary.com", "https://mapillary.com", "http://www.mapillary.com", "https://www.mapillary.com"]
|
||||
public static readonly valuePrefixes = [
|
||||
Mapillary.valuePrefix,
|
||||
"http://mapillary.com",
|
||||
"https://mapillary.com",
|
||||
"http://www.mapillary.com",
|
||||
"https://www.mapillary.com",
|
||||
]
|
||||
defaultKeyPrefixes = ["mapillary", "image"]
|
||||
|
||||
/**
|
||||
* Indicates that this is the same URL
|
||||
* Ignores 'stp' parameter
|
||||
*
|
||||
*
|
||||
* const a = "https://scontent-bru2-1.xx.fbcdn.net/m1/v/t6/An8xm5SGLt20ETziNqzhhBd8b8S5GHLiIu8N6BbyqHFohFAQoaJJPG8i5yQiSwjYmEqXSfVeoCmpiyBJICEkQK98JOB21kkJoBS8VdhYa-Ty93lBnznQyesJBtKcb32foGut2Hgt10hEMWJbE3dDgA?stp=s1024x768&ccb=10-5&oh=00_AT-ZGTXHzihoaQYBILmEiAEKR64z_IWiTlcAYq_D7Ka0-Q&oe=6278C456&_nc_sid=122ab1"
|
||||
* const b = "https://scontent-bru2-1.xx.fbcdn.net/m1/v/t6/An8xm5SGLt20ETziNqzhhBd8b8S5GHLiIu8N6BbyqHFohFAQoaJJPG8i5yQiSwjYmEqXSfVeoCmpiyBJICEkQK98JOB21kkJoBS8VdhYa-Ty93lBnznQyesJBtKcb32foGut2Hgt10hEMWJbE3dDgA?stp=s256x192&ccb=10-5&oh=00_AT9BZ1Rpc9zbY_uNu92A_4gj1joiy1b6VtgtLIu_7wh9Bg&oe=6278C456&_nc_sid=122ab1"
|
||||
* Mapillary.sameUrl(a, b) => true
|
||||
|
@ -28,9 +33,9 @@ export class Mapillary extends ImageProvider {
|
|||
const aUrl = new URL(a)
|
||||
const bUrl = new URL(b)
|
||||
if (aUrl.host !== bUrl.host || aUrl.pathname !== bUrl.pathname) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
let allSame = true;
|
||||
let allSame = true
|
||||
aUrl.searchParams.forEach((value, key) => {
|
||||
if (key === "stp") {
|
||||
// This is the key indicating the image size on mapillary; we ignore it
|
||||
|
@ -41,20 +46,18 @@ export class Mapillary extends ImageProvider {
|
|||
return
|
||||
}
|
||||
})
|
||||
return allSame;
|
||||
|
||||
return allSame
|
||||
} catch (e) {
|
||||
console.debug("Could not compare ", a, "and", b, "due to", e)
|
||||
}
|
||||
return false;
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the correct key for API v4.0
|
||||
*/
|
||||
private static ExtractKeyFromURL(value: string): number {
|
||||
let key: string;
|
||||
let key: string
|
||||
|
||||
const newApiFormat = value.match(/https?:\/\/www.mapillary.com\/app\/\?pKey=([0-9]*)/)
|
||||
if (newApiFormat !== null) {
|
||||
|
@ -62,7 +65,7 @@ export class Mapillary extends ImageProvider {
|
|||
} else if (value.startsWith(Mapillary.valuePrefix)) {
|
||||
key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1)
|
||||
} else if (value.match("[0-9]*")) {
|
||||
key = value;
|
||||
key = value
|
||||
}
|
||||
|
||||
const keyAsNumber = Number(key)
|
||||
|
@ -74,7 +77,7 @@ export class Mapillary extends ImageProvider {
|
|||
}
|
||||
|
||||
SourceIcon(backlinkSource?: string): BaseUIElement {
|
||||
return Svg.mapillary_svg();
|
||||
return Svg.mapillary_svg()
|
||||
}
|
||||
|
||||
async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
||||
|
@ -83,26 +86,30 @@ export class Mapillary extends ImageProvider {
|
|||
|
||||
public async DownloadAttribution(url: string): Promise<LicenseInfo> {
|
||||
const license = new LicenseInfo()
|
||||
license.artist = "Contributor name unavailable";
|
||||
license.license = "CC BY-SA 4.0";
|
||||
license.artist = "Contributor name unavailable"
|
||||
license.license = "CC BY-SA 4.0"
|
||||
// license.license = "Creative Commons Attribution-ShareAlike 4.0 International License";
|
||||
license.attributionRequired = true;
|
||||
license.attributionRequired = true
|
||||
return license
|
||||
}
|
||||
|
||||
private async PrepareUrlAsync(key: string, value: string): Promise<ProvidedImage> {
|
||||
const mapillaryId = Mapillary.ExtractKeyFromURL(value)
|
||||
if (mapillaryId === undefined) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
const metadataUrl = 'https://graph.mapillary.com/' + mapillaryId + '?fields=thumb_1024_url&&access_token=' + Constants.mapillary_client_token_v4;
|
||||
const response = await Utils.downloadJsonCached(metadataUrl,60*60)
|
||||
const url = <string>response["thumb_1024_url"];
|
||||
const metadataUrl =
|
||||
"https://graph.mapillary.com/" +
|
||||
mapillaryId +
|
||||
"?fields=thumb_1024_url&&access_token=" +
|
||||
Constants.mapillary_client_token_v4
|
||||
const response = await Utils.downloadJsonCached(metadataUrl, 60 * 60)
|
||||
const url = <string>response["thumb_1024_url"]
|
||||
return {
|
||||
url: url,
|
||||
provider: this,
|
||||
key: key
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import ImageProvider, {ProvidedImage} from "./ImageProvider";
|
||||
import BaseUIElement from "../../UI/BaseUIElement";
|
||||
import Svg from "../../Svg";
|
||||
import {WikimediaImageProvider} from "./WikimediaImageProvider";
|
||||
import Wikidata from "../Web/Wikidata";
|
||||
import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
||||
import BaseUIElement from "../../UI/BaseUIElement"
|
||||
import Svg from "../../Svg"
|
||||
import { WikimediaImageProvider } from "./WikimediaImageProvider"
|
||||
import Wikidata from "../Web/Wikidata"
|
||||
|
||||
export class WikidataImageProvider extends ImageProvider {
|
||||
|
||||
public static readonly singleton = new WikidataImageProvider()
|
||||
public readonly defaultKeyPrefixes = ["wikidata"]
|
||||
|
||||
|
@ -14,7 +13,7 @@ export class WikidataImageProvider extends ImageProvider {
|
|||
}
|
||||
|
||||
public SourceIcon(backlinkSource?: string): BaseUIElement {
|
||||
throw Svg.wikidata_svg();
|
||||
throw Svg.wikidata_svg()
|
||||
}
|
||||
|
||||
public async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
||||
|
@ -39,7 +38,10 @@ export class WikidataImageProvider extends ImageProvider {
|
|||
}
|
||||
|
||||
const commons = entity.commons
|
||||
if (commons !== undefined && (commons.startsWith("Category:") || commons.startsWith("File:"))) {
|
||||
if (
|
||||
commons !== undefined &&
|
||||
(commons.startsWith("Category:") || commons.startsWith("File:"))
|
||||
) {
|
||||
const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined, commons)
|
||||
allImages.push(...promises)
|
||||
}
|
||||
|
@ -47,7 +49,6 @@ export class WikidataImageProvider extends ImageProvider {
|
|||
}
|
||||
|
||||
public DownloadAttribution(url: string): Promise<any> {
|
||||
throw new Error("Method not implemented; shouldn't be needed!");
|
||||
throw new Error("Method not implemented; shouldn't be needed!")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,45 +1,47 @@
|
|||
import ImageProvider, {ProvidedImage} from "./ImageProvider";
|
||||
import BaseUIElement from "../../UI/BaseUIElement";
|
||||
import Svg from "../../Svg";
|
||||
import Link from "../../UI/Base/Link";
|
||||
import {Utils} from "../../Utils";
|
||||
import {LicenseInfo} from "./LicenseInfo";
|
||||
import Wikimedia from "../Web/Wikimedia";
|
||||
import ImageProvider, { ProvidedImage } from "./ImageProvider"
|
||||
import BaseUIElement from "../../UI/BaseUIElement"
|
||||
import Svg from "../../Svg"
|
||||
import Link from "../../UI/Base/Link"
|
||||
import { Utils } from "../../Utils"
|
||||
import { LicenseInfo } from "./LicenseInfo"
|
||||
import Wikimedia from "../Web/Wikimedia"
|
||||
|
||||
/**
|
||||
* This module provides endpoints for wikimedia and others
|
||||
*/
|
||||
export class WikimediaImageProvider extends ImageProvider {
|
||||
|
||||
|
||||
public static readonly singleton = new WikimediaImageProvider();
|
||||
public static readonly commonsPrefixes = ["https://commons.wikimedia.org/wiki/", "https://upload.wikimedia.org", "File:"]
|
||||
public static readonly singleton = new WikimediaImageProvider()
|
||||
public static readonly commonsPrefixes = [
|
||||
"https://commons.wikimedia.org/wiki/",
|
||||
"https://upload.wikimedia.org",
|
||||
"File:",
|
||||
]
|
||||
private readonly commons_key = "wikimedia_commons"
|
||||
public readonly defaultKeyPrefixes = [this.commons_key, "image"]
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
super()
|
||||
}
|
||||
|
||||
private static ExtractFileName(url: string) {
|
||||
if (!url.startsWith("http")) {
|
||||
return url;
|
||||
return url
|
||||
}
|
||||
const path = new URL(url).pathname
|
||||
return path.substring(path.lastIndexOf("/") + 1);
|
||||
|
||||
return path.substring(path.lastIndexOf("/") + 1)
|
||||
}
|
||||
|
||||
private static PrepareUrl(value: string): string {
|
||||
|
||||
if (value.toLowerCase().startsWith("https://commons.wikimedia.org/wiki/")) {
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
return (`https://commons.wikimedia.org/wiki/Special:FilePath/${encodeURIComponent(value)}?width=500&height=400`)
|
||||
return `https://commons.wikimedia.org/wiki/Special:FilePath/${encodeURIComponent(
|
||||
value
|
||||
)}?width=500&height=400`
|
||||
}
|
||||
|
||||
private static startsWithCommonsPrefix(value: string): boolean {
|
||||
return WikimediaImageProvider.commonsPrefixes.some(prefix => value.startsWith(prefix))
|
||||
return WikimediaImageProvider.commonsPrefixes.some((prefix) => value.startsWith(prefix))
|
||||
}
|
||||
|
||||
private static removeCommonsPrefix(value: string): string {
|
||||
|
@ -49,7 +51,7 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
if (!value.startsWith("File:")) {
|
||||
value = "File:" + value
|
||||
}
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
|
||||
for (const prefix of WikimediaImageProvider.commonsPrefixes) {
|
||||
|
@ -61,21 +63,20 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
return part
|
||||
}
|
||||
}
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
|
||||
SourceIcon(backlink: string): BaseUIElement {
|
||||
const img = Svg.wikimedia_commons_white_svg()
|
||||
.SetStyle("width:2em;height: 2em");
|
||||
const img = Svg.wikimedia_commons_white_svg().SetStyle("width:2em;height: 2em")
|
||||
if (backlink === undefined) {
|
||||
return img
|
||||
}
|
||||
|
||||
|
||||
return new Link(Svg.wikimedia_commons_white_img,
|
||||
`https://commons.wikimedia.org/wiki/${backlink}`, true)
|
||||
|
||||
|
||||
return new Link(
|
||||
Svg.wikimedia_commons_white_img,
|
||||
`https://commons.wikimedia.org/wiki/${backlink}`,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
public PrepUrl(value: string): ProvidedImage {
|
||||
|
@ -99,7 +100,9 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
value = WikimediaImageProvider.removeCommonsPrefix(value)
|
||||
if (value.startsWith("Category:")) {
|
||||
const urls = await Wikimedia.GetCategoryContents(value)
|
||||
return urls.filter(url => url.startsWith("File:")).map(image => Promise.resolve(this.UrlForImage(image)))
|
||||
return urls
|
||||
.filter((url) => url.startsWith("File:"))
|
||||
.map((image) => Promise.resolve(this.UrlForImage(image)))
|
||||
}
|
||||
if (value.startsWith("File:")) {
|
||||
return [Promise.resolve(this.UrlForImage(value))]
|
||||
|
@ -116,24 +119,30 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
filename = WikimediaImageProvider.ExtractFileName(filename)
|
||||
|
||||
if (filename === "") {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
const url = "https://en.wikipedia.org/w/" +
|
||||
const url =
|
||||
"https://en.wikipedia.org/w/" +
|
||||
"api.php?action=query&prop=imageinfo&iiprop=extmetadata&" +
|
||||
"titles=" + filename +
|
||||
"&format=json&origin=*";
|
||||
const data = await Utils.downloadJsonCached(url,365*24*60*60)
|
||||
const licenseInfo = new LicenseInfo();
|
||||
"titles=" +
|
||||
filename +
|
||||
"&format=json&origin=*"
|
||||
const data = await Utils.downloadJsonCached(url, 365 * 24 * 60 * 60)
|
||||
const licenseInfo = new LicenseInfo()
|
||||
const pageInfo = data.query.pages[-1]
|
||||
if (pageInfo === undefined) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
const license = (pageInfo.imageinfo ?? [])[0]?.extmetadata;
|
||||
|
||||
const license = (pageInfo.imageinfo ?? [])[0]?.extmetadata
|
||||
if (license === undefined) {
|
||||
console.warn("The file", filename, "has no usable metedata or license attached... Please fix the license info file yourself!")
|
||||
return undefined;
|
||||
console.warn(
|
||||
"The file",
|
||||
filename,
|
||||
"has no usable metedata or license attached... Please fix the license info file yourself!"
|
||||
)
|
||||
return undefined
|
||||
}
|
||||
|
||||
let title = pageInfo.title
|
||||
|
@ -145,26 +154,22 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
}
|
||||
|
||||
licenseInfo.title = title
|
||||
licenseInfo.artist = license.Artist?.value;
|
||||
licenseInfo.license = license.License?.value;
|
||||
licenseInfo.copyrighted = license.Copyrighted?.value;
|
||||
licenseInfo.attributionRequired = license.AttributionRequired?.value;
|
||||
licenseInfo.usageTerms = license.UsageTerms?.value;
|
||||
licenseInfo.licenseShortName = license.LicenseShortName?.value;
|
||||
licenseInfo.credit = license.Credit?.value;
|
||||
licenseInfo.description = license.ImageDescription?.value;
|
||||
licenseInfo.informationLocation = new URL("https://en.wikipedia.org/wiki/"+pageInfo.title)
|
||||
return licenseInfo;
|
||||
|
||||
licenseInfo.artist = license.Artist?.value
|
||||
licenseInfo.license = license.License?.value
|
||||
licenseInfo.copyrighted = license.Copyrighted?.value
|
||||
licenseInfo.attributionRequired = license.AttributionRequired?.value
|
||||
licenseInfo.usageTerms = license.UsageTerms?.value
|
||||
licenseInfo.licenseShortName = license.LicenseShortName?.value
|
||||
licenseInfo.credit = license.Credit?.value
|
||||
licenseInfo.description = license.ImageDescription?.value
|
||||
licenseInfo.informationLocation = new URL("https://en.wikipedia.org/wiki/" + pageInfo.title)
|
||||
return licenseInfo
|
||||
}
|
||||
|
||||
private UrlForImage(image: string): ProvidedImage {
|
||||
if (!image.startsWith("File:")) {
|
||||
image = "File:" + image
|
||||
}
|
||||
return {url: WikimediaImageProvider.PrepareUrl(image), key: undefined, provider: this}
|
||||
return { url: WikimediaImageProvider.PrepareUrl(image), key: undefined, provider: this }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue