diff --git a/Logic/ImageProviders/AllImageProviders.ts b/Logic/ImageProviders/AllImageProviders.ts index 2f27539a1..b56fae6f3 100644 --- a/Logic/ImageProviders/AllImageProviders.ts +++ b/Logic/ImageProviders/AllImageProviders.ts @@ -16,7 +16,11 @@ export default class AllImageProviders { Mapillary.singleton, WikidataImageProvider.singleton, WikimediaImageProvider.singleton, - new GenericImageProvider([].concat(...Imgur.defaultValuePrefix, WikimediaImageProvider.commonsPrefix, ...Mapillary.valuePrefixes))] + new GenericImageProvider( + [].concat(...Imgur.defaultValuePrefix, ...WikimediaImageProvider.commonsPrefixes, ...Mapillary.valuePrefixes) + ) + + ] private static _cache: Map> = new Map>() diff --git a/Logic/ImageProviders/WikimediaImageProvider.ts b/Logic/ImageProviders/WikimediaImageProvider.ts index ae922c99a..a00df6719 100644 --- a/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/Logic/ImageProviders/WikimediaImageProvider.ts @@ -15,7 +15,7 @@ export class WikimediaImageProvider extends ImageProvider { private readonly commons_key = "wikimedia_commons" public readonly defaultKeyPrefixes = [this.commons_key,"image"] public static readonly singleton = new WikimediaImageProvider(); - public static readonly commonsPrefix = "https://commons.wikimedia.org/wiki/" + public static readonly commonsPrefixes = ["https://commons.wikimedia.org/wiki/", "https://upload.wikimedia.org", "File:"] private constructor() { super(); @@ -89,22 +89,40 @@ export class WikimediaImageProvider extends ImageProvider { } return {url: this.PrepareUrl(image), key: undefined, provider: this} } + + private startsWithCommonsPrefix(value: string){ + return WikimediaImageProvider.commonsPrefixes.some(prefix => value.startsWith(prefix)) + } + + private removeCommonsPrefix(value: string){ + if(value.startsWith("https://upload.wikimedia.org/")){ + value = value.substring(value.lastIndexOf("/") + 1) + value = decodeURIComponent(value) + if(!value.startsWith("File:")){ + value = "File:"+value + } + return value; + } + + for (const prefix of WikimediaImageProvider.commonsPrefixes) { + if(value.startsWith(prefix)){ + let part = value.substr(prefix.length) + if(prefix.startsWith("http")){ + part = decodeURIComponent(part) + } + return part + } + } + return value; + } public async ExtractUrls(key: string, value: string): Promise[]> { - if(key !== undefined && key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){ + const hasCommonsPrefix = this.startsWithCommonsPrefix(value) + if(key !== undefined && key !== this.commons_key && !hasCommonsPrefix){ return [] } - if (value.startsWith(WikimediaImageProvider.commonsPrefix)) { - value = value.substring(WikimediaImageProvider.commonsPrefix.length) - } else if (value.startsWith("https://upload.wikimedia.org")) { - const result: ProvidedImage = { - key: undefined, - url: value, - provider: this - } - return [Promise.resolve(result)] - } + value = this.removeCommonsPrefix(value) if (value.startsWith("Category:")) { const urls = await Wikimedia.GetCategoryContents(value) return urls.filter(url => url.startsWith("File:")).map(image => this.UrlForImage(image)) diff --git a/test/ImageProvider.spec.ts b/test/ImageProvider.spec.ts new file mode 100644 index 000000000..76e73fc33 --- /dev/null +++ b/test/ImageProvider.spec.ts @@ -0,0 +1,68 @@ +import T from "./TestHelper"; +import AllImageProviders from "../Logic/ImageProviders/AllImageProviders"; +import {UIEventSource} from "../Logic/UIEventSource"; + +export default class ImageProviderSpec extends T { + + constructor() { + super("ImageProvider", [ + ["Search images", () => { + + let i = 0 + function expects(url, tags, providerName = undefined) { + tags.id = "test/"+i + i++ + AllImageProviders.LoadImagesFor(new UIEventSource(tags)).addCallbackD(images => { + console.log("ImageProvider test", tags.id, "for", tags) + const img = images[0] + if(img === undefined){ + throw "No image found" + } + T.equals(url, img.url, tags.id) + if(providerName){ + T.equals(img.provider.constructor.name, providerName) + } + console.log("OK") + }) + } + + const muntpoort_expected = "https://commons.wikimedia.org/wiki/Special:FilePath/File%3ABr%C3%BCgge-Muntpoort_6-29510-58192.jpg?width=500&height=400" + expects( + muntpoort_expected, + { "wikimedia_commons":"File:Brügge-Muntpoort_6-29510-58192.jpg" + } , "WikimediaImageProvider") + + + + expects(muntpoort_expected, + { "wikimedia_commons":"https://upload.wikimedia.org/wikipedia/commons/c/cd/Br%C3%BCgge-Muntpoort_6-29510-58192.jpg" + } , "WikimediaImageProvider") + + expects(muntpoort_expected , { + "image":"https://upload.wikimedia.org/wikipedia/commons/c/cd/Br%C3%BCgge-Muntpoort_6-29510-58192.jpg" + } , "WikimediaImageProvider") + + + expects("https://commons.wikimedia.org/wiki/Special:FilePath/File%3ABelgium-5955_-_Simon_Stevin_(13746657193).jpg?width=500&height=400" , { + "image":"File:Belgium-5955_-_Simon_Stevin_(13746657193).jpg" + } , "WikimediaImageProvider") + + expects("https://commons.wikimedia.org/wiki/Special:FilePath/File%3ABelgium-5955_-_Simon_Stevin_(13746657193).jpg?width=500&height=400" , { + "wikimedia_commons":"File:Belgium-5955_-_Simon_Stevin_(13746657193).jpg" + } , "WikimediaImageProvider") + + + + + expects("https://commons.wikimedia.org/wiki/Special:FilePath/File%3ABrugge_Leeuwstraat_zonder_nummer_Leeuwbrug_-_119334_-_onroerenderfgoed.jpg?width=500&height=400",{ + image:"File:Brugge_Leeuwstraat_zonder_nummer_Leeuwbrug_-_119334_-_onroerenderfgoed.jpg" + }, "WikimediaImageProvider") + + + }] + + + ]); + } + +} \ No newline at end of file diff --git a/test/TestAll.ts b/test/TestAll.ts index bdb92afdb..cf1d4d174 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -11,6 +11,7 @@ import SplitActionSpec from "./SplitAction.spec"; import {Utils} from "../Utils"; import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec"; import WikidataSpecTest from "./Wikidata.spec.test"; +import ImageProviderSpec from "./ImageProvider.spec"; ScriptUtils.fixUtils() @@ -25,7 +26,8 @@ const allTests = [ new RelationSplitHandlerSpec(), new SplitActionSpec(), new TileFreshnessCalculatorSpec(), - new WikidataSpecTest() + new WikidataSpecTest(), + new ImageProviderSpec() ] Utils.externalDownloadFunction = async (url) => { @@ -44,16 +46,17 @@ args = args.map(a => a.toLowerCase()) const allFailures: { testsuite: string, name: string, msg: string } [] = [] let testsToRun = allTests if (args.length > 0) { - testsToRun = allTests.filter(t => args.indexOf(t.name) >= 0) + args = args.map(a => a.toLowerCase()) + testsToRun = allTests.filter(t => args.indexOf(t.name.toLowerCase()) >= 0) } if (testsToRun.length == 0) { - throw "No tests found" + throw "No tests found. Try one of "+allTests.map(t => t.name).join(", ") } for (let i = 0; i < testsToRun.length; i++) { const test = testsToRun[i]; - console.log(" Running test", i, "/", allTests.length, test.name) + console.log(" Running test", i, "/", testsToRun.length, test.name) allFailures.push(...(test.Run() ?? [])) console.log("OK!") } diff --git a/test/TestHelper.ts b/test/TestHelper.ts index 971b64396..95ea119c4 100644 --- a/test/TestHelper.ts +++ b/test/TestHelper.ts @@ -40,6 +40,14 @@ export default class T { throw "Expected true, but got false: " + msg } } + + static equals(a, b, msg?){ + if(a !== b){ + throw "Not the same: "+(msg??"")+"\n" + + "Expcected: "+a+"\n" + + "Got : "+b + } + } static isFalse(b: boolean, msg: string) { if (b) {