Add "tools" section with exif-to-frontmatter tool
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
parent
0f6582d2ce
commit
ac8d9bc241
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Tools"
|
||||
---
|
||||
|
@ -0,0 +1,203 @@
|
||||
---
|
||||
title: "EXIF to Frontmatter"
|
||||
date: "2023-02-04T18:23:53.000Z"
|
||||
---
|
||||
|
||||
<p>
|
||||
I use this to extract EXIF data from pictures and generate the necessary
|
||||
Frontmatter for my Hugo "picture" pages. The picture isn't uploaded anywhere;
|
||||
the data is extracted on the client-side JS using <a
|
||||
href="https://github.com/exif-js/exif-js">exif.js</a>.
|
||||
</p>
|
||||
|
||||
<input type="file" multiple id="pic" accept="image/*" />
|
||||
|
||||
<!-- <div id="dropzone"
|
||||
style="height: 200px; width: 100%; border: 1px solid #000"></div> -->
|
||||
|
||||
<div id="results"></div>
|
||||
|
||||
<script src="/js/exif.js"></script>
|
||||
|
||||
<script>
|
||||
( function( doc ) {
|
||||
'use strict';
|
||||
|
||||
let output = doc.querySelector( "#results" ),
|
||||
|
||||
/**
|
||||
* Build frontmatter DOM template out of exif data
|
||||
*/
|
||||
getFrontMatterContainer = function ( exif ) {
|
||||
let image = "tmp",
|
||||
template = doc.createElement( "template" ),
|
||||
html = `
|
||||
<div class="result">
|
||||
<img src="${image}" />
|
||||
<pre class="meta">
|
||||
---
|
||||
title: ""
|
||||
date: ""
|
||||
|
||||
resources:
|
||||
- name: original
|
||||
title: ""
|
||||
src: images/original.jpg
|
||||
|
||||
datetaken: "${exif.dateTaken}"
|
||||
address1: ""
|
||||
address2: ""
|
||||
latitude: "${exif.latitude}"
|
||||
longitude: "${exif.longitude}"
|
||||
size: "${exif.size}"
|
||||
resolution: "${exif.width} x ${exif.height}"
|
||||
camera: "${exif.make} ${exif.model}"
|
||||
---
|
||||
</pre>
|
||||
</div>
|
||||
`
|
||||
template.innerHTML = html.trim();
|
||||
|
||||
return template.content.firstChild;
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for files picked from file picker.
|
||||
*/
|
||||
processInput = function() {
|
||||
let files = this.files,
|
||||
len = files.length,
|
||||
i;
|
||||
|
||||
for ( i = 0; i < len; i += 1 ) {
|
||||
process( files[ i ] );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Entry method to process a file.
|
||||
*/
|
||||
process = function( file ) {
|
||||
extractExif( file )
|
||||
.then( function( exif ) {
|
||||
let container = getFrontMatterContainer( exif ),
|
||||
reader = new FileReader(),
|
||||
img = container.querySelector( "img" );
|
||||
|
||||
output.appendChild( container );
|
||||
|
||||
reader.addEventListener( "load", function() {
|
||||
img.src = reader.result;
|
||||
} );
|
||||
|
||||
reader.readAsDataURL( file );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert bytes to easy to read format
|
||||
*/
|
||||
readableBytes = function( bytes ) {
|
||||
let i = Math.floor( Math.log( bytes ) / Math.log( 1024 ) ),
|
||||
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
return ( bytes / Math.pow( 1024, i ) ).toFixed( 2 ) *
|
||||
1 + " " + sizes[ i ];
|
||||
},
|
||||
|
||||
/**
|
||||
* For a given file, extract exif data.
|
||||
*/
|
||||
extractExif = function( file ) {
|
||||
const size = readableBytes( file.size );
|
||||
|
||||
return new Promise( function( resolve, reject ) {
|
||||
EXIF.getData( file, function() {
|
||||
let exif = EXIF.getAllTags( this ),
|
||||
latitude = "",
|
||||
longitude = "",
|
||||
dateTaken = "",
|
||||
info = {};
|
||||
|
||||
if ( exif.DateTimeOriginal ) {
|
||||
dateTaken = convertDate( exif.DateTimeOriginal );
|
||||
}
|
||||
|
||||
if ( exif.GPSLatitude ) {
|
||||
latitude = convertCoords( exif.GPSLatitudeRef,
|
||||
exif.GPSLatitude );
|
||||
}
|
||||
|
||||
if ( exif.GPSLongitude ) {
|
||||
longitude = convertCoords( exif.GPSLongitudeRef,
|
||||
exif.GPSLongitude );
|
||||
}
|
||||
|
||||
info = {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
dateTaken: dateTaken,
|
||||
width: exif.PixelXDimension,
|
||||
height: exif.PixelYDimension,
|
||||
make: exif.Make || "",
|
||||
model: exif.Model || "",
|
||||
size: size
|
||||
};
|
||||
|
||||
resolve( info );
|
||||
} );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler when dropping files on the dropzone.
|
||||
*/
|
||||
processDroppedImages = function( e ) {
|
||||
e.preventDefault();
|
||||
|
||||
let files = e.dataTransfer.files,
|
||||
dataUrl;
|
||||
|
||||
forEach( function( file ) {
|
||||
process( file );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert date to ISO format.
|
||||
*/
|
||||
convertDate = function( datetime ) {
|
||||
let arr = datetime.split( " " ),
|
||||
date = arr[ 0 ].replace(/:/g, '-'),
|
||||
time = arr[ 1 ],
|
||||
d = new Date( date + " " + time);
|
||||
|
||||
return d.toISOString();
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert coordinate (lat or lon) from degrees to decimal.
|
||||
*/
|
||||
convertCoords = function( ref, coord ) {
|
||||
let direction = 1,
|
||||
decimalCoords;
|
||||
|
||||
if ( ref === 'S' || ref === 'W' ) {
|
||||
direction = -1;
|
||||
}
|
||||
|
||||
decimalCoords = coord[ 0 ] + ( coord[ 1 ] / 60 ) +
|
||||
( coord[ 2 ] / 3600 );
|
||||
|
||||
return decimalCoords * direction;
|
||||
};
|
||||
|
||||
/*doc.querySelector( "#dropzone" )
|
||||
.addEventListener( "drop", processDroppedImages );*/
|
||||
|
||||
doc.querySelector( "#pic" )
|
||||
.addEventListener( "change", processInput );
|
||||
|
||||
}( document ) );
|
||||
</script>
|
||||
|
@ -0,0 +1,96 @@
|
||||
{{ define "tags" }}
|
||||
|
||||
{{ $taxo := "tags" }} <!-- Use the plural form here -->
|
||||
<ul id="{{ $taxo }}" class="entry-tags">
|
||||
{{ range .Param $taxo }}
|
||||
{{ $name := . }}
|
||||
{{ with $.Site.GetPage (printf "/%s/%s" $taxo ($name | urlize)) }}
|
||||
<li> <a href="{{ .Permalink }}">{{ $name }}</a></li>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ define "byline" }}
|
||||
<!-- Author info -->
|
||||
<div class="entry-author h-card">
|
||||
<a class="u-url" href="http://chromic.org">
|
||||
<img alt="Chimo bio photo" class="bio-photo u-photo"
|
||||
src="/images/bio-photo.png" />
|
||||
<span class="by-line">
|
||||
<span class="sr-only">By</span>
|
||||
<span class="p-author p-name" href="https://chromic.org">Chimo</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Date published -->
|
||||
<div class="entry-date date published">
|
||||
<span title="Date published">
|
||||
<i class="fa fa-calendar-o"></i>
|
||||
<span class="sr-only">Date published</span>
|
||||
</span>
|
||||
<time class="dt-published" datetime="{{ .PublishDate }}">
|
||||
{{ .PublishDate| dateFormat "Jan. 2, 2006" }}
|
||||
</time>
|
||||
</div>
|
||||
|
||||
<!-- Date modified -->
|
||||
<div class="entry-date date modified">
|
||||
<span title="Date modified">
|
||||
<i class="fa fa-pencil"></i>
|
||||
<span class="sr-only">Date modified</span>
|
||||
</span>
|
||||
<time class="dt-updated" datetime="{{ .Lastmod }}">
|
||||
{{ .Lastmod | dateFormat "Jan. 2, 2006" }}
|
||||
</time>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
{{ .Content }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "webmentions" }}
|
||||
{{ $webmentions := .Resources.Match "webmentions/*" }}
|
||||
{{ $nb_wm := len $webmentions }}
|
||||
|
||||
<section class="webmentions">
|
||||
<!--<form action="https://webmention.chromic.org/chromic.org/webmention"
|
||||
class="webmention-form" method="post" id="webmention_form">
|
||||
|
||||
<label class="webmention-form__question" for="webmention-url">
|
||||
Have you written a response to this? Let me know the URL:
|
||||
</label>
|
||||
|
||||
<div class="form-group">
|
||||
<input class="webmention-form__url" id="webmention-url" name="source"
|
||||
type="url">
|
||||
|
||||
<input class="button btn" type="submit" value="Send Webmention" />
|
||||
</div>
|
||||
|
||||
<input name="target" type="hidden" value="{{ .Permalink }}">
|
||||
</form>-->
|
||||
|
||||
{{ if ne $nb_wm 0 }}
|
||||
<h1>Webmentions</h1>
|
||||
|
||||
<ol class="webmention__list" id="webmentions">
|
||||
{{ range sort $webmentions ".Params.publishdate" "desc" }}
|
||||
{{ if eq .Params.type "like-of" }}
|
||||
<li class="webmention webmention--like">{{ partial "webmentions/like.html" . }}</li>
|
||||
{{ else if eq .Params.type "repost-of" }}
|
||||
<li class="webmention webmention--repost">{{ partial "webmentions/repost.html" . }}</li>
|
||||
{{ else if eq .Params.type "bookmark-of" }}
|
||||
<li class="webmention webmention--bookmark">{{ partial "webmentions/bookmark.html" . }}</li>
|
||||
{{ else }}
|
||||
<li class="webmention webmention--reply">{{ partial "webmentions/reply.html" . }}</li>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</ol>
|
||||
{{ end }}
|
||||
</section>
|
||||
{{ end }}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue