Migrate movie info downloader
This commit is contained in:
@@ -58,6 +58,7 @@
|
|||||||
var T = EditorTables;
|
var T = EditorTables;
|
||||||
var L = EditorLists;
|
var L = EditorLists;
|
||||||
var D = EditorDates;
|
var D = EditorDates;
|
||||||
|
var M = EditorMovie;
|
||||||
|
|
||||||
var actions = {
|
var actions = {
|
||||||
save: function () { form.submit(); },
|
save: function () { form.submit(); },
|
||||||
@@ -100,6 +101,7 @@
|
|||||||
tbldeleterow: function () { applyTableOp(T.deleteRow); },
|
tbldeleterow: function () { applyTableOp(T.deleteRow); },
|
||||||
dateiso: function () { insertAtCursor(D.isoDate()); },
|
dateiso: function () { insertAtCursor(D.isoDate()); },
|
||||||
datelong: function () { insertAtCursor(D.longDate()); },
|
datelong: function () { insertAtCursor(D.longDate()); },
|
||||||
|
movie: function () { M.run(textarea); },
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Keyboard shortcut registration ---
|
// --- Keyboard shortcut registration ---
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
window.EditorMovie = (function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// OMDb API key. Shipped to the browser; acceptable for a single-user LAN tool.
|
||||||
|
var OMDB_API_KEY = 'c906744f';
|
||||||
|
|
||||||
|
var BEGIN = '<!-- BEGIN MOVIE -->';
|
||||||
|
var END = '<!-- END MOVIE -->';
|
||||||
|
|
||||||
|
function firstHeading(text) {
|
||||||
|
var m = text.match(/^#{1,6}\s+(.+?)\s*$/m);
|
||||||
|
return m ? m[1].trim() : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTitleYear(raw) {
|
||||||
|
var m = raw.match(/^(.+?)\s*\((\d{4})\)\s*$/);
|
||||||
|
return m ? { title: m[1].trim(), year: m[2] } : { title: raw.trim(), year: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
function safe(v) { return (!v || v === 'N/A') ? '' : String(v); }
|
||||||
|
|
||||||
|
function esc(s) {
|
||||||
|
return String(s)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBlock(m) {
|
||||||
|
var out = [BEGIN, '<aside class="movie-info">'];
|
||||||
|
if (m.Poster && m.Poster !== 'N/A') {
|
||||||
|
out.push('<img class="movie-poster" src="' + esc(m.Poster) +
|
||||||
|
'" alt="' + esc(safe(m.Title)) + ' poster">');
|
||||||
|
}
|
||||||
|
out.push('<table>');
|
||||||
|
[
|
||||||
|
['Title', m.Title],
|
||||||
|
['Year', m.Year],
|
||||||
|
['Runtime', m.Runtime],
|
||||||
|
['Genre', m.Genre],
|
||||||
|
['Director', m.Director],
|
||||||
|
['Cast', m.Actors],
|
||||||
|
['Plot', m.Plot],
|
||||||
|
].forEach(function (r) {
|
||||||
|
out.push('<tr><th>' + r[0] + '</th><td>' + esc(safe(r[1])) + '</td></tr>');
|
||||||
|
});
|
||||||
|
out.push('</table>', '</aside>', END);
|
||||||
|
return out.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertOrReplace(ta, markup) {
|
||||||
|
var t = ta.value || '';
|
||||||
|
var b = t.indexOf(BEGIN);
|
||||||
|
var e = t.indexOf(END);
|
||||||
|
if (b !== -1 && e !== -1 && e > b) {
|
||||||
|
ta.value = t.slice(0, b) + markup + t.slice(e + END.length);
|
||||||
|
} else {
|
||||||
|
var h = t.match(/^#{1,6}\s+.+?\s*$/m);
|
||||||
|
if (h) {
|
||||||
|
var idx = t.indexOf(h[0]) + h[0].length;
|
||||||
|
ta.value = t.slice(0, idx) + '\n\n' + markup + t.slice(idx);
|
||||||
|
} else {
|
||||||
|
ta.value = markup + (t ? '\n\n' + t : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ta.dispatchEvent(new Event('input'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchMovie(title, year) {
|
||||||
|
var url = 'https://www.omdbapi.com/?apikey=' + encodeURIComponent(OMDB_API_KEY) +
|
||||||
|
'&type=movie&t=' + encodeURIComponent(title);
|
||||||
|
if (year) url += '&y=' + encodeURIComponent(year);
|
||||||
|
return fetch(url).then(function (r) { return r.json(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMessage(title, msg) {
|
||||||
|
openModal({ title: title, body: msg, confirm: { label: 'OK' } });
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(textarea) {
|
||||||
|
if (!OMDB_API_KEY) {
|
||||||
|
showMessage('Movie import', 'OMDb API key is not set. Edit assets/editor/movie.js.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var input = document.createElement('input');
|
||||||
|
input.type = 'text';
|
||||||
|
input.className = 'modal-input';
|
||||||
|
input.placeholder = 'Title, optionally with (YYYY)';
|
||||||
|
input.value = firstHeading(textarea.value || '');
|
||||||
|
|
||||||
|
openModal({
|
||||||
|
title: 'Import movie',
|
||||||
|
body: input,
|
||||||
|
confirm: {
|
||||||
|
label: 'IMPORT',
|
||||||
|
onConfirm: function () {
|
||||||
|
var raw = input.value.trim();
|
||||||
|
if (!raw) return;
|
||||||
|
var parsed = parseTitleYear(raw);
|
||||||
|
closeModal();
|
||||||
|
fetchMovie(parsed.title, parsed.year)
|
||||||
|
.then(function (data) {
|
||||||
|
if (!data || data.Response === 'False') {
|
||||||
|
showMessage('Not found',
|
||||||
|
(data && data.Error) || 'Movie not found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
insertOrReplace(textarea, buildBlock(data));
|
||||||
|
})
|
||||||
|
.catch(function () {
|
||||||
|
showMessage('Import failed', 'OMDb lookup failed.');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { run: run };
|
||||||
|
})();
|
||||||
@@ -62,12 +62,15 @@
|
|||||||
<span class="toolbar-sep"></span>
|
<span class="toolbar-sep"></span>
|
||||||
<button type="button" class="btn btn-tool dropdown" data-action="tbldrop" title="Table (T)">T▾</button>
|
<button type="button" class="btn btn-tool dropdown" data-action="tbldrop" title="Table (T)">T▾</button>
|
||||||
<button type="button" class="btn btn-tool dropdown" data-action="datedrop" title="Insert date (D/W)">D▾</button>
|
<button type="button" class="btn btn-tool dropdown" data-action="datedrop" title="Insert date (D/W)">D▾</button>
|
||||||
|
<span class="toolbar-sep"></span>
|
||||||
|
<button type="button" class="btn btn-tool" data-action="movie" data-key="V" title="Import movie (V)">MV</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea name="content" id="editor" autofocus>{{.RawContent}}</textarea>
|
<textarea name="content" id="editor" autofocus>{{.RawContent}}</textarea>
|
||||||
</form>
|
</form>
|
||||||
<script src="/_/editor/lists.js"></script>
|
<script src="/_/editor/lists.js"></script>
|
||||||
<script src="/_/editor/tables.js"></script>
|
<script src="/_/editor/tables.js"></script>
|
||||||
<script src="/_/editor/dates.js"></script>
|
<script src="/_/editor/dates.js"></script>
|
||||||
|
<script src="/_/editor/movie.js"></script>
|
||||||
<script src="/_/editor.js"></script>
|
<script src="/_/editor.js"></script>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{if .Content}}
|
{{if .Content}}
|
||||||
|
|||||||
@@ -584,6 +584,31 @@ hr {
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === Movie info box === */
|
||||||
|
.movie-info {
|
||||||
|
margin: 0.75rem 0;
|
||||||
|
}
|
||||||
|
.movie-info::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.movie-info .movie-poster {
|
||||||
|
float: right;
|
||||||
|
max-width: 200px;
|
||||||
|
margin: 0 0 0.75rem 1rem;
|
||||||
|
}
|
||||||
|
.movie-info table {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.movie-info .movie-poster {
|
||||||
|
float: none;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* === Diary Calendar === */
|
/* === Diary Calendar === */
|
||||||
.diary-cal {
|
.diary-cal {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
Reference in New Issue
Block a user