Prompt user for OMDb API key and persist in localStorage

Removes the hardcoded key in favour of a per-browser key entered via
modal on first movie import. An Invalid API key response from OMDb
clears the stored key and re-prompts, preserving the typed title.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-08 15:01:28 +02:00
parent a30fa8b061
commit c2c4960ee7
+66 -10
View File
@@ -1,8 +1,7 @@
window.EditorMovie = (function () {
'use strict';
// OMDb API key. Shipped to the browser; acceptable for a single-user LAN tool.
var OMDB_API_KEY = '';
var STORAGE_KEY = 'omdb-api-key';
var BEGIN = '<!-- BEGIN MOVIE -->';
var END = '<!-- END MOVIE -->';
@@ -73,8 +72,8 @@ window.EditorMovie = (function () {
}
}
function fetchMovie(title, year) {
var url = 'https://www.omdbapi.com/?apikey=' + encodeURIComponent(OMDB_API_KEY) +
function fetchMovie(key, title, year) {
var url = 'https://www.omdbapi.com/?apikey=' + encodeURIComponent(key) +
'&type=movie&t=' + encodeURIComponent(title);
if (year) url += '&y=' + encodeURIComponent(year);
return fetch(url).then(function (r) { return r.json(); });
@@ -84,17 +83,54 @@ window.EditorMovie = (function () {
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;
function promptForKey(rejected, onSaved) {
var body = document.createDocumentFragment();
if (rejected) {
var notice = document.createElement('p');
notice.textContent = 'The previously stored key was rejected by OMDb.';
body.appendChild(notice);
}
var info = document.createElement('p');
info.appendChild(document.createTextNode('Enter your OMDb API key. Get one at '));
var link = document.createElement('a');
link.href = 'https://www.omdbapi.com/apikey.aspx';
link.target = '_blank';
link.rel = 'noopener';
link.textContent = 'omdbapi.com/apikey.aspx';
info.appendChild(link);
info.appendChild(document.createTextNode('.'));
body.appendChild(info);
var input = document.createElement('input');
input.type = 'text';
input.className = 'modal-input';
input.placeholder = 'OMDb API key';
body.appendChild(input);
openModal({
title: 'OMDb API key required',
body: body,
confirm: {
label: 'SAVE',
onConfirm: function () {
var key = input.value.trim();
if (!key) return;
localStorage.setItem(STORAGE_KEY, key);
closeModal();
onSaved(key);
},
},
});
}
function importWithKey(textarea, key, initialTitle) {
var input = document.createElement('input');
input.type = 'text';
input.className = 'modal-input';
input.placeholder = 'Title, optionally with (YYYY)';
input.value = firstHeading(textarea.value || '');
input.value = initialTitle;
openModal({
title: 'Import movie',
@@ -106,8 +142,16 @@ window.EditorMovie = (function () {
if (!raw) return;
var parsed = parseTitleYear(raw);
closeModal();
fetchMovie(parsed.title, parsed.year)
fetchMovie(key, parsed.title, parsed.year)
.then(function (data) {
if (data && data.Response === 'False' &&
data.Error === 'Invalid API key!') {
localStorage.removeItem(STORAGE_KEY);
promptForKey(true, function (newKey) {
importWithKey(textarea, newKey, raw);
});
return;
}
if (!data || data.Response === 'False') {
showMessage('Not found',
(data && data.Error) || 'Movie not found.');
@@ -123,5 +167,17 @@ window.EditorMovie = (function () {
});
}
function run(textarea) {
var initialTitle = firstHeading(textarea.value || '');
var key = localStorage.getItem(STORAGE_KEY);
if (!key) {
promptForKey(false, function (newKey) {
importWithKey(textarea, newKey, initialTitle);
});
return;
}
importWithKey(textarea, key, initialTitle);
}
return { run: run };
})();