From b9fe935e983878258c19c82214bf49195f07bede Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Mon, 29 Dec 2025 18:06:51 +0900 Subject: [PATCH] ... --- .../controllers/SynologyProxyController.kt | 45 ++++ src/main/resources/static/js/exif-js.js | 8 + .../resources/templates/content/home.html | 2 +- .../templates/content/slideshow.html | 251 ++++++++++-------- 4 files changed, 191 insertions(+), 115 deletions(-) create mode 100644 src/main/resources/static/js/exif-js.js diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/SynologyProxyController.kt b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/SynologyProxyController.kt index 0909043..1083710 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/SynologyProxyController.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/SynologyProxyController.kt @@ -61,6 +61,7 @@ class SynologyProxyController { } } + // 1. [수정] list 메서드: additional 제거 (에러 해결) @PostMapping("/list") suspend fun list(@RequestBody payload: Map): String { try { @@ -70,6 +71,8 @@ class SynologyProxyController { val limit = payload["limit"].toString() val baseUrl = buildBaseUrl(nasAddress) + + // 순수하게 목록만 요청 val url = "$baseUrl/webapi/entry.cgi?api=SYNO.Foto.Browse.Item&version=1&method=list&limit=$limit&offset=$offset&_sid=$sid" logger.info("List Request URL: $url") @@ -80,6 +83,48 @@ class SynologyProxyController { } } + // 2. [추가] info 메서드: 사진 1장의 상세 정보(날짜, 위치) 조회 + // [수정] 유효한 additional 항목("exif", "gps")만 요청하도록 변경 + @PostMapping("/info") + suspend fun getInfo(@RequestBody payload: Map): String { + try { + val nasAddress = payload["nasAddress"].toString() + val sid = payload["sid"].toString() + val id = payload["id"].toString() + + val baseUrl = buildBaseUrl(nasAddress) + + // [핵심 변경] "address", "taken_time"은 유효하지 않음 -> "exif", "gps"로 변경 + // ["exif","gps"] -> %5B%22exif%22%2C%22gps%22%5D + val additionalEncoded = "%5B%22exif%22%2C%22gps%22%5D" + + val urlString = "$baseUrl/webapi/entry.cgi?api=SYNO.Foto.Browse.Item&version=1&method=get&id=$id&additional=$additionalEncoded&_sid=$sid" + + val uri = java.net.URI(urlString) + + return webClient.get().uri(uri).retrieve().awaitBody() + } catch (e: Exception) { + logger.error("Info Error: ", e) + throw e + } + } + + @GetMapping("/geocode") + suspend fun geocode( + @RequestParam lat: String, + @RequestParam lon: String + ): String { + // OpenStreetMap API URL + val url = "https://nominatim.openstreetmap.org/reverse?format=json&lat=$lat&lon=$lon&zoom=10&accept-language=ko" + + // Nominatim은 User-Agent 헤더가 필수입니다. + return webClient.get() + .uri(url) + .header("User-Agent", "MyNasSlideshow/1.0 (contact@example.com)") // 임의의 식별 문자열 + .retrieve() + .awaitBody() + } + @GetMapping("/image") suspend fun getImage( @RequestParam nasAddress: String, diff --git a/src/main/resources/static/js/exif-js.js b/src/main/resources/static/js/exif-js.js new file mode 100644 index 0000000..d022c4b --- /dev/null +++ b/src/main/resources/static/js/exif-js.js @@ -0,0 +1,8 @@ +/** + * Minified by jsDelivr using Terser v5.37.0. + * Original file: /npm/exif-js@2.3.0/exif.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +(function(){var e=!1,t=function(e){return e instanceof t?e:this instanceof t?void(this.EXIFwrapped=e):new t(e)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=t),exports.EXIF=t):this.EXIF=t;var r=t.Tags={36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubsecTime",37521:"SubsecTimeOriginal",37522:"SubsecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"ISOSpeedRatings",34856:"OECF",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRation",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",40965:"InteroperabilityIFDPointer",42016:"ImageUniqueID"},i=t.TiffTags={256:"ImageWidth",257:"ImageHeight",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer",40965:"InteroperabilityIFDPointer",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright"},o=t.GPSTags={0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential"},a=t.IFD1Tags={256:"ImageWidth",257:"ImageHeight",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",273:"StripOffsets",274:"Orientation",277:"SamplesPerPixel",278:"RowsPerStrip",279:"StripByteCounts",282:"XResolution",283:"YResolution",284:"PlanarConfiguration",296:"ResolutionUnit",513:"JpegIFOffset",514:"JpegIFByteCount",529:"YCbCrCoefficients",530:"YCbCrSubSampling",531:"YCbCrPositioning",532:"ReferenceBlackWhite"},s=t.StringValues={ExposureProgram:{0:"Not defined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Not defined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},Components:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"}};function u(e){return!!e.exifdata}function l(n,r){function i(i){var o=c(i);n.exifdata=o||{};var a=function(t){var n=new DataView(t);e;if(255!=n.getUint8(0)||216!=n.getUint8(1))return!1;var r=2,i=t.byteLength,o=function(e,t){return 56===e.getUint8(t)&&66===e.getUint8(t+1)&&73===e.getUint8(t+2)&&77===e.getUint8(t+3)&&4===e.getUint8(t+4)&&4===e.getUint8(t+5)};for(;r")+8,c=(u=u.substring(u.indexOf("4?g:t+8,a=[],u=0;u4?g:t+8,f-1);case 3:if(1==f)return e.getUint16(t+8,!i);for(o=f>2?g:t+8,a=[],u=0;ue.byteLength)return{};var o=g(e,t,t+i,a,r);if(o.Compression)switch(o.Compression){case 6:if(o.JpegIFOffset&&o.JpegIFByteCount){var s=t+o.JpegIFOffset,u=o.JpegIFByteCount;o.blob=new Blob([new Uint8Array(e.buffer,s,u)],{type:"image/jpeg"})}break;case 1:console.log("Thumbnail image format is TIFF, which is not implemented.");break;default:console.log("Unknown thumbnail image format '%s'",o.Compression)}else 2==o.PhotometricInterpretation&&console.log("Thumbnail image format is RGB, which is not implemented.");return o}(e,f,m,n),u}function S(e){var t={};if(1==e.nodeType){if(e.attributes.length>0){t["@attributes"]={};for(var n=0;n0)for(var n=0;n diff --git a/src/main/resources/templates/content/slideshow.html b/src/main/resources/templates/content/slideshow.html index 32a8c3b..8abf65a 100644 --- a/src/main/resources/templates/content/slideshow.html +++ b/src/main/resources/templates/content/slideshow.html @@ -4,82 +4,54 @@ BUM's NAS Slideshow - ✕ Close -

Synology Login

- - @@ -88,7 +60,19 @@
-
+
+ +
+
+
+ + +
+
+ +
@@ -97,57 +81,40 @@