[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: basilioss\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: # Replace with a single Buy Me a Coffee username\nthanks_dev: # Replace with a single thanks.dev username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Vasyl Tyshchuk\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Obsidian scrapers\n\nA collection of [Templater](https://github.com/SilentVoid13/Templater) scripts for [Obsidian](https://obsidian.md/) that can be easily integrated into your templates to get information from different sites with a copied link.\n\nhttps://user-images.githubusercontent.com/71596800/193448137-3a4d4489-cbc6-4108-905c-9eb3165e6ee1.mp4\n\n## Installation\n\n1. [Download](https://github.com/basilioss/obsidian-scrapers/archive/refs/heads/main.zip) and unzip the files/folders.\n2. Copy `scripts` and `templates` folder to your vault (notes) folder.\n3. Install Templater via the Community Plugins tab within Obsidian. Open Templater options in settings under `Community plugins` section.\n4. Set `Template folder location` to downloaded `templates` folder.\n5. Set `Script files folder location` to downloaded `scripts` folder.\n6. Optionally, add a new hotkey for the `scraper` template that can automatically insert the correct template depending on the link domain.\n7. Copy the URL and open insert template modal (by default `Alt + E`). Choose `scraper` or youtube/goodreads/imdb etc., depending on the link.\n8. Customize the downloaded templates to your liking. Refer to [Templater documentation](https://silentvoid13.github.io/Templater/) for more info.\n\n## Available functions\n\n### Any website\n\n| Function                                   | Description            |\n| ------------------------------------------ | ---------------------- |\n| `<% tp.user.website('title', tp) %>`       | Get title              |\n| `<% tp.user.website('description', tp) %>` | Get description        |\n| `<% tp.user.website('url', tp) %>`         | Get url                |\n| `<% tp.user.website('image', tp) %>`       | Get image preview link |\n\n### [YouTube](https://www.youtube.com/)\n\n| Function                                       | Description                                                                           |\n| ---------------------------------------------- | ------------------------------------------------------------------------------------- |\n| `<% tp.user.youtube('title', tp) %>`           | Get title                                                                             |\n| `<% tp.user.youtube('channel', tp) %>`         | Get channel name                                                                      |\n| `<% tp.user.youtube('published', tp) %>`       | Get publish date                                                                      |\n| `<% tp.user.youtube('url', tp) %>`             | Get url                                                                               |\n| `<% tp.user.youtube('thumbnail', tp) %>`       | Get thumbnail link                                                                    |\n| `<% tp.user.youtube('keywords', tp) %>`        | Get keywords (alternative formats: `keywordsList`, `keywordsQuotes`, `keywordsLinks`) |\n| `<% tp.user.youtube('duration', tp) %>`        | Get duration                                                                          |\n| `<% tp.user.youtube('description', tp) %>`     | Get short description                                                                 |\n| `<% tp.user.youtube('descriptionFull', tp) %>` | Get full description                                                                  |\n| `<% tp.user.youtube('id', tp) %>`              | Get ID (can be used in embeds)                                                        |\n\n### [Goodreads](https://www.goodreads.com/)\n\n| Function                                     | Description                                                                       |\n| -------------------------------------------- | --------------------------------------------------------------------------------- |\n| `<% tp.user.goodreads('url', tp) %>`         | Get url                                                                           |\n| `<% tp.user.goodreads('title', tp) %>`       | Get title                                                                         |\n| `<% tp.user.goodreads('authors', tp) %>`     | Get authors (alternative formats: `authorsList`, `authorsQuotes`, `authorsLinks`) |\n| `<% tp.user.goodreads('isbn', tp) %>`        | Get ISBN                                                                          |\n| `<% tp.user.goodreads('published', tp) %>`   | Get publish date                                                                  |\n| `<% tp.user.goodreads('genres', tp) %>`      | Get genres (alternative formats: `genresList`, `genresQuotes`, `genresLinks`)     |\n| `<% tp.user.goodreads('cover', tp) %>`       | Get cover link                                                                    |\n| `<% tp.user.goodreads('pageCount', tp) %>`   | Get number of pages                                                               |\n| `<% tp.user.goodreads('description', tp) %>` | Get description                                                                   |\n| `<% tp.user.goodreads('rating', tp) %>`      | Get rating                                                                        |\n\n### [IMDb](https://www.imdb.com/)\n\n| Function                                  | Description                                                                               |\n| ----------------------------------------- | ----------------------------------------------------------------------------------------- |\n| `<% tp.user.imdb('title', tp) %>`         | Get title                                                                                 |\n| `<% tp.user.imdb('image', tp) %>`         | Get poster link                                                                           |\n| `<% tp.user.imdb('published', tp) %>`     | Get publish date                                                                          |\n| `<% tp.user.imdb('keywords', tp) %>`      | Get keywords (alternative formats: `keywordsList`, `keywordsQuotes`, `keywordsLinks`)     |\n| `<% tp.user.imdb('directors', tp) %>`     | Get directors (alternative formats: `directorsList`, `directorsQuotes`, `directorsLinks`) |\n| `<% tp.user.imdb('creators', tp) %>`      | Get creators (alternative formats: `creatorsList`, `creatorsQuotes`, `creatorsLinks`)     |\n| `<% tp.user.imdb('duration', tp) %>`      | Get duration                                                                              |\n| `<% tp.user.imdb('description', tp) %>`   | Get description                                                                           |\n| `<% tp.user.imdb('type', tp) %>`          | Get type (movie/series)                                                                   |\n| `<% tp.user.imdb('contentRating', tp) %>` | Get content rating                                                                        |\n| `<% tp.user.imdb('genres', tp) %>`        | Get genres (alternative formats: `genresList`, `genresQuotes`, `genresLinks`)             |\n| `<% tp.user.imdb('stars', tp) %>`         | Get cast (alternative formats: `starsList`, `starsQuotes`, `starsLinks`)                  |\n| `<% tp.user.imdb('imdbRating', tp) %>`    | Get IMDb rating                                                                           |\n| `<% tp.user.imdb('countries', tp) %>`     | Get countries (alternative formats: `countriesList`, `countriesQuotes`, `countriesLinks`) |\n| `<% tp.user.imdb('url', tp) %>`           | Get url                                                                                   |\n\n### [Letterboxd](https://letterboxd.com/)\n\n| Function                                       | Description                                                                                    |\n| ---------------------------------------------- | ---------------------------------------------------------------------------------------------- |\n| `<% tp.user.letterboxd('image', tp) %>`        | Get image link                                                                                 |\n| `<% tp.user.letterboxd('directors', tp) %>`    | Get directors (alternative formats: `directorsList`, `directorsQuotes`, `directorsLinks`)      |\n| `<% tp.user.letterboxd('studios', tp) %>`      | Get studios (alternative formats: `studiosList`, `studiosQuotes`, `studiosLinks`)              |\n| `<% tp.user.letterboxd('published', tp) %>`    | Get publish date                                                                               |\n| `<% tp.user.letterboxd('url', tp) %>`          | Get url                                                                                        |\n| `<% tp.user.letterboxd('cast', tp) %>`         | Get cast (alternative formats: `castList`, `castQuotes`, `castLinks`)                          |\n| `<% tp.user.letterboxd('castShort', tp) %>`    | Get cast shortlist (alternative formats: `castShortList`, `castShortQuotes`, `castShortLinks`) |\n| `<% tp.user.letterboxd('title', tp) %>`        | Get title                                                                                      |\n| `<% tp.user.letterboxd('genres', tp) %>`       | Get genres (alternative formats: `genresList`, `genresQuotes`, `genresLinks`)                  |\n| `<% tp.user.letterboxd('countries', tp) %>`    | Get countries (alternative formats: `countriesList`, `countriesQuotes`, `countriesLinks`)      |\n| `<% tp.user.letterboxd('rating', tp) %>`       | Get rating                                                                                     |\n| `<% tp.user.letterboxd('description', tp) %>`  | Get description                                                                                |\n| `<% tp.user.letterboxd('imdbUrl', tp) %>`      | Get IMDb link                                                                                  |\n| `<% tp.user.letterboxd('tmdbUrl', tp) %>`      | Get TMDB link                                                                                  |\n| `<% tp.user.letterboxd('languages', tp) %>`    | Get languages (alternative formats: `languagesList`, `languagesQuotes`, `languagesLinks`)      |\n| `<% tp.user.letterboxd('writers', tp) %>`      | Get writers (alternative formats: `writersList`, `writersQuotes`, `writersLinks`)              |\n| `<% tp.user.letterboxd('runtime', tp) %>`      | Get duration                                                                                   |\n| `<% tp.user.letterboxd('altTitle', tp) %>`     | Get alternative title                                                                          |\n| `<% tp.user.letterboxd('altTitleUTF8', tp) %>` | Get alternative title if it includes UTF8 only (e.g. no Chinese characters)                    |\n\n### [Wikipedia](https://www.wikipedia.org/)\n\n| Function                                  | Description           |\n| ----------------------------------------- | --------------------- |\n| `<% tp.user.wikipedia('title', tp) %>`    | Get title             |\n| `<% tp.user.wikipedia('url', tp) %>`      | Get url               |\n| `<% tp.user.wikipedia('image', tp) %>`    | Get image link        |\n| `<% tp.user.wikipedia('headline', tp) %>` | Get short description |\n\n### [Odysee](https://odysee.com/)\n\n| Function                                  | Description                                                                           |\n| ----------------------------------------- | ------------------------------------------------------------------------------------- |\n| `<% tp.user.odysee('title', tp) %>`       | Get title                                                                             |\n| `<% tp.user.odysee('channel', tp) %>`     | Get channel name                                                                      |\n| `<% tp.user.odysee('description', tp) %>` | Get description                                                                       |\n| `<% tp.user.odysee('published', tp) %>`   | Get publish date                                                                      |\n| `<% tp.user.odysee('thumbnail', tp) %>`   | Get thumbnail link                                                                    |\n| `<% tp.user.odysee('duration', tp) %>`    | Get duration                                                                          |\n| `<% tp.user.odysee('url', tp) %>`         | Get Odysee url                                                                        |\n| `<% tp.user.odysee('contentUrl', tp) %>`  | Get direct link to a video                                                            |\n| `<% tp.user.odysee('embedUrl', tp) %>`    | Get embed url                                                                         |\n| `<% tp.user.odysee('keywords', tp) %>`    | Get keywords (alternative formats: `keywordsList`, `keywordsQuotes`, `keywordsLinks`) |\n"
  },
  {
    "path": "scripts/goodreads.js",
    "content": "// source: https://github.com/basilioss/obsidian-scrapers\n\nasync function goodreads(value, tp, doc) {\n  let url = await tp.system.clipboard();\n\n  if (!isValidHttpUrl(url)) {\n    console.error(\"Invalid URL for \" + value);\n    return \"\";\n  }\n\n  if (!doc) {\n    let page = await tp.obsidian.request({ url });\n    let p = new DOMParser();\n    doc = p.parseFromString(page, \"text/html\");\n  }\n\n  const data = extractData(doc);\n  const $ = (selector) => doc.querySelector(selector);\n\n  switch (value) {\n    case \"url\":\n      return safeReturn(getUrl(doc), \"url\");\n    case \"title\":\n      return safeReturn(getTitle(doc), \"title\");\n    case \"authors\":\n      return safeReturn(getAuthors(data), \"authors\");\n    case \"authorsQ\":\n    case \"authorsQuotes\":\n      return formatQuote(getAuthors(data), \"authors\");\n    case \"authorsL\":\n    case \"authorsList\":\n      return formatList(getAuthors(data), \"authors\");\n    case \"authorsW\":\n    case \"authorsLinks\":\n      return formatLink(getAuthors(data), \"authors\");\n    case \"isbn\":\n      return safeReturn(data?.isbn, \"isbn\");\n    case \"published\":\n      return safeReturn(getPublished(doc), \"published\");\n    case \"genres\":\n      return safeReturn(getGenres(doc), \"genres\");\n    case \"genresQ\":\n    case \"genresQuotes\":\n      return formatQuote(getGenres(doc), \"genres\");\n    case \"genresL\":\n    case \"genresList\":\n      return formatList(getGenres(doc), \"genres\");\n    case \"genresW\":\n    case \"genresLinks\":\n      return formatLink(getGenres(doc), \"genres\");\n    case \"cover\":\n      return safeReturn(getCover(data), \"cover\");\n    case \"pageCount\":\n      return safeReturn(data?.numberOfPages, \"pageCount\");\n    case \"description\":\n      return safeReturn(getDescription(doc), \"description\");\n    case \"rating\":\n      return safeReturn(data?.aggregateRating?.ratingValue, \"rating\");\n    default:\n      new Notice(\"Incorrect parameter: \" + value, 5000);\n      return \"\";\n  }\n}\n\n// --- Data extractors ---\n\nfunction extractData(doc) {\n  const scriptTag = doc.querySelector('script[type=\"application/ld+json\"]');\n  return scriptTag ? JSON.parse(scriptTag.textContent) : {};\n}\n\nfunction getUrl(doc) {\n  return doc.querySelector(\"link[rel='canonical']\")?.href || \"\";\n}\n\nfunction getTitle(doc) {\n  const title = doc.querySelector(\".BookPageTitleSection .Text__title1\")?.innerText || \"\";\n  return title.trim().replace(/&amp;/g, \"&\").replace(/&apos;/g, \"'\");\n}\n\nfunction getAuthors(data) {\n  const authors = data.author;\n  return !authors || authors.length === 0\n    ? \"\"\n    : Array.from(authors, (a) => a.name.trim().replace(/ +(?= )/g, \"\")).join(\", \");\n}\n\nfunction getPublished(doc) {\n  const pubInfo = doc.querySelector('p[data-testid=\"publicationInfo\"]');\n  if (!pubInfo) return \"\";\n  const match = pubInfo.innerHTML.match(/First published\\s(.*)/);\n  return match ? match[1].trim() : \"\";\n}\n\nfunction getGenres(doc) {\n  const genreElements = doc.querySelectorAll('.BookPageMetadataSection__genreButton .Button__labelItem');\n  if (!genreElements || genreElements.length === 0) return \"\";\n  const genres = Array.from(genreElements, (el) => el.textContent.trim());\n  return [...new Set(genres)].join(\", \");\n}\n\nfunction getCover(data) {\n  return (data?.image || \"\").replace(/\\?.*$/g, \"\");\n}\n\nfunction getDescription(doc) {\n  const desc = doc.querySelector('.BookPageMetadataSection__description .Formatted');\n  return desc ? desc.textContent.trim() : \"\";\n}\n\n// --- Helpers ---\n\nfunction safeReturn(result, name) {\n  if (!result) logParsingError(name);\n  return result || \"\";\n}\n\nfunction formatQuote(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `\"${value.replace(/, /g, '\", \"')}\"` : \"\";\n}\n\nfunction formatList(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `\\n- ${value.replace(/, /g, \"\\n- \")}` : \"\";\n}\n\nfunction formatLink(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `[[${value.replace(/, /g, \"]], [[\")}]]` : \"\";\n}\n\nfunction isValidHttpUrl(string) {\n  try {\n    const url = new URL(string);\n    return url.protocol === \"http:\" || url.protocol === \"https:\";\n  } catch (_) {\n    return false;\n  }\n}\n\nfunction logParsingError(variable) {\n  console.error(`Parsing Error: Couldn't get ${variable}.`);\n}\n\nmodule.exports = goodreads;\n"
  },
  {
    "path": "scripts/imdb.js",
    "content": "// source: https://github.com/basilioss/obsidian-scrapers\n\nasync function imdb(value, tp, doc) {\n  let url = await tp.system.clipboard();\n\n  if (!isValidHttpUrl(url)) {\n    console.error(\"Invalid URL for \" + value);\n    return \"\";\n  }\n\n  if (doc === undefined) {\n    let page = await tp.obsidian.request({ url });\n    let p = new DOMParser();\n    doc = p.parseFromString(page, \"text/html\");\n  }\n\n  const $ = (selector) => doc.querySelector(selector);\n\n  let json;\n  try {\n    let script = $(\"script[type='application/ld+json']\")?.innerText ?? \"\";\n    json = JSON.parse(script);\n  } catch (err) {\n    console.warn(\"Warning: Failed to parse JSON-LD metadata. Proceeding without JSON data.\");\n    json = null; // allow fallback functions to run\n  }\n\n  switch (value) {\n    case \"title\":\n      return safeReturn(getTitle(json), \"title\");\n    case \"image\":\n      return safeReturn(json?.image, \"image\");\n    case \"published\":\n      return safeReturn(getPublished(json, doc), \"published\");\n    case \"keywords\":\n      return safeReturn(getKeywords(json), \"keywords\");\n    case \"keywordsQ\":\n    case \"keywordsQuotes\":\n      return formatQuote(getKeywords(json), \"keywords\");\n    case \"keywordsL\":\n    case \"keywordsList\":\n      return formatList(getKeywords(json), \"keywords\");\n    case \"keywordsW\":\n    case \"keywordsLinks\":\n      return formatLink(getKeywords(json), \"keywords\");\n    case \"directors\":\n      return safeReturn(getDirectors(json), \"directors\");\n    case \"directorsQ\":\n    case \"directorsQuotes\":\n      return formatQuote(getDirectors(json), \"directors\");\n    case \"directorsL\":\n    case \"directorsList\":\n      return formatList(getDirectors(json), \"directors\");\n    case \"directorsW\":\n    case \"directorsLinks\":\n      return formatLink(getDirectors(json), \"directors\");\n    case \"creators\":\n      return safeReturn(getCreators(json), \"creators\");\n    case \"creatorsQ\":\n    case \"creatorsQuotes\":\n      return formatQuote(getCreators(json), \"creators\");\n    case \"creatorsL\":\n    case \"creatorsList\":\n      return formatList(getCreators(json), \"creators\");\n    case \"creatorsW\":\n    case \"creatorsLinks\":\n      return formatLink(getCreators(json), \"creators\");\n    case \"duration\":\n      return safeReturn(getDuration(json), \"duration\");\n    case \"description\":\n      return safeReturn(getDescription(doc), \"description\");\n    case \"type\":\n      return safeReturn(getType(json), \"type\");\n    case \"contentRating\":\n      return safeReturn(json?.contentRating, \"contentRating\");\n    case \"genres\":\n      return safeReturn(getGenres(json), \"genres\");\n    case \"genresQ\":\n    case \"genresQuotes\":\n      return formatQuote(getGenres(json), \"genres\");\n    case \"genresL\":\n    case \"genresList\":\n      return formatList(getGenres(json), \"genres\");\n    case \"genresW\":\n    case \"genresLinks\":\n      return formatLink(getGenres(json), \"genres\");\n    case \"stars\":\n      return safeReturn(getStars(json), \"stars\");\n    case \"starsQ\":\n    case \"starsQuotes\":\n      return formatQuote(getStars(json), \"stars\");\n    case \"starsL\":\n    case \"starsList\":\n      return formatList(getStars(json), \"stars\");\n    case \"starsW\":\n    case \"starsLinks\":\n      return formatLink(getStars(json), \"stars\");\n    case \"imdbRating\":\n      return safeReturn(json?.aggregateRating?.ratingValue, \"imdbRating\")\n    case \"countries\":\n      return safeReturn(getCountries(doc), \"countries\");\n    case \"countriesQ\":\n    case \"countriesQuotes\":\n      return formatQuote(getCountries(doc), \"countries\");\n    case \"countriesL\":\n    case \"countriesList\":\n      return formatList(getCountries(doc), \"countries\");\n    case \"countriesW\":\n    case \"countriesLinks\":\n      return formatLink(getCountries(doc), \"countries\");\n    case \"url\":\n      return safeReturn(getUrl(json), \"url\");\n    default:\n      new Notice(\"Incorrect parameter: \" + value, 5000);\n      return \"\";\n  }\n}\n\n// --- Data extractors ---\n\nfunction getTitle(json) {\n  let title = json?.alternateName ?? json?.name ?? \"\";\n  return title.replace(/&apos;/g, \"'\");\n}\n\nfunction getPublished(json, doc) {\n  if (json?.datePublished) {\n    return json.datePublished.substring(0, 4);\n  }\n  return doc.querySelector(\"a[href*='releaseinfo']\")?.innerText || \"\";\n}\n\nfunction getKeywords(json) {\n  if (!json?.keywords) return \"\";\n  return json.keywords.toLowerCase().replace(/,/g, \", \");\n}\n\nfunction getDirectors(json) {\n  if (!json?.director) return \"\";\n  return json.director.map((d) => d.name).filter(Boolean).join(\", \");\n}\n\nfunction getCreators(json) {\n  if (!json?.creator) return \"\";\n  return json.creator.map((c) => c.name).filter(Boolean).join(\", \");\n}\n\nfunction getDuration(json) {\n  let duration = \"\";\n  if (json?.duration != null) {\n    duration = JSON.stringify(json.duration).toLowerCase();\n    duration = duration\n      .replace(/\"pt/, \"\")\n      .replace(/h/, \"h \")\n      .replace(/m\"/, \"m\");\n  }\n  return duration;\n}\n\nfunction getType(json) {\n  return json?.[\"@type\"]?.toLowerCase().replace(/tv/i, \"\") || \"\";\n}\n\nfunction getGenres(json) {\n  if (!json?.genre) return \"\";\n  return JSON.stringify(json.genre)\n    .toLowerCase()\n    .replace(/\",\"/g, \", \")\n    .replace(/\\[\"/g, \"\")\n    .replace(/\\\"]/, \"\");\n}\n\nfunction getStars(json) {\n  if (!json?.actor) return \"\";\n  return json.actor.map((a) => a.name).join(\", \");\n}\n\nfunction getCountries(doc) {\n  let countries = doc.querySelectorAll(\"a[href*='country_of_origin']\");\n  return Array.from(countries, (countries) => countries.textContent).join(\", \");\n}\n\nfunction getDescription(doc) {\n  return doc.querySelector(\"span[data-testid='plot-xl']\")?.innerText || \"\";\n}\n\nfunction getUrl(json) {\n  if (!json?.url) return \"\";\n  return json.url.startsWith(\"http\") ? json.url : \"https://www.imdb.com\" + json.url;\n}\n\n// --- Helpers ---\n\nfunction safeReturn(result, name) {\n  if (!result) logParsingError(name);\n  return result || \"\";\n}\n\nfunction formatQuote(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `\"${value.replace(/, /g, '\", \"')}\"` : \"\";\n}\n\nfunction formatList(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `\\n- ${value.replace(/, /g, \"\\n- \")}` : \"\";\n}\n\nfunction formatLink(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `[[${value.replace(/, /g, \"]], [[\")}]]` : \"\";\n}\n\nfunction logParsingError(variable) {\n  console.error(`Parsing Error: Couldn't get ${variable}.`);\n}\n\nfunction isValidHttpUrl(string) {\n  try {\n    let url = new URL(string);\n    return url.protocol === \"http:\" || url.protocol === \"https:\";\n  } catch (_) {\n    return false;\n  }\n}\n\nmodule.exports = imdb;\n"
  },
  {
    "path": "scripts/letterboxd.js",
    "content": "// source: https://github.com/basilioss/obsidian-scrapers\n\nasync function letterboxd(value, tp, doc) {\n  let url = await tp.system.clipboard();\n\n  if (!isValidHttpUrl(url)) {\n    console.error(\"Invalid URL for \" + value);\n    return \"\";\n  }\n\n  if (doc === undefined) {\n    let page = await tp.obsidian.request({ url });\n    let p = new DOMParser();\n    doc = p.parseFromString(page, \"text/html\");\n  }\n\n  const $ = (selector) => doc.querySelector(selector);\n\n  let json;\n  try {\n    let script = $(\"script[type='application/ld+json']\")?.innerText ?? \"\";\n      // Remove multi line comments inside the script\n    script = script.replace(/\\/\\*([\\s\\S]*?)\\*\\//g, \"\");\n    // Remove new lines\n    script = script.replace(/(\\r\\n|\\n|\\r)/gm, \"\");\n    json = JSON.parse(script);\n  } catch (err) {\n    console.warn(\"Warning: Failed to parse JSON-LD metadata. Proceeding without JSON data.\");\n    json = null; // allow fallback functions to run\n  }\n\n  switch (value) {\n    case \"image\":\n      return safeReturn(getImage(json), \"image\");\n    case \"directors\":\n      return safeReturn(getDirectors(json), \"directors\");\n    case \"directorsQ\":\n    case \"directorsQuotes\":\n      return formatQuote(getDirectors(json), \"directors\");\n    case \"directorsL\":\n    case \"directorsList\":\n      return formatList(getDirectors(json), \"directors\");\n    case \"directorsW\":\n    case \"directorsLinks\":\n      return formatLink(getDirectors(json), \"directors\");\n    case \"studios\":\n      return safeReturn(getStudios(json), \"studios\");\n    case \"studiosQ\":\n    case \"studiosQuotes\":\n      return formatQuote(getStudios(json), \"studios\");\n    case \"studiosL\":\n    case \"studiosList\":\n      return formatList(getStudios(json), \"studios\");\n    case \"studiosW\":\n    case \"studiosLinks\":\n      return formatLink(getStudios(json), \"studios\");\n    case \"published\":\n      return safeReturn(json?.releasedEvent?.[0]?.startDate, \"published\");\n    case \"url\":\n      return safeReturn(json?.url, \"url\");\n    case \"cast\":\n      return safeReturn(getCast(json), \"cast\");\n    case \"castQ\":\n    case \"castQuotes\":\n      return formatQuote(getCast(json), \"cast\");\n    case \"castL\":\n    case \"castList\":\n      return formatList(getCast(json), \"cast\");\n    case \"castW\":\n    case \"castLinks\":\n      return formatLink(getCast(json), \"cast\");\n    case \"castShort\":\n      return safeReturn(getCastShort(json), \"castShort\");\n    case \"castShortQ\":\n    case \"castShortQuotes\":\n      return formatQuote(getCastShort(json), \"castShort\");\n    case \"castShortL\":\n    case \"castShortList\":\n      return formatList(getCastShort(json), \"castShort\");\n    case \"castShortW\":\n    case \"castShortLinks\":\n      return formatLink(getCastShort(json), \"castShort\");\n    case \"title\":\n      return safeReturn(json?.name?.replace(/\"/g, \"”\"), \"title\");\n    case \"genres\":\n      return safeReturn(getGenres(json), \"genres\");\n    case \"genresQ\":\n    case \"genresQuotes\":\n      return formatQuote(getGenres(json), \"genres\");\n    case \"genresL\":\n    case \"genresList\":\n      return formatList(getGenres(json), \"genres\");\n    case \"genresW\":\n    case \"genresLinks\":\n      return formatLink(getGenres(json), \"genres\");\n    case \"countries\":\n      return safeReturn(getCountries(json), \"countries\");\n    case \"countriesQ\":\n    case \"countriesQuotes\":\n      return formatQuote(getCountries(json), \"countries\");\n    case \"countriesL\":\n    case \"countriesList\":\n      return formatList(getCountries(json), \"countries\");\n    case \"countriesW\":\n    case \"countriesLinks\":\n      return formatLink(getCountries(json), \"countries\");\n    case \"rating\":\n      return safeReturn(json?.aggregateRating?.ratingValue, \"rating\");\n    case \"description\":\n      return safeReturn($(\"meta[name='description']\")?.content, \"description\");\n    case \"imdbUrl\":\n      return safeReturn(getImdbUrl(doc), \"imdbUrl\");\n    case \"tmdbUrl\":\n      return safeReturn($(\"a[data-track-action='TMDB']\")?.href, \"tmdbUrl\");\n    case \"languages\":\n      return safeReturn(getLanguages(doc), \"languages\");\n    case \"languagesQ\":\n    case \"languagesQuotes\":\n      return formatQuote(getLanguages(doc), \"languages\");\n    case \"languagesL\":\n    case \"languagesList\":\n      return formatList(getLanguages(doc), \"languages\");\n    case \"languagesW\":\n    case \"languagesLinks\":\n      return formatLink(getLanguages(doc), \"languages\");\n    case \"writers\":\n      return safeReturn(getWriters(doc), \"writers\");\n    case \"writersQ\":\n    case \"writersQuotes\":\n      return formatQuote(getWriters(doc), \"writers\");\n    case \"writersL\":\n    case \"writersList\":\n      return formatList(getWriters(doc), \"writers\");\n    case \"writersW\":\n    case \"writersLinks\":\n      return formatLink(getWriters(doc), \"writers\");\n    case \"runtime\":\n      return safeReturn(getRuntime(doc), \"runtime\");\n    case \"altTitle\":\n      return safeReturn(getAltTitle(doc), \"altTitle\");\n    case \"altTitleUTF8\":\n      return safeReturn(getAltTitleUTF8(doc), \"altTitle\");\n    default:\n      new Notice(\"Incorrect parameter: \" + value, 5000);\n      return \"\";\n  }\n}\n\n// --- Data Extractors ---\n\nfunction getImage(json) {\n  return (json?.image || \"\").replace(/\\?.*$/, \"\");\n}\n\nfunction getDirectors(json) {\n  if (json?.director) {\n    return json.director.map((d) => d.name).join(\", \");\n  }\n  return \"\";\n\n}\n\nfunction getStudios(json) {\n  if (json?.productionCompany) {\n    return json.productionCompany.map((s) => s.name).join(\", \");\n  }\n  return \"\";\n}\n\nfunction getCast(json) {\n  if (json?.actors) {\n    return json.actors.map((a) => a.name).join(\", \");\n  }\n  return \"\";\n}\n\nfunction getCastShort(json, n = 5) {\n  let _cast = getCast(json);\n  if (!_cast) return \"\";\n  return _cast.split(\", \").slice(0, n).join(\", \");\n}\n\nfunction getGenres(json) {\n  return Array.isArray(json?.genre) ? json.genre.join(\", \").toLowerCase() : (json?.genre || \"\").toLowerCase();\n}\n\nfunction getCountries(json) {\n  if (json?.countryOfOrigin) {\n    return json.countryOfOrigin.map((c) => c.name).join(\", \");\n  }\n  return \"\";\n}\n\nfunction getLanguages(doc) {\n  let languages = doc.querySelectorAll(\"a[href^='/films/language/']\");\n  languages = Array.from(languages, (languages) => languages.textContent);\n  languages = [...new Set(languages)]; // Remove duplicates\n  return languages.join(\", \");\n}\n\nfunction getWriters(doc) {\n  let writers = doc.querySelectorAll(\"a[href^='/writer/']\");\n  return Array.from(writers, (writers) => writers.textContent).join(\", \");\n}\n\nfunction getRuntime(doc) {\n  let runtime = doc.querySelector(\"p[class*='text-link']\")?.innerText || \"\";\n  // Remove new lines\n  runtime = runtime.replace(/(\\r\\n|\\n|\\r)/gm, \"\").trim();\n  runtime = runtime.substring(0, runtime.indexOf(\" \")).replace(/\\smins/, \"\");\n  return runtime;\n}\n\nfunction getImdbUrl(doc) {\n  let imdb = doc.querySelector(\"a[data-track-action='IMDb']\")?.href;\n  return imdb ? imdb.replace(/\\/maindetails/, \"\") : \"\";\n}\n\nfunction getAltTitle(doc) {\n  // let alt = doc.querySelector(\"section[id='featured-film-header'] em\")?.innerText || \"\";\n  let altTitle = doc.querySelector(\"h2.originalname em\")?.innerText || \"\";\n  return altTitle.replace(/[‘’]/g, \"\").replace(/\"/g, \"”\");\n}\n\nfunction isUTF8(input) {\n  for (var i = 0; i < input.length; i++) {\n    var temp = input.charCodeAt(i)\n    if (temp > 0xFF) {\n      return false\n    }\n  }\n  return true\n}\n\nfunction getAltTitleUTF8(doc) {\n  altTitle = getAltTitle(doc);\n  if (!isUTF8(altTitle)) {\n    return \"\";\n  } else {\n    return altTitle;\n  }\n}\n\n// --- Helpers ---\n\nfunction isValidHttpUrl(string) {\n  try {\n    let url = new URL(string);\n    return url.protocol === \"http:\" || url.protocol === \"https:\";\n  } catch (_) {\n    return false;\n  }\n}\n\nfunction logParsingError(variable) {\n  console.error(`Parsing Error: Couldn't get ${variable}.`);\n}\n\nfunction safeReturn(result, name) {\n  if (!result) logParsingError(name);\n  return result || \"\";\n}\n\nfunction formatQuote(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `\"${value.replace(/, /g, '\", \"')}\"` : \"\";\n}\n\nfunction formatList(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `\\n- ${value.replace(/, /g, \"\\n- \")}` : \"\";\n}\n\nfunction formatLink(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `[[${value.replace(/, /g, \"]], [[\")}]]` : \"\";\n}\n\nmodule.exports = letterboxd;\n"
  },
  {
    "path": "scripts/odysee.js",
    "content": "// source: https://github.com/basilioss/obsidian-scrapers\r\n\r\nasync function odysee(value, tp, doc) {\r\n  let url = await tp.system.clipboard();\r\n\r\n  if (!isValidHttpUrl(url)) {\r\n    console.error(\"Invalid URL for \" + value);\r\n    return \"\";\r\n  }\r\n\r\n  if (doc === undefined) {\r\n    let page = await tp.obsidian.request({ url });\r\n    let p = new DOMParser();\r\n    doc = p.parseFromString(page, \"text/html\");\r\n  }\r\n\r\n  let json = JSON.parse(\r\n    doc.querySelector(\"script[type='application/ld+json']\").innerHTML\r\n  );\r\n\r\n  switch (value) {\r\n    case \"title\":\r\n      // Get title from JSON. If undefined, return empty string\r\n      let title = json?.name || \"\";\r\n      return title\r\n        .replace(/&amp;/g, \"&\")\r\n        .replace(/&#039;/g, \"'\")\r\n        .replace(/&quot;/g, \"”\");\r\n    case \"description\":\r\n      let description = json?.description || \"\";\r\n      return description\r\n        .replace(/&amp;/g, \"&\")\r\n        .replace(/&#039;/g, \"'\")\r\n        .replace(/&quot;/g, \"”\");\r\n    case \"thumbnail\":\r\n      return json?.thumbnailUrl || \"\";\r\n    case \"published\":\r\n      let published = json?.uploadDate || \"\";\r\n      return published.substring(0, 10);\r\n    case \"duration\":\r\n      let duration = json?.duration || \"\";\r\n      return duration\r\n        .replace(/PT/, \"\")\r\n        .replace(/H/, \"h \")\r\n        .replace(/M/, \"m \")\r\n        .replace(/S/, \"s\");\r\n    case \"url\":\r\n      return json?.url || \"\";\r\n    case \"contentUrl\":\r\n      return json?.contentUrl || \"\";\r\n    case \"embedUrl\":\r\n      return json?.embedUrl || \"\";\r\n    case \"channel\":\r\n      return json.author?.name || \"\";\r\n    case \"keywords\":\r\n      let keywords = json?.keywords || \"\";\r\n      return keywords.replace(/,/g, \", \");\r\n    case \"keywordsQ\":\r\n      // Quotes\r\n      let keywordsQ = json?.keywords || \"\";\r\n      return '\"' + keywordsQ.replace(/,/g, '\", \"') + '\"';\r\n    case \"keywordsL\":\r\n      // List\r\n      let keywordsL = json?.keywords || \"\";\r\n      return \"\\n- \" + keywordsL.replace(/,/g, \"\\n- \");\r\n    case \"keywordsW\":\r\n      // Wiki links\r\n      let keywordsW = json?.keywords || \"\";\r\n      return \"[[\" + keywordsW.replace(/,/g, \"]], [[\") + \"]]\";\r\n    default:\r\n      new Notice(\"Incorrect parameter: \" + value, 5000);\r\n  }\r\n}\r\n\r\nfunction isValidHttpUrl(string) {\r\n  let url;\r\n\r\n  try {\r\n    url = new URL(string);\r\n  } catch (_) {\r\n    return false;\r\n  }\r\n\r\n  return url.protocol === \"http:\" || url.protocol === \"https:\";\r\n}\r\n\r\nmodule.exports = odysee;\r\n"
  },
  {
    "path": "scripts/website.js",
    "content": "// source: https://github.com/basilioss/obsidian-scrapers\n\nasync function website(value, tp, doc) {\n  let url = await tp.system.clipboard();\n\n  if (!isValidHttpUrl(url)) {\n    console.error(\"Invalid URL for \" + value);\n    return \"\";\n  }\n\n  if (doc === undefined) {\n    let page = await tp.obsidian.request(url);\n    let p = new DOMParser();\n    doc = p.parseFromString(page, \"text/html\");\n  }\n\n  // Alias for querySelector\n  let $ = (s) => doc.querySelector(s);\n\n  switch (value) {\n    case \"url\":\n      return url.trim();\n    case \"title\":\n      return (\n        $(\"meta[property='title']\")?.content ||\n        $(\"meta[property='og:title']\")?.content ||\n        $(\"meta[name='twitter:title']\")?.content ||\n        $(\"title\")?.textContent.trim() ||\n        \"\"\n      );\n    case \"description\":\n      let description =\n        $(\"meta[property='og:description']\")?.content ||\n        $(\"meta[name='description']\")?.content ||\n        $(\"meta[name='twitter:description']\")?.content ||\n        \"\";\n      description = description\n        .replace(/&#039;/g, \"'\")\n        .replace(/&#39;/g, \"'\")\n        .trim();\n      return description\n        .replace(/&amp;/g, \"&\")\n        .replace(/&quot;/g, '\"')\n        .replace(/&nbsp;/g, \" \");\n    case \"image\":\n      let image =\n        $(\"meta[property='og:image']\")?.content ||\n        $(\"meta[name='twitter:image']\")?.content ||\n        $(\"meta[name='twitter:image:src']\")?.content ||\n        \"\";\n      // Remove unnecessary part\n      return image.replace(/\\?.*$/g, \"\")\n    default:\n      new Notice(\"Incorrect parameter: \" + value, 5000);\n  }\n}\n\nfunction isValidHttpUrl(string) {\n  let url;\n\n  try {\n    url = new URL(string);\n  } catch (_) {\n    return false;\n  }\n\n  return url.protocol === \"http:\" || url.protocol === \"https:\";\n}\n\nmodule.exports = website;\n"
  },
  {
    "path": "scripts/wikipedia.js",
    "content": "// source: https://github.com/basilioss/obsidian-scrapers\n\nasync function wikipedia(value, tp, doc) {\n  let url = await tp.system.clipboard();\n\n  if (!isValidHttpUrl(url)) {\n    console.error(\"Invalid URL for \" + value);\n    return \"\";\n  }\n\n  if (doc === undefined) {\n    let page = await tp.obsidian.request({ url });\n    let p = new DOMParser();\n    doc = p.parseFromString(page, \"text/html\");\n  }\n\n  let json = \"\";\n  try {\n    json = JSON.parse(\n      doc.querySelector(\"script[type='application/ld+json']\").innerHTML\n    );\n  } catch (error) {\n    new Notice(error);\n  }\n\n  switch (value) {\n    case \"title\":\n      return json?.name || \"\";\n    case \"url\":\n      return json?.url || \"\";\n    case \"image\":\n      return json?.image || \"\";\n    case \"headline\":\n      // Short description\n      return json?.headline || \"\";\n    default:\n      new Notice(\"Incorrect parameter: \" + value, 5000);\n  }\n}\n\nfunction isValidHttpUrl(string) {\n  let url;\n\n  try {\n    url = new URL(string);\n  } catch (_) {\n    return false;\n  }\n\n  return url.protocol === \"http:\" || url.protocol === \"https:\";\n}\n\nmodule.exports = wikipedia;\n"
  },
  {
    "path": "scripts/youtube.js",
    "content": "// source: https://github.com/basilioss/obsidian-scrapers\n\nasync function youtube(value, tp, doc) {\n  let url = await tp.system.clipboard();\n\n  if (!isValidHttpUrl(url)) {\n    console.error(\"Invalid URL for \" + value);\n    return \"\";\n  }\n\n  if (doc === undefined) {\n    // Alternative front-end (invidious.io)\n    let altDomain = \"yewtu.be\";\n    if (url.includes(altDomain)) {\n      var regex = new RegExp(altDomain, \"g\");\n      url = url.replace(regex, \"youtube.com\");\n    }\n    let page = await tp.obsidian.request({ url });\n    let p = new DOMParser();\n    doc = p.parseFromString(page, \"text/html\");\n  }\n\n  const $ = (selector) => doc.querySelector(selector);\n\n  switch (value) {\n    case \"title\":\n      return safeReturn(getTitle(doc), \"title\");\n    case \"channel\":\n      return safeReturn(getChannel(doc), \"channel\");\n    case \"published\":\n      return safeReturn(getPublished(doc), \"published\");\n    case \"url\":\n      return safeReturn(getShortUrl(doc), \"url\");\n    case \"thumbnail\":\n      return safeReturn(getThumbnail(doc), \"thumbnail\");\n    case \"keywords\":\n      return safeReturn(getKeywords(doc), \"keywords\");\n    case \"keywordsQ\":\n    case \"keywordsQuotes\":\n      return formatQuote(getKeywords(doc), \"keywords\");\n    case \"keywordsL\":\n    case \"keywordsList\":\n      return formatList(getKeywords(doc), \"keywords\");\n    case \"keywordsW\":\n    case \"keywordsLinks\":\n      return formatLink(getKeywords(doc), \"keywords\");\n    case \"duration\":\n      return safeReturn(getDuration(doc), \"duration\");\n    case \"description\":\n      return safeReturn(getDescription(doc), \"description\");\n    case \"descriptionFull\":\n      return safeReturn(getDescriptionFull(doc), \"descriptionFull\");\n    case \"id\":\n      return safeReturn(getId(doc), \"id\");\n    default:\n      new Notice(\"Incorrect parameter: \" + value, 5000);\n      return \"\";\n  }\n}\n\n// --- Data extractors ---\n\nfunction getTitle(doc) {\n  const title = doc.querySelector(\"meta[property='og:title']\")?.content || \"\";\n  return title.replace(/\"/g, \"'\");\n}\n\nfunction getChannel(doc) {\n  return doc.querySelector(\"link[itemprop='name']\")?.getAttribute(\"content\") || \"\";\n}\n\nfunction getPublished(doc) {\n  return doc.querySelector(\"meta[itemprop='uploadDate']\")?.content || \"\";\n}\n\nfunction getShortUrl(doc) {\n  return doc.querySelector(\"link[rel='shortLinkUrl']\")?.href || \"\";\n}\n\nfunction getThumbnail(doc) {\n  const shortUrl = getShortUrl(doc);\n  return shortUrl\n    ? shortUrl.replace(/youtu\\.be/, \"img.youtube.com/vi\") + \"/maxresdefault.jpg\"\n    : \"\";\n}\n\nfunction getKeywords(doc) {\n  return doc.querySelector(\"meta[name='keywords']\")?.content || \"\";\n}\n\nfunction getDuration(doc) {\n  let duration = doc.querySelector(\"meta[itemprop='duration']\")?.content || \"\";\n  if (duration.startsWith(\"PT\")) duration = duration.slice(2);\n  return duration.replace(/M/gi, \"m \").replace(/S/gi, \"s\");\n}\n\nfunction getDescription(doc) {\n  const desc = doc.querySelector(\"meta[itemprop='description']\")?.content || \"\";\n  return desc.replace(/\"/g, \"'\");\n}\n\nfunction getDescriptionFull(doc) {\n  const html = new XMLSerializer().serializeToString(doc);\n  const match = html.match(/\"shortDescription\":\"(.*?)\",\"isCrawlable\":/);\n  if (!match) return \"\";\n  return match[1]\n    .replace(/\\\\u0026/g, \"&\")\n    .replace(/\\\\n/g, \"\\n\")\n    .replace(/\\\\r/g, \"\")\n    .replace(/\\\\\"/g, '\"');\n}\n\nfunction getId(doc) {\n  return doc.querySelector(\"meta[itemprop='identifier']\")?.content || \"\";\n}\n\n// --- Helpers ---\n\nfunction safeReturn(result, name) {\n  if (!result) logParsingError(name);\n  return result || \"\";\n}\n\nfunction formatQuote(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `\"${value.replace(/, /g, '\", \"')}\"` : \"\";\n}\n\nfunction formatList(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `\\n- ${value.replace(/, /g, \"\\n- \")}` : \"\";\n}\n\nfunction formatLink(value, name) {\n  if (!value) logParsingError(name);\n  return value ? `[[${value.replace(/, /g, \"]], [[\")}]]` : \"\";\n}\n\nfunction isValidHttpUrl(string) {\n  try {\n    let url = new URL(string);\n    return url.protocol === \"http:\" || url.protocol === \"https:\";\n  } catch (_) {\n    return false;\n  }\n}\n\nfunction logParsingError(variable) {\n  console.error(`Parsing Error: Couldn't get ${variable}.`);\n}\n\nmodule.exports = youtube;\n"
  },
  {
    "path": "templates/goodreads.md",
    "content": "---\n<%*\n// Request a web page to speed up execution time\nlet page = await tp.obsidian.request(await tp.system.clipboard())\nlet doc = new DOMParser().parseFromString(page,\"text/html\")\n\nlet title = await tp.user.goodreads('title', tp, doc)\n-%>\nurl: \"<% tp.user.goodreads('url', tp, doc) %>\"\nisbn: <% tp.user.goodreads('isbn', tp, doc) %>\npublished: <% tp.user.goodreads('published', tp, doc) %>\npages: <% tp.user.goodreads('pageCount', tp, doc) %>\nratings: <% tp.user.goodreads('rating', tp, doc) %>\nauthors: [<% tp.user.goodreads('authorsQuotes', tp) %>]\ngenres: [<% tp.user.goodreads('genresQuotes', tp, doc) %>]\n---\n\n# <% title %>\n\n![](<% tp.user.goodreads('cover', tp, doc) %>)\n\n## Description\n\n<% tp.user.goodreads('description', tp, doc) %>\n\n## Authors\n\n<% tp.user.goodreads('authors', tp) %>\n\nLinks: <% tp.user.goodreads('authorsLinks', tp) %>\n\nList:\n<% tp.user.goodreads('authorsList', tp) %>\n\n## Genres\n\n<% tp.user.goodreads('genres', tp) %>\n\nLinks: <% tp.user.goodreads('genresLinks', tp) %>\n\nList:\n<% tp.user.goodreads('genresList', tp) %>\n\n<%* \nlet filename = title\n// Remove prohibited characters\nfilename = filename.replace(/[/\\:*?<>|\"\"]/g, \"\")\n// Rename a note\nawait tp.file.move(filename)\n-%>\n"
  },
  {
    "path": "templates/imdb.md",
    "content": "---\n<%*\n// Request a web page to speed up execution time\nlet page = await tp.obsidian.request(await tp.system.clipboard())\nlet doc = new DOMParser().parseFromString(page,\"text/html\")\n\nlet title = await tp.user.imdb('title', tp, doc)\n-%>\nurl: \"<% tp.user.imdb('url', tp, doc) %>\"\nimdb-rating: <% tp.user.imdb('imdbRating', tp, doc) %>\ncontent-rating: <% tp.user.imdb('contentRating', tp, doc) %>\nduration: <% tp.user.imdb('duration', tp, doc) %>\nyear: <% tp.user.imdb('published', tp, doc) %> \ntype: <% tp.user.imdb('type', tp, doc) %>\ngenres: [<% tp.user.imdb('genresQuotes', tp, doc) %>]\nkeywords: [<% tp.user.imdb('keywordsQuotes', tp, doc) %>]\ndirectors: [<% tp.user.imdb('directorsQuotes', tp, doc) %>]\ncreators: [<% tp.user.imdb('creatorsQuotes', tp, doc) %>]\ncast: [<% tp.user.imdb('starsQuotes', tp, doc) %>]\ncountries: [<% tp.user.imdb('countriesQuotes', tp, doc) %>]\n---\n\n# <% title %>\n\n## Image\n\n![](<% tp.user.imdb('image', tp, doc) %>)\n\n## Description\n\n<% tp.user.imdb('description', tp, doc) %>\n\n## Genres\n\n- <% tp.user.imdb('genres', tp, doc) %>\n- Links: <% tp.user.imdb('genresLinks', tp, doc) %>\n\nList:\n<% tp.user.imdb('genresList', tp, doc) %>\n\n## Keywords\n\n- <% tp.user.imdb('keywords', tp, doc) %>\n- Links: <% tp.user.imdb('keywordsLinks', tp, doc) %>\n\nList:\n<% tp.user.imdb('keywordsList', tp, doc) %>\n\n## Directors\n\n- <% tp.user.imdb('directors', tp, doc) %>\n- Links: <% tp.user.imdb('directorsLinks', tp, doc) %>\n\nList:\n<% tp.user.imdb('directorsList', tp, doc) %>\n\n## Creators\n\n- <% tp.user.imdb('creators', tp, doc) %>\n- Links: <% tp.user.imdb('creatorsLinks', tp, doc) %>\n\nList:\n<% tp.user.imdb('creatorsList', tp, doc) %>\n\n## Countries\n\n- <% tp.user.imdb('countries', tp, doc) %>\n- Links: <% tp.user.imdb('countriesLinks', tp, doc) %>\n\nList:\n<% tp.user.imdb('countriesList', tp, doc) %>\n\n## Cast\n\n- <% tp.user.imdb('stars', tp, doc) %>\n- Links: <% tp.user.imdb('starsLinks', tp, doc) %>\n\nList:\n<% tp.user.imdb('starsList', tp, doc) %>\n\n<%* \nlet filename = title\n// Remove prohibited characters\nfilename = filename.replace(/[/\\:*?<>|\"\"]/g, \"\")\n// Rename a note\nawait tp.file.move(filename)\n-%>\n"
  },
  {
    "path": "templates/letterboxd.md",
    "content": "---\n<%*\n// Request a web page to speed up execution time\nlet page = await tp.obsidian.request(await tp.system.clipboard())\nlet doc = new DOMParser().parseFromString(page,\"text/html\")\n\nlet title = await tp.user.letterboxd('title', tp, doc)\nlet altTitle = await tp.user.letterboxd('altTitle', tp, doc)\n-%>\naliases: <%* altTitle == \"\" ? tR += '[\"' + title + '\"]' : tR += '[\"' + title + '\", \"' + altTitle + '\"]' %>\nurl: \"<% tp.user.letterboxd('url', tp, doc) %>\"\nimdb-url: <% tp.user.letterboxd('imdbUrl', tp, doc) %>\ntmdb-url: <% tp.user.letterboxd('tmdbUrl', tp, doc) %>\nrating: <% tp.user.letterboxd('rating', tp, doc) %>\nruntime: <% tp.user.letterboxd('runtime', tp, doc) %>\nyear: <% tp.user.letterboxd('published', tp, doc) %> \ngenres: [<% tp.user.letterboxd('genresQuotes', tp, doc) %>]\ndirectors: [<% tp.user.letterboxd('directorsQuotes', tp, doc) %>]\nstudios: [<% tp.user.letterboxd('studiosQuotes', tp, doc) %>]\ncountries: [<% tp.user.letterboxd('countriesQuotes', tp, doc) %>]\nlanguages: [<% tp.user.letterboxd('languagesQuotes', tp, doc) %>]\nwriters: [<% tp.user.letterboxd('writersQuotes', tp, doc) %>]\ncast: [<% tp.user.letterboxd('castShortQuotes', tp, doc) %>]\n---\n\n# <% tp.user.letterboxd('title', tp, doc) %>\n\n## Image\n\n![](<% tp.user.letterboxd('image', tp, doc) %>)\n\n## Description\n\n<% tp.user.letterboxd('description', tp, doc) %>\n\n## Genres\n\n<% tp.user.letterboxd('genres', tp, doc) %>\n\nLinks: <% tp.user.letterboxd('genresLinks', tp, doc) %>\n\nList:\n<% tp.user.letterboxd('genresL', tp, doc) %>\n\n## Directors\n\n<% tp.user.letterboxd('directors', tp, doc) %>\n\nLinks: <% tp.user.letterboxd('directorsLinks', tp, doc) %>\n\nList:\n<% tp.user.letterboxd('directorsList', tp, doc) %>\n\n## Studios\n\n<% tp.user.letterboxd('studios', tp, doc) %>\n\nLinks: <% tp.user.letterboxd('studiosLinks', tp, doc) %>\n\nList:\n<% tp.user.letterboxd('studiosList', tp, doc) %>\n\n## Countries\n\n<% tp.user.letterboxd('countries', tp, doc) %>\n\nLinks: <% tp.user.letterboxd('countriesLinks', tp, doc) %>\n\nList:\n<% tp.user.letterboxd('countriesList', tp, doc) %>\n\n## Languages\n\n<% tp.user.letterboxd('languages', tp, doc) %>\n\nLinks: <% tp.user.letterboxd('languagesLinks', tp, doc) %>\n\nList:\n<% tp.user.letterboxd('languagesList', tp, doc) %>\n\n## Writers\n\n<% tp.user.letterboxd('writers', tp, doc) %>\n\nLinks: <% tp.user.letterboxd('writersLinks', tp, doc) %>\n\nList:\n<% tp.user.letterboxd('writersList', tp, doc) %>\n\n## Cast (shortlist)\n\n<% tp.user.letterboxd('castShort', tp, doc) %>\n\nLinks: <% tp.user.letterboxd('castShortLinks', tp, doc) %>\n\nList:\n<% tp.user.letterboxd('castShortList', tp, doc) %>\n\n## Cast\n\n<% tp.user.letterboxd('cast', tp, doc) %>\n\nLinks: <% tp.user.letterboxd('castLinks', tp, doc) %>\n\nList:\n<% tp.user.letterboxd('castList', tp, doc) %>\n\n<%* \nlet filename = title\n// Remove prohibited characters\nfilename = filename.replace(/[/\\:*?<>|\"\"]/g, \"\")\n// Rename a note\nawait tp.file.move(filename)\n-%>\n"
  },
  {
    "path": "templates/odysee.md",
    "content": "---\n<%*\n// Request a web page to speed up execution time\nlet page = await tp.obsidian.request(await tp.system.clipboard())\nlet doc = new DOMParser().parseFromString(page,\"text/html\")\n\nlet title = await tp.user.odysee('title', tp, doc)\n-%>\nchannel: \"<% tp.user.odysee('channel', tp, doc) %>\"\npublished: <% tp.user.odysee('published', tp, doc) %>\nurl: \"<% tp.user.odysee('url', tp, doc) %>\"\ncontent-url: \"<% tp.user.odysee('contentUrl', tp, doc) %>\"\nduration: <% tp.user.odysee('duration', tp, doc) %>\nkeywords: [<% tp.user.odysee('keywordsQ', tp, doc) %>]\n---\n\n# <% title %>\n\n## Thumbnail\n\n![](<% tp.user.odysee('thumbnail', tp, doc) %>)\n\n## Keywords\n\n<% tp.user.odysee('keywords', tp, doc) %>\n\nLinks: <% tp.user.odysee('keywordsW', tp, doc) %>\n\nList:\n<% tp.user.odysee('keywordsL', tp, doc) %>\n\n## Description\n\n<% tp.user.odysee('description', tp, doc) %>\n\n<%* \nlet filename = title\n// Remove prohibited characters\nfilename = filename.replace(/[/\\:*?<>|\"\"]/g, \"\")\n// Rename a note\nawait tp.file.move(filename)\n-%>\n"
  },
  {
    "path": "templates/scraper.md",
    "content": "<%* \nlet clipboard = await tp.system.clipboard();\nclipboard = clipboard.trim(); // remove whitespace from both ends\nlet urlExpression = /^https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)?/gmi;\nlet urlRegex = new RegExp(urlExpression);\n\nif (clipboard.includes(\"/www.youtube.com/\")) {\n  // Insert YouTube template\n  tR += await tp.file.include(\"[[youtube]]\");\n} else if (clipboard.includes(\"/youtu.be/\")) {\n  tR += await tp.file.include(\"[[youtube]]\");\n} else if (clipboard.includes(\"/yewtu.be/\")) {\n  // Alternative YouTube front-end (invidious.io)\n  tR += await tp.file.include(\"[[youtube]]\");\n} else if (clipboard.includes(\"/www.goodreads.com/\")) {\n  tR += await tp.file.include(\"[[goodreads]]\");\n} else if (clipboard.includes(\"/www.imdb.com/\")) {\n  tR += await tp.file.include(\"[[imdb]]\");\n} else if (clipboard.includes(\"/letterboxd.com/\")) {\n  tR += await tp.file.include(\"[[letterboxd]]\");\n} else if (clipboard.includes(\"wikipedia.org/\")) {\n  tR += await tp.file.include(\"[[wikipedia]]\");\n} else if (clipboard.includes(\"/odysee.com/\")) {\n  tR += await tp.file.include(\"[[odysee]]\");\n} else if (clipboard.match(urlRegex)) {\n  tR += await tp.file.include(\"[[website]]\");\n} else {\n  new Notice(\"No link in the clipboard\");\n}\n%>\n"
  },
  {
    "path": "templates/website.md",
    "content": "[<% tp.user.website('title', tp) %>](<% tp.user.website('url', tp) %>)\n\n![](<% tp.user.website('image', tp) %>)\n\n<% tp.user.website('description', tp) %>"
  },
  {
    "path": "templates/wikipedia.md",
    "content": "[<% tp.user.wikipedia('title', tp) %>](<% tp.user.wikipedia('url', tp) %>)\n\n![](<% tp.user.wikipedia('image', tp) %>)\n\n<% tp.user.wikipedia('headline', tp) %>"
  },
  {
    "path": "templates/youtube.md",
    "content": "---\n<%*\n// Request a web page to speed up execution time\nlet page = await tp.obsidian.request(await tp.system.clipboard())\nlet doc = new DOMParser().parseFromString(page,\"text/html\")\n\nlet title = await tp.user.youtube('title', tp, doc)\n-%>\nchannel: \"<% tp.user.youtube('channel', tp, doc) %>\"\npublished: <% tp.user.youtube('published', tp, doc) %>\nurl: \"<% tp.user.youtube('url', tp, doc) %>\"\nduration: <% tp.user.youtube('duration', tp, doc) %>\nid: <% tp.user.youtube('id', tp, doc) %>\nkeywords: [<% tp.user.youtube('keywordsQuotes', tp, doc) %>]\n---\n\n# <% title %>\n\n## Thumbnail\n\n![](<% tp.user.youtube('thumbnail', tp, doc) %>)\n\n## Keywords\n\n<% tp.user.youtube('keywords', tp, doc) %>\n\nLinks: <% tp.user.youtube('keywordsLinks', tp, doc) %>\n\nList:\n<% tp.user.youtube('keywordsList', tp, doc) %>\n\n## Description\n\n<% tp.user.youtube('description', tp, doc) %>\n\n## Full description\n\n<% tp.user.youtube('descriptionFull', tp, doc) %>\n\n<%* \nlet filename = title\n// Remove prohibited characters\nfilename = filename.replace(/[/\\:*?<>|\"\"]/g, \"\")\n// Rename a note\nawait tp.file.move(filename)\n-%>\n"
  }
]