1import{C as k}from"./index-TN-eQrj9.js";import{a as O,c as N}from"./fileDropzone-CA_uaFcV.js";import{hashCopyButtonHtml as R,bindHashStyleCopy as z}from"./hashStyleCopy-9BQjpJfE.js";import{s as M}from"./buttonBusy-DoyqFlQ8.js";import"./_commonjsHelpers-BosuxZz1.js";import"./_commonjs-dynamic-modules-TDtrdbi3.js";import"./___vite-browser-external_commonjs-proxy-DRaEfepn.js";import"./transientNotice-Ds_di7lD.js";import"./ctFullscreenAd-wL9Ec5Hb.js";import"./app-Dsk0FNLH.js";const D=[{mime:"image/png",ext:"png",bytes:["89","50","4E","47","0D","0A","1A","0A"],label:"PNG"},{mime:"image/jpeg",ext:"jpg",bytes:["FF","D8","FF"],label:"JPEG"},{mime:"image/gif",ext:"gif",ascii:"GIF8",label:"GIF"},{mime:"application/pdf",ext:"pdf",ascii:"%PDF-",label:"PDF"},{mime:"application/zip",ext:"zip",bytes:["50","4B","03","04"],label:"ZIP"},{mime:"application/x-rar-compressed",ext:"rar",bytes:["52","61","72","21","1A","07"],label:"RAR"},{mime:"application/x-7z-compressed",ext:"7z",bytes:["37","7A","BC","AF","27","1C"],label:"7Z"},{mime:"audio/mpeg",ext:"mp3",ascii:"ID3",label:"MP3(ID3)"},{mime:"audio/wav",ext:"wav",ascii:"RIFF",extraAt:{offset:8,ascii:"WAVE"},label:"WAV"},{mime:"video/x-msvideo",ext:"avi",ascii:"RIFF",extraAt:{offset:8,ascii:"AVI "},label:"AVI"},{mime:"video/webm",ext:"webm",bytes:["1A","45","DF","A3"],label:"WEBM/MKV"},{mime:"audio/flac",ext:"flac",ascii:"fLaC",label:"FLAC"},{mime:"image/webp",ext:"webp",ascii:"RIFF",extraAt:{offset:8,ascii:"WEBP"},label:"WEBP"}],H={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",bmp:"image/bmp",heic:"image/heic",heif:"image/heif",mp4:"video/mp4",webm:"video/webm",mkv:"video/x-matroska",mov:"video/quicktime",avi:"video/x-msvideo",mp3:"audio/mpeg",wav:"audio/wav",m4a:"audio/mp4",aac:"audio/aac",flac:"audio/flac",ogg:"audio/ogg",opus:"audio/opus",pdf:"application/pdf",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",json:"application/json",xml:"application/xml",csv:"text/csv",txt:"text/plain",md:"text/markdown",html:"text/html"};function U(e){if(!Number.isFinite(e))return"-";if(e<1024)return`${e} B`;const t=["KB","MB","GB","TB"];let n=e/1024,i=0;for(;n>=1024&&i<t.length-1;)n/=1024,i+=1;return`${n.toFixed(2)} ${t[i]}`}function P(e){const t=String(e||"").lastIndexOf(".");return t<0?"":String(e).slice(t+1).toLowerCase()}function W(e,t=e.length){const n=e.slice(0,t);return Array.from(n).map(i=>i.toString(16).padStart(2,"0").toUpperCase()).join(" ")}function V(e,t=e.length){const n=e.slice(0,t);return Array.from(n).map(i=>i>=32&&i<=126?String.fromCharCode(i):".").join("")}function w(e){return String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function E(e,t,n){const i=e.slice(t,t+n);return Array.from(i).map(a=>String.fromCharCode(a)).join("")}function J(e){for(const t of D){let n=!0;if(t.bytes){if(e.length<t.bytes.length)n=!1;else for(let i=0;i<t.bytes.length;i+=1)if(e[i].toString(16).padStart(2,"0").toUpperCase()!==t.bytes[i]){n=!1;break}}else t.ascii?E(e,0,t.ascii.length)!==t.ascii&&(n=!1):n=!1;if(n&&t.extraAt&&E(e,t.extraAt.offset,t.extraAt.ascii.length)!==t.extraAt.ascii&&(n=!1),n)return t}return null}function L(e){return new DataView(e.buffer,e.byteOffset,e.byteLength)}function _(e){if(!(e[0]===255&&e[1]===216))return null;let t=2;for(;t+4<e.length&&e[t]===255;){const n=e[t+1],i=(e[t+2]<<8)+e[t+3];if(n===225){const a=t+4;return E(e,a,6)!=="Exif\0\0"?null:G(e,a+6)}if(n===218||i<=0)break;t+=2+i}return null}function G(e,t){try{const n=L(e),i=E(e,t,2)==="II",a=o=>n.getUint16(o,i),r=o=>n.getUint32(o,i);if(a(t+2)!==42)return null;const g=t+r(t+4),c=a(g),s={};for(let o=0;o<c;o+=1){const m=g+2+o*12,d=a(m),x=a(m+2),p=r(m+4),u=m+8,l=x===3&&p===1?u:t+r(u);if(d===274&&(s.orientation=x===3?a(l):null),d===306&&(s.dateTime=E(e,l,Math.min(p,32)).replace(/\0/g,"").trim()),d===271&&(s.make=E(e,l,Math.min(p,64)).replace(/\0/g,"").trim()),d===272&&(s.model=E(e,l,Math.min(p,64)).replace(/\0/g,"").trim()),d===34665){const h=t+r(u);Object.assign(s,q(e,h,i,t))}}return Object.keys(s).length?s:null}catch{return null}}function q(e,t,n,i){const a=L(e),r=o=>a.getUint16(o,n),g=o=>a.getUint32(o,n),c={},s=r(t);for(let o=0;o<s;o+=1){const m=t+2+o*12,d=r(m),x=r(m+2),p=g(m+4),u=i+g(m+8);d===36867&&(c.dateTimeOriginal=E(e,u,Math.min(p,32)).replace(/\0/g,"").trim()),d===40962&&x===4&&(c.pixelX=g(u)),d===40963&&x===4&&(c.pixelY=g(u))}return c}async function K(e){return e.arrayBuffer()}function X(e){return e.startsWith("text/")||e.includes("json")||e.includes("xml")||e.includes("yaml")}function Z(e,t,n){const i=(t||n||"").toLowerCase();return i.startsWith("image/")?"image":i.startsWith("video/")?"video":i.startsWith("audio/")?"audio":"none"}async function Y(e){return new Promise(t=>{const n=new Image;n.onload=()=>
1t({width:n.naturalWidth,height:n.naturalHeight}),n.onerror=()=>t(null),n.src=e})}async function Q(e,t){return new Promise(n=>{const i=document.createElement(t==="video"?"video":"audio");i.preload="metadata",i.onloadedmetadata=()=>{const a={duration:Number.isFinite(i.duration)?i.duration:null,resolution:t==="video"?`${i.videoWidth||0}x${i.videoHeight||0}`:null,width:t==="video"&&i.videoWidth||null,height:t==="video"&&i.videoHeight||null};n(a)},i.onerror=()=>n(null),i.src=e})}async function ee(e){const t=window.AudioContext||window.webkitAudioContext;if(!t)return null;const n=new t;try{const i=await n.decodeAudioData(e.slice(0));return{sampleRate:i.sampleRate,channels:i.numberOfChannels,duration:i.duration}}catch{return null}finally{await n.close()}}function te(e){return H[e]||""}function ne(e,t){return`<span class="badge ${e?"text-bg-success":"text-bg-warning"}">${w(t)}</span>`}function ie(e,t){const n=new Blob([JSON.stringify(e,null,2)],{type:"application/json;charset=utf-8"}),i=document.createElement("a");i.href=URL.createObjectURL(n),i.download=t,i.click(),URL.revokeObjectURL(i.href)}function I(e){const t=Object.entries(e||{}).filter(([,n])=>n!=null&&`${n}`!="");return t.length?`<div class="row g-2">${t.map(([n,i])=>`<div class="col-12 col-md-6"><div class="small text-body-secondary">${w(n)}</div><div class="small fw-semibold text-break">${w(i)}</div></div>`).join("")}</div>`:'<p class="small text-body-secondary mb-0">No data available.</p>'}function $(e,t,n=!0){return n?`<div class="card border-secondary-subtle mb-3"> 2 <div class="card-header py-2 bg-body-tertiary"><h3 class="h6 mb-0">${w(e)}</h3></div> 3 <div class="card-body py-2">${t}</div> 4 </div>`:""}async function ae(e){const t=k.lib.WordArray.create(e),n=k.MD5(t).toString(),i=await crypto.subtle.digest("SHA-256",e),a=Array.from(new Uint8Array(i)).map(r=>r.toString(16).padStart(2,"0")).join("");return{md5:n,sha256:a}}async function oe(e){var x;const t=P(e.name),n=te(t),i=e.type||"",a=URL.createObjectURL(e);let r=null;const g={name:e.name,size:`${U(e.size)} (${e.size.toLocaleString()} bytes)`,extension:t||"(none)",mimeFromBrowser:i||"(empty)",mimeFromExtension:n||"Unknown",lastModified:new Date(e.lastModified).toLocaleString()},c={},s={},o={},m=[];let d="none";try{r=await K(e);const p=new Uint8Array(r.slice(0,8192)),u=J(p),l=(u==null?void 0:u.mime)||"";d=Z(e,i,l||n),c.signatureName=(u==null?void 0:u.label)||"Unknown",c.signatureHex=W(p,32),c.signatureAscii=V(p,32),c.mimeDetected=l||"Unknown",c.actualType=l||i||n||"Unknown";const h=n||i,y=!h||!l||h.split(";")[0]===l.split(";")[0];c.extensionVsActual=y?"Likely match":"Mismatch";const b=await ae(r);if(c.md5=b.md5,c.sha256=b.sha256,d==="image"){const f=await Y(a);f&&(s.imageDimensions=`${f.width}x${f.height}`,s.width=f.width,s.height=f.height);const v=_(new Uint8Array(r.slice(0,512*1024)));v&&(s.exif=v)}if(d==="video"||d==="audio"){const f=await Q(a,d);f&&(f.duration!=null&&(s.durationSec=Number(f.duration).toFixed(3),f.duration>0&&(s.estimatedBitrateKbps=Math.round(e.size*8/f.duration/1e3))),f.resolution&&(s.resolution=f.resolution));const v=await ee(r);v&&(s.sampleRate=v.sampleRate,s.channels=v.channels,s.durationSec||(s.durationSec=Number(v.duration).toFixed(3))),s.codec=i||"Unknown (browser does not expose codec details)",s.fps=d==="video"?"Not reliably available from browser metadata API":null}const C=(l||i||n||"").toLowerCase();if(X(C)||["txt","json","xml","csv","md"].includes(t)){const f=await e.text(),v=f===""?0:f.split(/\r?\n/).length,T=(f.match(/\S+/g)||[]).length;if(o.characters=f.length,o.lines=v,o.words=T,t==="json"||C.includes("json"))try{const A=JSON.parse(f);o.jsonTopLevel=Array.isArray(A)?"array":typeof A,o.jsonKeys=!Array.isArray(A)&&A&&typeof A=="object"?Object.keys(A).length:null}catch{o.jsonParse="Invalid JSON"}if(t==="csv"||C.includes("csv")){const A=f.split(/\r?\n/).filter(S=>S.trim()!=="").length,B=f.split(/\r?\n/)[0]||"";o.csvRows=A,o.csvColumnsApprox=B?B.split(",").length:0}if(t==="xml"||C.includes("xml")){const B=new DOMParser().parseFromString(f,"application/xml");B.getElementsByTagName("parsererror").length>0?o.xmlParse="Invalid XML":(o.xmlRoot=((x=B.documentElement)==null?void 0:x.tagName)||null,o.xmlNodeCount=B.getElementsByTagName("*").length)}}return{raw:{basic:g,technical:c,media:s,other:o,errors:m},previewType:d,fileUrl:a}}catch(p){return m.push(p instanceof Error?p.message:"Unknown error"),{raw:{basic:g,technical:c,media:s,other:o,errors:m},previewType:"none",fileUrl:a}}}function re(e,t){return e==="image"?`<img src="${w(t)}" alt="preview" class="img-fluid rounded border border-secondary-subtle" />`:e==="video"?`<video src="${w(t)}" class="w-100 rounded border border-secondary-subtle" controls preload="metadata"></video>`:e==="audio"?`<audio src="${w(t)}" class="w-100" controls preload="metadata"></audio>`:'<p class="small text-body-secondary mb-0">Preview is not available for this file type.</p>'}function j(e){const t=document.getElementById("ctUfmToastBody"),n=document.getElementById("ctUfmToast");if(!t||!n||typeof bootstrap>"u"){window.alert(e);return}t.textContent=e,bootstrap.Toast.getOrCreateInstance(n).show()}function se(e,t,n,i){var h;const{raw:a,previewType:r,fileUrl:g}=n,c=a.technical.extensionVsActual!=="Mismatch",s=Object.keys(a.media||{}).length>0,o=Object.keys(a.other||{}).length>0,m=Array.isArray(a.errors)&&a.errors.length>0,d=JSON.stringify(a,null,2),x=`ctUfmCopy-${i}`,p=` 5 ${$("Basic Info",I(a.basic))} 6 ${$("Technical Info",` 7 <div class="mb-2">${ne(c,`Extension check: ${a.technical.extensionVsActual||"Unknown"}`)}</div> 8 ${I(a.technical)} 9 `)} 10 ${$("Media Info",I(a.media),s)} 11 ${$("Other Metadata",I(a.other),o)} 12 ${$("Preview",re(r,g),!0)} 13 ${$("Metadata (Raw JSON)",`<div class="ct-hash-output__shell rounded ct-ufm-json-shell"> 14 <textarea class="form-control form-control-sm font-monospace ct-hash-output__textarea ct-ufm-json-textarea" rows="12" readonly spellcheck="false">${w(d)}</textarea> 15 ${R(x,"Copy raw JSON metadata to clipboard",!1)} 16 </div>`,!0)} 17 ${$("Errors",`<ul class="small mb-0">${a.errors.map(y=>`<li>${w(y)}</li>`).join("")}</ul>`,m)} 18 `,u=document.createElement("div");
18u.className="card mb-3 border-secondary-subtle ct-ufm-file-card",u.innerHTML=` 19 <div class="card-header d-flex flex-wrap justify-content-between align-items-center gap-2 bg-body-tertiary"> 20 <div> 21 <h2 class="h5 mb-0 text-break">${w(t.name)}</h2> 22 <div class="small text-body-secondary">${w(U(t.size))}</div> 23 </div> 24 <div class="d-flex gap-2 align-items-center ms-auto flex-shrink-0"> 25 <button type="button" class="btn btn-primary btn-sm" data-role="download-json">Download metadata</button> 26 </div> 27 </div> 28 <div class="card-body">${p}</div> 29 `;const l=u.querySelector(`#${x}`);z(l,()=>d,y=>j(y)),(h=u.querySelector('[data-role="download-json"]'))==null||h.addEventListener("click",()=>{const y=t.name.replace(/[^\w.-]+/g,"_");ie(a,`${y}.metadata.json`)}),e.appendChild(u)}function ye(){if(!document.getElementById("ct-file-meta-root"))return;const t=document.getElementById("ctUfmInput"),n=document.getElementById("ctUfmDropZone"),i=document.getElementById("ctUfmDropCaption"),a=document.getElementById("ctUfmDropCardBody"),r=document.getElementById("ctUfmAnalyze"),g=document.getElementById("ctUfmClear"),c=document.getElementById("ctUfmResults"),s=document.getElementById("ctUfmResultsEmpty"),o=document.getElementById("ctUfmOutputHint"),m=document.getElementById("ctUfmStatus");if(!t||!n||!c||!s||!r||!g||!m)return;let d=0;const x=(l="Waiting for a file")=>{c.innerHTML="",c.classList.add("d-none"),c.hidden=!0,s.classList.remove("d-none"),s.hidden=!1,o&&(o.textContent=l)},p=(l="Showing metadata")=>{s.classList.add("d-none"),s.hidden=!0,c.classList.remove("d-none"),c.hidden=!1,o&&(o.textContent=l)},u=async()=>{const l=Array.from(t.files||[]);if(!l.length){x("Waiting for a file"),r&&(r.disabled=!0),m.textContent="No files selected.";return}const h=++d;c.innerHTML="",p("Analyzingâ¦");const y=document.querySelector(".ct-ufm-workspace__out");y==null||y.classList.add("is-working"),r instanceof HTMLButtonElement&&M(r,!0,{loadingLabel:"Analyzingâ¦"}),m.textContent="Analyzing files...";try{for(let b=0;b<l.length;b+=1){if(h!==d)return;const C=l[b],F=await oe(C);if(h!==d)return;se(c,C,F,b),m.textContent=`Analyzed ${b+1}/${l.length} file(s)`}h===d&&(m.textContent=`Done. ${l.length} file(s) analyzed.`,p(`${l.length} file(s) analyzed`))}catch(b){h===d&&(m.textContent="Analysis failed for one or more files.",j(b instanceof Error?b.message:"Failed to analyze files."))}finally{h===d&&(y==null||y.classList.remove("is-working"),r instanceof HTMLButtonElement&&(M(r,!1),r.disabled=l.length===0))}};O({input:t,drop:n,captionEl:i,dragContainer:a,accumulate:!1,onInvalidFile:l=>j(l),onChange:async()=>{var h;const l=((h=t.files)==null?void 0:h.length)||0;r&&(r.disabled=l===0),g.disabled=l===0,m.textContent=l>0?`${l} file(s) selected`:"No files selected.",l>0?await u():x("Waiting for a file")}}),g.addEventListener("click",()=>{N(t),d+=1,x("Waiting for a file"),r&&(r.disabled=!0),g.disabled=!0,m.textContent="No files selected."}),r==null||r.addEventListener("click",async()=>{await u()})}export{ye as initUniversalFileMetadataViewerPage};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.