diff options
| author | Michael Peter Christen <mc@yacy.net> | 2026-01-03 02:55:13 +0100 |
|---|---|---|
| committer | Michael Peter Christen <mc@yacy.net> | 2026-01-03 02:55:13 +0100 |
| commit | e2d0050a1c15df8fda56edb5bc559d84149e6633 (patch) | |
| tree | 86c90e82408efad8993108c1b5bcb2028b51514e | |
| parent | 5645c3c55deba7a15033ee3d13c173eac44877d8 (diff) | |
Fix image preview licensing and show 20 images per page, fixes
https://github.com/yacy/yacy_search_server/issues/438
Use separate one‑time licenses for image thumbnails and full previews to
prevent preview failures after thumbnail loads, and set the Images tab
to request 20 results by default via the content‑domain switch form.
| -rw-r--r-- | htroot/js/yacysearch.js | 30 | ||||
| -rw-r--r-- | htroot/yacysearchitem.html | 4 | ||||
| -rw-r--r-- | htroot/yacysearchtrailer.html | 28 | ||||
| -rw-r--r-- | source/net/yacy/htroot/yacysearch.java | 4 | ||||
| -rw-r--r-- | source/net/yacy/htroot/yacysearchitem.java | 26 |
5 files changed, 64 insertions, 28 deletions
diff --git a/htroot/js/yacysearch.js b/htroot/js/yacysearch.js index dc9a19868..aaf317ff2 100644 --- a/htroot/js/yacysearch.js +++ b/htroot/js/yacysearch.js @@ -294,10 +294,32 @@ function toggleMoreTags(button, moreTagsId) { * {HTMLImageElement} the html img element that could not be rendered */ function handleResultThumbError(imgElem) { + if (imgElem == null) { + return; + } + var stage = imgElem.getAttribute("data-error-stage") || "0"; + if (stage === "0") { + var original = imgElem.getAttribute("data-original"); + if (original && imgElem.src !== original) { + imgElem.setAttribute("data-error-stage", "1"); + imgElem.src = original; + return; + } + } + if (stage === "1") { + var fallback = imgElem.getAttribute("data-fallback"); + if (fallback && imgElem.src.indexOf(fallback) < 0) { + imgElem.setAttribute("data-error-stage", "2"); + imgElem.src = fallback; + return; + } + } if (imgElem.parentNode != null && imgElem.parentNode.parentNode != null - && imgElem.parentNode.parentNode.className == "thumbcontainer") { - /* Hide the thumbnail container */ - imgElem.parentNode.parentNode.className = "thumbcontainer thumbError hidden"; + && imgElem.parentNode.parentNode.className.indexOf("thumbcontainer") >= 0) { + /* Keep the thumbnail container visible, but mark it as failed */ + if (imgElem.parentNode.parentNode.className.indexOf("thumbError") < 0) { + imgElem.parentNode.parentNode.className += " thumbError"; + } var errorsInfoElem = document.getElementById("imageErrorsInfo"); if (errorsInfoElem != null && errorsInfoElem.className.indexOf("hidden") >= 0) { /* Show the image errors information block */ @@ -348,4 +370,4 @@ function hideErrThumbnails() { if(hideBtn != null) { hideBtn.className = hideBtn.className + " hidden"; } -}
\ No newline at end of file +} diff --git a/htroot/yacysearchitem.html b/htroot/yacysearchitem.html index 4b76ae33c..9692fc1f0 100644 --- a/htroot/yacysearchitem.html +++ b/htroot/yacysearchitem.html @@ -47,7 +47,7 @@ #(showMetadata)#::<span role="separator" aria-orientation="vertical"> | </span><a href="solr/select?q=id:%22#[urlhash]#%22&start=0&rows=1&core=collection1&wt=html&omitHeader=true" target="_blank">Metadata</a>#(/showMetadata)# #(showParser)#::<span role="separator" aria-orientation="vertical"> | </span><a href="ViewFile.html?urlHash=#[urlhash]#&words=#[words]#" target="_blank">Parser</a>#(/showParser)# #(showCitation)#::<span role="separator" aria-orientation="vertical"> | </span><a href="api/citation.html?hash=#[urlhash]#&filter=true" target="_blank">Citations</a>#(/showCitation)# - #(showPictures)#::<span role="separator" aria-orientation="vertical"> | </span><a href="yacysearch.html?contentdom=image#(authSearch)#::&auth#(/authSearch)#&url=#[link]#&query=#[former]#+inurl:#[link]#" target="_blank">Pictures</a>#(/showPictures)# + #(showPictures)#::<span role="separator" aria-orientation="vertical"> | </span><a href="yacysearch.html?contentdom=image#(authSearch)#::&auth#(/authSearch)#&maximumRecords=20&url=#[link]#&query=#[former]#+inurl:#[link]#" target="_blank">Pictures</a>#(/showPictures)# #(showCache)#::<span role="separator" aria-orientation="vertical"> | </span><a href="CacheResource_p.html?url=#[link]#" target="_blank">Cache</a>#(/showCache)# #(showProxy)#::<span role="separator" aria-orientation="vertical"> | </span><a href="proxy.html?url=#[link]#" target="_blank">View via proxy</a>#(/showProxy)# #(showIndexBrowser)#::<span role="separator" aria-orientation="vertical"> | </span><a href="IndexBrowser_p.html?path=#[link]#"><img src="env/grafics/minitree.png" width="15" height="8" title="Browse index" alt="Browse index"/></a>#(/showIndexBrowser)# @@ -59,7 +59,7 @@ :: #(item)#::<div class="thumbcontainer"> <a href="#[hrefFullPreview]#" target="#[target]#" class="thumblink" onclick="return hs.expand(this)"> - <img src="#[hrefCache]#" width="256" height="256" style="#[style]#" alt="#[name]#" onerror="handleResultThumbError(this)"/> + <img src="#[hrefCache]#" width="256" height="256" style="#[style]#" alt="#[name]#" data-original="#[href]#" data-fallback="env/grafics/nok.png" onerror="handleResultThumbError(this)"/> </a> <div class="highslide-caption"><a href="#[href]#" target="#[target]#" #(noreferrer)#::rel="noreferrer"#(/noreferrer)#>#[name]#</a><br /><a href="#[source]#" target="#[target]#" #(noreferrer)#::rel="noreferrer"#(/noreferrer)#>#[sourcedom]#</a></div> </div>#(/item)# diff --git a/htroot/yacysearchtrailer.html b/htroot/yacysearchtrailer.html index ad4aad107..cdbf32093 100644 --- a/htroot/yacysearchtrailer.html +++ b/htroot/yacysearchtrailer.html @@ -22,6 +22,7 @@ <input type="hidden" name="strictContentDom" value="#[strictContentDom]#" /> <input type="hidden" name="former" value="#[former]#" /> <input type="hidden" name="maximumRecords" value="#[maximumRecords]#" /> + <input type="hidden" name="maximumRecords" value="#[maximumRecords]#" /> <input type="hidden" name="startRecord" value="#[startRecord]#" /> <input type="hidden" name="verify" value="#[search.verify]#" /> <input type="hidden" name="nav" value="#[search.navigation]#" /> @@ -112,28 +113,33 @@ $('#resource-switch-form').popover() <input id="contentDomTimezoneOffset" type="hidden" name="timezoneOffset" value=""><script>document.getElementById("contentDomTimezoneOffset").value = new Date().getTimezoneOffset();</script> <div class="btn-group btn-group-justified"> #(searchtext)#::<div class="btn-group btn-group-xs"> - <button type="#(check)#submit::button#(/check)#" name="contentdom" value="text" - class="btn btn-default#(check)#:: active#(/check)#">Documents</button> + <button type="submit" name="contentdom" value="text" + class="btn btn-default#(check)#:: active#(/check)#" + onclick="this.form.maximumRecords.value = '10';">Documents</button> </div> #(/searchtext)# #(searchimage)#::<div class="btn-group btn-group-xs"> - <button type="#(check)#submit::button#(/check)#" name="contentdom" value="image" - class="btn btn-default#(check)#:: active#(/check)#">Images</button> + <button type="submit" name="contentdom" value="image" + class="btn btn-default#(check)#:: active#(/check)#" + onclick="this.form.maximumRecords.value = '20';">Images</button> </div> #(/searchimage)# #(searchaudio)#::<div class="btn-group btn-group-xs"> - <button type="#(check)#submit::button#(/check)#" name="contentdom" value="audio" - class="btn btn-default#(check)#:: active#(/check)#">Audio</button> + <button type="submit" name="contentdom" value="audio" + class="btn btn-default#(check)#:: active#(/check)#" + onclick="this.form.maximumRecords.value = '10';">Audio</button> </div> #(/searchaudio)# #(searchvideo)#::<div class="btn-group btn-group-xs"> - <button type="#(check)#submit::button#(/check)#" name="contentdom" value="video" - class="btn btn-default#(check)#:: active#(/check)#">Video</button> + <button type="submit" name="contentdom" value="video" + class="btn btn-default#(check)#:: active#(/check)#" + onclick="this.form.maximumRecords.value = '10';">Video</button> </div> #(/searchvideo)# #(searchapp)#::<div class="btn-group btn-group-xs"> - <button type="#(check)#submit::button#(/check)#" name="contentdom" value="app" - class="btn btn-default#(check)#:: active#(/check)#">Apps</button> + <button type="submit" name="contentdom" value="app" + class="btn btn-default#(check)#:: active#(/check)#" + onclick="this.form.maximumRecords.value = '10';">Apps</button> </div> #(/searchapp)# </div> @@ -316,4 +322,4 @@ function toggleVisibility(name, count) { <li><h3>#[headline]#</h3></li> <li>#[body]#</li> </ul> -#(/nav-about)#
\ No newline at end of file +#(/nav-about)# diff --git a/source/net/yacy/htroot/yacysearch.java b/source/net/yacy/htroot/yacysearch.java index fa1e452bd..dbe77d1ec 100644 --- a/source/net/yacy/htroot/yacysearch.java +++ b/source/net/yacy/htroot/yacysearch.java @@ -256,6 +256,7 @@ public class yacysearch { // check an determine items per page (max of [100 or configured default]} final int defaultItemsPerPage = sb.getConfigInt(SwitchboardConstants.SEARCH_ITEMS, 10); + final boolean itemsPerPageExplicit = post != null && (post.containsKey("maximumRecords") || post.containsKey("count") || post.containsKey("rows")); int itemsPerPage = post.getInt("maximumRecords", post.getInt("count", post.getInt("rows", defaultItemsPerPage))); // requested or default // SRU syntax with old property as alternative // whatever admin has set as default, that's always ok if (itemsPerPage > defaultItemsPerPage && itemsPerPage > 100) { // if above hardcoded 100 limit restrict request (except default allows more) @@ -301,6 +302,9 @@ public class yacysearch { // find search domain final Classification.ContentDomain contentdom = post == null || !post.containsKey("contentdom") ? ContentDomain.ALL : ContentDomain.contentdomParser(post.get("contentdom", "all")); + if (contentdom == ContentDomain.IMAGE && itemsPerPage == defaultItemsPerPage) { + itemsPerPage = 20; + } // Strict/extended content domain constraint : configured setting may be overriden by request param final boolean strictContentDom = !Boolean.FALSE.toString().equalsIgnoreCase(post.get("strictContentDom", diff --git a/source/net/yacy/htroot/yacysearchitem.java b/source/net/yacy/htroot/yacysearchitem.java index d488480f6..28a7ecac9 100644 --- a/source/net/yacy/htroot/yacysearchitem.java +++ b/source/net/yacy/htroot/yacysearchitem.java @@ -783,28 +783,32 @@ public class yacysearchitem { final String imageUrlExt = MultiProtocolURL.getFileExtension(image.imageUrl.getFileName()); final String target = sb.getConfig(imageUrlstring.matches(target_special_pattern) ? SwitchboardConstants.SEARCH_TARGET_SPECIAL : SwitchboardConstants.SEARCH_TARGET_DEFAULT, "_self"); - final String license = URLLicense.aquireLicense(image.imageUrl); // this is just the license key to get the image forwarded through the YaCy thumbnail viewer, not an actual lawful license + String license = ""; // this is just the license key to get the image forwarded through the YaCy thumbnail viewer, not an actual lawful license /* Image format ouput for ViewImage servlet : default is png, except with gif and svg images */ final String viewImageExt = !imageUrlExt.isEmpty() && ImageViewer.isBrowserRendered(imageUrlExt) ? imageUrlExt : "png"; /* Thumb URL */ - final StringBuilder thumbURLBuilder = new StringBuilder("ViewImage.").append(viewImageExt).append("?maxwidth=") + final StringBuilder thumbURLBuilder = new StringBuilder("ViewImage.").append(viewImageExt).append("?maxwidth=") .append(DEFAULT_IMG_WIDTH).append("&maxheight=").append(DEFAULT_IMG_HEIGHT) .append("&isStatic=true&quadratic"); /* Only use licence code for non authentified users. For authenticated users licence would never be released and would unnecessarily fill URLLicense.permissions. */ + final String thumbURL; + final String fullPreviewURL; if(fullViewingRights) { thumbURLBuilder.append("&url=").append(imageUrlstring); + thumbURL = thumbURLBuilder.toString(); + /* Full size preview URL */ + fullPreviewURL = "ViewImage." + viewImageExt + "?isStatic=true&url=" + imageUrlstring; } else { - thumbURLBuilder.append("&code=").append(URLLicense.aquireLicense(image.imageUrl)); + final String thumbLicense = URLLicense.aquireLicense(image.imageUrl); + final String fullPreviewLicense = URLLicense.aquireLicense(image.imageUrl); + final String baseThumbURL = thumbURLBuilder.toString(); + thumbURL = baseThumbURL + "&code=" + thumbLicense; + license = thumbLicense; + /* Not authenticated : full preview URL must be the same as thumb URL, but with a separate one-time license */ + fullPreviewURL = baseThumbURL + "&code=" + fullPreviewLicense; } - final String thumbURL = thumbURLBuilder.toString(); prop.putHTML("content_item_hrefCache", thumbURL); - /* Full size preview URL */ - if(fullViewingRights) { - prop.putHTML("content_item_hrefFullPreview", "ViewImage." + viewImageExt + "?isStatic=true&url=" + imageUrlstring); - } else { - /* Not authenticated : full preview URL must be the same as thumb URL */ - prop.putHTML("content_item_hrefFullPreview", thumbURL); - } + prop.putHTML("content_item_hrefFullPreview", fullPreviewURL); prop.putHTML("content_item_href", imageUrlstring); prop.putHTML("content_item_target", target); prop.put("content_item_code", license); |
