quickadd icon indicating copy to clipboard operation
quickadd copied to clipboard

[BUG] Quickadd calling template twice

Open Smitty010 opened this issue 6 months ago • 5 comments

Describe the bug I think #533 is not completely fixed.

One other comment before I get into this, I don't think this was happening until the latest release of quickadd a few weeks ago

I'm really sorry this is so long, but I think i need to provide enough details to either prove I'm correct or so you tell me I'm wrong.

I have a template which is used to add youtube videos to my vault. It is run by quickadd and passed the url of the video (via the clipboard), it collects the information from the video via templater user function (which calls the youtube api call), and then builds the note.

I believe the template is being called twice by quickadd.

The sequence is click commander button in ribbon --> Quickadd -> Template -> tp.user.youtubeMetadata.js and then it all backs back out.

Here's some more details:

  1. Here's the quickadd setup:

Image


  1. The beginning of the template looks like:
const dir = 'SecondBrain/';

// 1. Generate a single runId at the very start
const runId = Date.now();
const log = (msg) => console.log(`[${runId}] ${msg}`);

log(`Template execution STARTED: ${runId}`);
log(`Filename path: ${tp.file.path()}`);

// 1. Grab raw clipboard
let url = await tp.system.clipboard();
if (!url) {
      new Notice('Clipboard empty—nothing to process.', 4000);
      return;
}

console.log(`[${runId}] URL → ${url}`);

log(`Fetching metadata…`);
const metadata = await tp.user.youtubeMetadata(url, tp);
log(`Metadata fetch complete.`);
// the rest is just building the page contents and moving it to the correct directory

The templater user script looks like:

/**
 * Call with:
 *   const obj = await youtubeMetadata(url, tp);
 *
 * Returns an object with video metadata, or empty strings if not found.
 */

async function youtubeMetadata(url, tp) {
  const runId = Date.now();
  const log = (msg) => console.log(`[${runId}]: ${msg}`);
  log(`youtubeMetadata Started-------------------------`)

  // Normalize http → https
  if (url.startsWith("http://")) {
    url = url.replace("http://", "https://");
  }

  log(`YMD-1 - ${url}`)
  // Regex to extract exactly 11-char YouTube IDs from:
  //   • https://youtu.be/92UhZbJJivU?si=...
  //   • https://www.youtube.com/shorts/5M2Nx9YnNvQ
  //   • https://www.youtube.com/watch?v=0bGuse8mcLw&t=15s
  //   • https://www.youtube.com/embed/abcdefghijkl
  // Anything after the 11-char ID (e.g. &t=..., ?feature=share) is ignored.
  const videoIdRegex = /(?:youtu\.be\/|\/shorts\/|v=|embed\/)([A-Za-z0-9_-]{11})/;
  const match = url.match(videoIdRegex);
  const videoId = match?.[1] ?? "";

  log(`YMD-2 - Extracted videoId="${videoId}" from url="${url}"`);

  let filename = "";
  let title    = "";
  let authors  = [];
  let thumbUrl = "";
  let sdThumbUrl = "";

  if (videoId) {
    // Use YouTube oEmbed endpoint for JSON metadata
    const oembedUrl = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}&format=json`;
	
    try {
	  log(`YMD-3 - Fetching metadata for ${videoId}`);
      const response = await fetch(oembedUrl);
	  log(`YMD-4 - After fetching metadata/before getting json response`);
      if (response.ok) {
        const data = await response.json();
		log(`YMD-5 - After getting json response`);
        filename     = data.title || "";
        title        = tp.user.goodFilename(data.title || "");
        authors      = Array.isArray(data.author_name)
                       ? data.author_name
                       : [data.author_name].filter(Boolean);
        thumbUrl     = data.thumbnail_url || "";
        sdThumbUrl   = `https://i.ytimg.com/vi/${videoId}/sddefault.jpg`;
      } else {
        console.warn(`${runId}: oEmbed fetch failed with ${response.status}`);
      }
    } catch (err) {
      console.error(`${runId}]: Error fetching oEmbed for ${url}:`, err);
    }
  } else if (url.startsWith("https://")) {
    // URL but no recognizable video ID → leave url intact
  } else if (url.length < 125) {
    // Treat as a plain title/filename, not a URL
    filename = url;
    title    = tp.user.goodFilename(url);
    url      = "";
  } else {
    // Something bogus → clear out
    url = "";
  }

  log(`YMD-6 - youtubeMetadata ended at ${Date.now()} (duration ${Date.now() - runId}ms)`);

  return {
    title,
    authors,
    channel: "",
    published: "",
    url,
    thumbnail: thumbUrl,
    sdThumbnail: sdThumbUrl,
    keywords: "",
    quotes: "",
    keywordsList: "",
    wikiLinks: "",
    duration: "",
    durSecs: "",
    description: "",
    fullDescription: "",
    id: videoId
  };
}

