I managed to make the code work, but have bumped into another issue: whenever it encounters a broken link it just stops, instead of continuing forward. What should I do?
I have tried you javascript code for bulk upload from a file of urls.
It's working, but not as well as I had hoped : it's actually adding snapshots from the url's webpages, while what I want is the whole of the references mentioned on each url's webpages (the zotero connector of each of these page is actually offering a list of references to import) to be imported into zotero. Example of one these pages : https://www.idref.fr/026766701.
I tried the initial code that was posted by dstillman and it worked. But if I insert a list of journal article URLs, they will be stored as webpages, not as journal articles. I would need them to be stored as journal articles and, ideally, the abstracts should automatically be downloaded as well. Is it possible to adjust the code in this way somehow?
Thanks for posting this. I also have the need for bulk import :-) Helping a friend writing his PhD dissertation. He collected a bunch of references as http links and now needs to add hundreds of such items to his formal reference list via zotero.
As far as I understand and tested the script you posted works by importing everything into the main collection. Is there a way to import the items in the list into a specific collection? He has his references split in several subcollections.
1. Generate a message for the url whether it was successfully added or not 2. At the end create a new file listing the urls that were not successfully added 3. Add the document in the collection that is selected in Zotero client instead of "My Library"?
await Zotero.HTTP.loadDocuments( urls, async function (doc) { console.log(doc.location.href) var parser = new DOMParser(); var safeDoc = Zotero.HTTP.wrapDocument( parser.parseFromString(doc.documentElement.outerHTML, 'text/html'), doc.location.href ); var translate = new Zotero.Translate.Web(); translate.setDocument(safeDoc); var translators = await translate.getTranslators(); if (translators.length) { translate.setTranslator(translators[0]); try { await translate.translate(); return; } catch (e) {} } try { await ZoteroPane.addItemFromDocument(safeDoc); status[safeDoc.location.href] = "success"; return; } catch (e) {} } )
// Convert the associative array to a string with key-value pairs separated by newlines const data = Object.entries(status).map(([key, value]) => `${key}: ${value}`).join('\n');
// Write the data to the file Zotero.File.writeFile(log, data, (err) => { if (err) { console.error('Error writing file:', err); return; } console.log('File saved successfully'); });
But it gives error that there is no function Zotero.File.writeFile.
@dstillman: With little more searching on this forum, I could solve the writing to file issue. However, I get "fail" for all urls. Even through there are urls successfully saved.
Please help with this version.
var path = '/Users/pals2/ZoteroImportUrls.txt'; var log = '/Users/pals2/ZoteroImportUrls.log';
var status = {};
var urls = Zotero.File.getContents(path).split('\n').map(url => url);
async function saveStatus() { // Convert the associative array to a string with key-value pairs separated by newlines const data = Object.entries(status).map(([key, value]) => `${key}: ${value}`).join('\n');
// Write the data to the file await Zotero.File.putContentsAsync(log, data); }
await Promise.all([ Zotero.HTTP.loadDocuments( urls, async function (doc) { console.log(doc.location.href) var parser = new DOMParser(); var safeDoc = Zotero.HTTP.wrapDocument( parser.parseFromString(doc.documentElement.outerHTML, 'text/html'), doc.location.href ); var translate = new Zotero.Translate.Web(); translate.setDocument(safeDoc); var translators = await translate.getTranslators(); if (translators.length) { translate.setTranslator(translators[0]); try { await translate.translate(); status[safeDoc.location.href] = "success"; return; } catch (e) {} } try { await ZoteroPane.addItemFromDocument(safeDoc); status[safeDoc.location.href] = "success"; return; } catch (e) {
Hey so I did a bunch of upgrades a while back to this script and used it for my own import. It worked but not with full metadata and the proper item types, instead defaulting to "Web page". Now I upgraded it again to work with Zotero 9.0.6 and seems to properly get rich metadata. The downside is that its going to take time to load and run compared to the version shown here.
I don't recall the details but I somehow replaced the mere parsing of the source HTML and traverse that DOM, with a hiddenbrowser to run all of the page javascript and traverse the resulting DOM. I had tried to post a detailed version that day and kept getting refused or kicked out from the forums.
Here's my script, feel free to use:
var path = '/path/to/bookmarks-list.txt'; var log = '/path/to/ZoteroImportUrls.log';
var status = {}; var urls = Zotero.File.getContents(path).split('\n').filter(url => url.trim() !== '');
async function saveStatus() { const data = Object.entries(status).map(([key, value]) => `${key}: ${value}`).join('\n'); await Zotero.File.putContentsAsync(log, data); }
// Function to process a single URL async function processUrl(url) { let hiddenBrowser; const { HiddenBrowser } = ChromeUtils.importESModule("chrome://zotero/content/HiddenBrowser.mjs"); try { // Create a hidden browser instance hiddenBrowser = new HiddenBrowser();
// Load the URL await hiddenBrowser.load(url); let doc = await hiddenBrowser.getDocument();
// Use the Zotero.Translate.Web API with the hidden browser's document const translate = new Zotero.Translate.Web(); translate.setDocument(doc);
if (translators.length) { translate.setTranslator(translators[0]); await translate.translate(); status[url] = "success"; } else { // Fallback for pages without a specific translator await ZoteroPane.addItemFromDocument(doc); status[url] = "success"; } } catch (e) { console.error(`Failed to process ${url}:`, e); status[url] = "error"; } finally { if (hiddenBrowser) { hiddenBrowser.destroy(); } } }
// Process all URLs sequentially to avoid overwhelming the system async function processAllUrls() { for (const url of urls) { await processUrl(url); console.log(url); await saveStatus(); } console.log("Processing complete. Final status:"); console.log(status); }
That's it! Thank you so much!
I'm trying to use the javascript code as well but keep getting the same mistake as above:
var path = '/Users/bruno/Desktop/articles.txt';
var urls = Zotero.File.getContents(path).split('\n').map(url => url);
await Zotero.HTTP.processDocuments(
urls,
async function (doc) {
var translate = new Zotero.Translate.Web();
translate.setDocument(doc);
var translators = await translate.getTranslators();
if (translators.length) {
translate.setTranslator(translators[0]);
try {
await translate.translate();
return;
}
catch (e) {}
}
await ZoteroPane.addItemFromDocument(doc);
}
)
[Exception... "Component returned failure code: 0x80520001 (NS_ERROR_FILE_UNRECOGNIZED_PATH) [nsIFile.initWithPath]" nsresult: "0x80520001 (NS_ERROR_FILE_UNRECOGNIZED_PATH)" location: "JS frame :: chrome://zotero/content/xpcom/file.js :: Zotero.File</this.getContents :: line 159" data: no]
What can I do?
I have tried you javascript code for bulk upload from a file of urls.
It's working, but not as well as I had hoped : it's actually adding snapshots from the url's webpages, while what I want is the whole of the references mentioned on each url's webpages (the zotero connector of each of these page is actually offering a list of references to import) to be imported into zotero.
Example of one these pages : https://www.idref.fr/026766701.
Thank you in advance for your help !
As far as I understand and tested the script you posted works by importing everything into the main collection. Is there a way to import the items in the list into a specific collection? He has his references split in several subcollections.
Thanks.
thanks very much, that was really easy, problem solved.
Cheers
Thanks a lot for the javascript.
Could you please update the javascript to:
1. Generate a message for the url whether it was successfully added or not
2. At the end create a new file listing the urls that were not successfully added
3. Add the document in the collection that is selected in Zotero client instead of "My Library"?
var path = '/Users/pals2/ZoteroImportUrls.txt';
var log = '/Users/pals2/ZoteroImportUrls.log';
var status = {};
var urls = Zotero.File.getContents(path).split('\n').map(url => url);
urls.forEach((url, i) => status[url] = "fail");
console.log(status);
await Zotero.HTTP.loadDocuments(
urls,
async function (doc) {
console.log(doc.location.href)
var parser = new DOMParser();
var safeDoc = Zotero.HTTP.wrapDocument(
parser.parseFromString(doc.documentElement.outerHTML, 'text/html'),
doc.location.href
);
var translate = new Zotero.Translate.Web();
translate.setDocument(safeDoc);
var translators = await translate.getTranslators();
if (translators.length) {
translate.setTranslator(translators[0]);
try {
await translate.translate();
return;
}
catch (e) {}
}
try {
await ZoteroPane.addItemFromDocument(safeDoc);
status[safeDoc.location.href] = "success";
return;
}
catch (e) {}
}
)
// Convert the associative array to a string with key-value pairs separated by newlines
const data = Object.entries(status).map(([key, value]) => `${key}: ${value}`).join('\n');
// Write the data to the file
Zotero.File.writeFile(log, data, (err) => {
if (err) {
console.error('Error writing file:', err);
return;
}
console.log('File saved successfully');
});
But it gives error that there is no function Zotero.File.writeFile.
Pls help!
Please help with this version.
var path = '/Users/pals2/ZoteroImportUrls.txt';
var log = '/Users/pals2/ZoteroImportUrls.log';
var status = {};
var urls = Zotero.File.getContents(path).split('\n').map(url => url);
urls.forEach((url, i) => status[url] = "fail");
console.log(status);
async function saveStatus() {
// Convert the associative array to a string with key-value pairs separated by newlines
const data = Object.entries(status).map(([key, value]) => `${key}: ${value}`).join('\n');
// Write the data to the file
await Zotero.File.putContentsAsync(log, data);
}
await Promise.all([
Zotero.HTTP.loadDocuments(
urls,
async function (doc) {
console.log(doc.location.href)
var parser = new DOMParser();
var safeDoc = Zotero.HTTP.wrapDocument(
parser.parseFromString(doc.documentElement.outerHTML, 'text/html'),
doc.location.href
);
var translate = new Zotero.Translate.Web();
translate.setDocument(safeDoc);
var translators = await translate.getTranslators();
if (translators.length) {
translate.setTranslator(translators[0]);
try {
await translate.translate();
status[safeDoc.location.href] = "success";
return;
}
catch (e) {}
}
try {
await ZoteroPane.addItemFromDocument(safeDoc);
status[safeDoc.location.href] = "success";
return;
}
catch (e) {
}
status[doc.location.href] = "success";
}
),
saveStatus()
])
Did you ever get this to work?
I don't recall the details but I somehow replaced the mere parsing of the source HTML and traverse that DOM, with a hiddenbrowser to run all of the page javascript and traverse the resulting DOM. I had tried to post a detailed version that day and kept getting refused or kicked out from the forums.
Here's my script, feel free to use:
var path = '/path/to/bookmarks-list.txt';
var log = '/path/to/ZoteroImportUrls.log';
var status = {};
var urls = Zotero.File.getContents(path).split('\n').filter(url => url.trim() !== '');
async function saveStatus() {
const data = Object.entries(status).map(([key, value]) => `${key}: ${value}`).join('\n');
await Zotero.File.putContentsAsync(log, data);
}
// Function to process a single URL
async function processUrl(url) {
let hiddenBrowser;
const { HiddenBrowser } = ChromeUtils.importESModule("chrome://zotero/content/HiddenBrowser.mjs");
try {
// Create a hidden browser instance
hiddenBrowser = new HiddenBrowser();
// Load the URL
await hiddenBrowser.load(url);
let doc = await hiddenBrowser.getDocument();
// Use the Zotero.Translate.Web API with the hidden browser's document
const translate = new Zotero.Translate.Web();
translate.setDocument(doc);
const translators = await translate.getTranslators();
if (translators.length) {
translate.setTranslator(translators[0]);
await translate.translate();
status[url] = "success";
} else {
// Fallback for pages without a specific translator
await ZoteroPane.addItemFromDocument(doc);
status[url] = "success";
}
} catch (e) {
console.error(`Failed to process ${url}:`, e);
status[url] = "error";
} finally {
if (hiddenBrowser) {
hiddenBrowser.destroy();
}
}
}
// Process all URLs sequentially to avoid overwhelming the system
async function processAllUrls() {
for (const url of urls) {
await processUrl(url);
console.log(url);
await saveStatus();
}
console.log("Processing complete. Final status:");
console.log(status);
}
// Start the process
processAllUrls();
Hope this helps.