module.exports = youtubeMetadata;

When I run this, I get the following logs. The numbers in the square brackets are millisconds since epic. They are not the time when the log occurred, but rather the time when the template/function was invoked (some might call it a runId). So, it's easy to see which logs occurred as part of which call. I have bolded the parts where I believe we are in the first invocation of the template. The portions not bolded are the second invocation.

VM1633: [1751147227392] Template execution STARTED: 1751147227392 VM1633:20 [1751147227392] Filename path: D:\obsidian\obsidian/SecondBrain/Videos/Untitled2.md VM1633:32 [1751147227392] URL → https://www.youtube.com/watch?v=g8JM3prvEf4&pp=0gcJCc4JAYcqIYzv VM1633:20 [1751147227392] Fetching metadata… VM1630:10 [1751147227393]: youtubeMetadata Started------------------------- VM1630:10 [1751147227393]: YMD-1 - https://www.youtube.com/watch?v=g8JM3prvEf4&pp=0gcJCc4JAYcqIYzv VM1630:10 [1751147227393]: YMD-2 - Extracted videoId="g8JM3prvEf4" from url="https://www.youtube.com/watch?v=g8JM3prvEf4&pp=0gcJCc4JAYcqIYzv" VM1630:10 [1751147227393]: YMD-3 - Fetching metadata for g8JM3prvEf4 VM1637:20 [1751147227401] Template execution STARTED: 1751147227401 VM1637:20 [1751147227401] Filename path: D:\obsidian\obsidian/SecondBrain/Videos/Untitled2.md VM1637:32 [1751147227401] URL → https://www.youtube.com/watch?v=g8JM3prvEf4&pp=0gcJCc4JAYcqIYzv VM1637:20 [1751147227401] Fetching metadata… VM1635:10 [1751147227402]: youtubeMetadata Started------------------------- VM1635:10 [1751147227402]: YMD-1 - https://www.youtube.com/watch?v=g8JM3prvEf4&pp=0gcJCc4JAYcqIYzv VM1635:10 [1751147227402]: YMD-2 - Extracted videoId="g8JM3prvEf4" from url="https://www.youtube.com/watch?v=g8JM3prvEf4&pp=0gcJCc4JAYcqIYzv" VM1635:10 [1751147227402]: YMD-3 - Fetching metadata for g8JM3prvEf4 VM1630:10 [1751147227393]: YMD-4 - After fetching metadata/before getting json response VM1635:10 [1751147227402]: YMD-4 - After fetching metadata/before getting json response VM1630:10 [1751147227393]: YMD-5 - After getting json response VM1630:10 [1751147227393]: YMD-6 - youtubeMetadata ended at 1751147227651 (duration 258ms) VM1633:20 [1751147227392] Metadata fetch complete. VM1633:20 [1751147227392] Fetched & applied metadata: Perplexity's new AI agents are INSANE (g8JM3prvEf4) VM1633:20 [1751147227392] Sanitized filename: Perplexity's new AI agents are INSANE VM1633:20 [1751147227392] Target path: SecondBrain/Videos/Perplexity's new AI agents are INSANE.md VM1635:10 [1751147227402]: YMD-5 - After getting json response VM1635:10 [1751147227402]: YMD-6 - youtubeMetadata ended at 1751147227652 (duration 250ms) VM1637:20 [1751147227401] Metadata fetch complete. VM1637:20 [1751147227401] Fetched & applied metadata: Perplexity's new AI agents are INSANE (g8JM3prvEf4) VM1637:20 [1751147227401] Sanitized filename: Perplexity's new AI agents are INSANE VM1637:20 [1751147227401] Target path: SecondBrain/Videos/Perplexity's new AI agents are INSANE.md VM1633:20 [1751147227392] Moving note to final path SecondBrain/Videos/Perplexity's new AI agents are INSANE.md… VM1637:20 [1751147227401] Moving note to final path SecondBrain/Videos/Perplexity's new AI agents are INSANE.md… VM1637:20 [1751147227401] File move complete VM1637:20 [1751147227401] Template execution COMPLETED VM1633:20 [1751147227392] File move complete VM1633:20 [1751147227392] Template execution COMPLETED

Again, the number in the square brackets is the milliseconds for when the template or the user function started. You'll notice that there are 4 of them. There should only be two.

Other things of note

  1. the file that was created by quickadd path is displayed in the logs. In both cases it is "untitled2". This doesn't makes sense if there were really two activations of quickadd as it should have created "untitled3" for the second call
  2. The logs show that the two invocations are running simultaneously.
  3. I can see that whenever a call is made to a routine requiring an 'await', the code switches between template invocations
  4. Sometimes when i run it, one invocation will realize that the new file already exists (and abort) and sometimes it doesn't. Feels like a timing thing, but not the fundamental problem.

To Reproduce Steps to reproduce the behavior:

  1. Click on the commander button when there is a youtube video url in the clipboard.
  2. watch the logs in the developer console.

Expected behavior The template and user function should run only once.

Screenshots If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS] Windows 11 (I always install updates)
  • Browser [e.g. chrome, safari] (this is obsidian1.9.4; I use chrome, but I don't see how that's relevant,
  • Version [e.g. 22] Quickadd 1.17.0

Smartphone (please complete the following information): Not relevant

Additional context Add any other context about the problem here.

Smitty010 avatar Jun 28 '25 22:06 Smitty010

I was doing some work last night where I use a template that I only use about once a week. The day I do I'll use it a dozen times. Totally different template, totally different templater user functions. I realized that it was also executing twice. This makes me think that the release that broke the code was the latest one (June 24?).

Smitty010 avatar Jun 29 '25 17:06 Smitty010

I've been having the same error, on the latest QuickAdd. A template I've been using for a year+ suddenly has been giving me duplicate prompts.

I downgraded to 1.16.0 and the problem was resolved immediately.

EDIT: The issue resumed after a few hours, strangely enough.

vervitee avatar Jul 06 '25 11:07 vervitee

How do I downgrade? I 'm unable to locate the 1.16.0 release.

Smitty010 avatar Jul 06 '25 23:07 Smitty010

This may be helpful or it may not. Out of curiousity, I asked Gemini what it thought the problem was. Here's its analysis (I'm well aware of AI's ability to hallucinate so I'll let you judge if it got it right; I did try having Gemini rebuild the project, but this resulted in errors and, in an attempt to get it to compile, Gemini got into an endless loop of changes and changes to changes and... so I'm wondering if it got the original reasoning correct).

"The issue lies in the TemplateEngine.ts file. The methods responsible for creating, overwriting, and appending to files (createFileWithTemplate, overwriteFileWithTemplate, and appendToFileWithTemplate) all contain a call to replaceTemplaterTemplatesInCreatedFile.

This function is designed to integrate with the popular "Templater" plugin. However, it unconditionally triggers a second processing of the template by the Templater plugin, causing the template to be executed twice.

The Fix

The solution is to remove the calls to replaceTemplaterTemplatesInCreatedFile from src/engine/TemplateEngine.ts. This will prevent the second execution of the template while still allowing QuickAdd's own template processing to function as expected."

Smitty010 avatar Jul 17 '25 23:07 Smitty010

I did not face any issues with the Template Choice but I did face the twice execution bug with a Capture Choice. Not sure if this helps but maybe you can try this workaround that worked for me, #919, to avoid the bug with a capture choice.

rx-chris avatar Sep 13 '25 06:09 rx-chris

@Smitty010 thanks a ton for the logs — the interleaved runIds make it clear this isn’t “user clicked twice”. And the follow-up that you saw it with a completely different template/user function is a great datapoint.

QuickAdd v2.9.2 (released 2025-12-14) includes a rework of the Templater integration to avoid double execution (including suppressing Templater’s trigger-on-create when QuickAdd is already applying templates, and preventing overlapping whole-file renders). This should stop the “template runs twice concurrently / duplicate prompts” behavior you’re seeing.

Could you (and @vervitee / @rx-chris) please update to 2.9.2 and paste an updated console log?

  • Obsidian → Settings → Community plugins → Installed plugins → Check for updates → update QuickAdd → restart Obsidian
  • Run the same Template choice with the YouTube URL in clipboard

@vervitee: you mentioned downgrading to 1.16.0 fixed it, but that it “resumed after a few hours” — if you can reproduce that on 2.9.2, I’d love details on what changed right before it resumed (Obsidian reload, plugin toggles, vault switch, etc.).

Re the Gemini analysis: the high-level conclusion (“QuickAdd causes a second Templater pass”) aligns with what I found; 2.9.2 removes the unconditional re-processing that could lead to double runs.

If you don’t see 2.9.2 yet, it may take a bit to propagate; try “Check for updates” again and restart Obsidian.

If it still happens on 2.9.2, please include:

  • your QuickAdd + Templater versions
  • whether Templater has “Trigger on new file creation” enabled
  • whether this also reproduces on an existing file (not just new-file creation)

chhoumann avatar Dec 14 '25 17:12 chhoumann

Hi. Unfortunately, Scott passed away in November. I wanted to let you know so that you weren’t anticipating any assistance from him.

On Sun, Dec 14, 2025 at 10:38 AM Christian Bager Bach Houmann < @.***> wrote:

chhoumann left a comment (chhoumann/quickadd#844) https://github.com/chhoumann/quickadd/issues/844#issuecomment-3651740584

@Smitty010 https://github.com/Smitty010 thanks a ton for the logs — the interleaved runIds make it clear this isn’t “user clicked twice”. And the follow-up that you saw it with a completely different template/user function is a great datapoint.

QuickAdd v2.9.2 (released 2025-12-14) includes a rework of the Templater integration to avoid double execution (including suppressing Templater’s trigger-on-create when QuickAdd is already applying templates, and preventing overlapping whole-file renders). This should stop the “template runs twice concurrently / duplicate prompts” behavior you’re seeing.

Could you (and @vervitee https://github.com/vervitee / @rx-chris https://github.com/rx-chris) please update to 2.9.2 and paste an updated console log?

  • Obsidian → Settings → Community plugins → Installed plugins → Check for updates → update QuickAdd → restart Obsidian
  • Run the same Template choice with the YouTube URL in clipboard

@vervitee https://github.com/vervitee: you mentioned downgrading to 1.16.0 fixed it, but that it “resumed after a few hours” — if you can reproduce that on 2.9.2, I’d love details on what changed right before it resumed (Obsidian reload, plugin toggles, vault switch, etc.).

Re the Gemini analysis: the high-level conclusion (“QuickAdd causes a second Templater pass”) aligns with what I found; 2.9.2 removes the unconditional re-processing that could lead to double runs.

If you don’t see 2.9.2 yet, it may take a bit to propagate; try “Check for updates” again and restart Obsidian.

If it still happens on 2.9.2, please include:

  • your QuickAdd + Templater versions
  • whether Templater has “Trigger on new file creation” enabled
  • whether this also reproduces on an existing file (not just new-file creation)

— Reply to this email directly, view it on GitHub https://github.com/chhoumann/quickadd/issues/844#issuecomment-3651740584, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA4V7SYFKZXUIQY7CTSROLD4BWN7TAVCNFSM6AAAAACALMYVVOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTMNJRG42DANJYGQ . You are receiving this because you were mentioned.Message ID: @.***>

Smitty010 avatar Dec 14 '25 21:12 Smitty010

Thank you for letting me know. I'm very sorry for your loss.

chhoumann avatar Dec 15 '25 16:12 chhoumann