Pret Inter Extractor (ctrl + shift + l)

Extracts interlibrary loans sorted by section and then by call number, then downloads the info as a text file when a shortcut is pressed.

当前为 2025-03-23 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Pret Inter Extractor (ctrl + shift + l)
  3. // @version 1.0
  4. // @description Extracts interlibrary loans sorted by section and then by call number, then downloads the info as a text file when a shortcut is pressed.
  5. // @author Mojo Jojo
  6. // @match https://bgmdolly.gminvent.fr/*
  7. // @grant none
  8. // @namespace https://greasyfork.org/users/1448578
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Set the shortcut key to CTRL + SHIFT + L
  15. const shortcutKey = "l";
  16.  
  17. document.addEventListener("keydown", function(event) {
  18. if (event.key.toLowerCase() === shortcutKey && event.ctrlKey && event.shiftKey) {
  19. event.preventDefault(); // Prevent default browser actions
  20. console.log("✅ Shortcut CTRL + SHIFT + L detected! Running script...");
  21. extractAndDownload();
  22. }
  23. });
  24.  
  25. function extractAndDownload() {
  26. const targetHeaders = [
  27. "Mise à disposition pour Bibliothèque publique de Robermont",
  28. "Mise à disposition pour Navette de la province",
  29. "Mise à disposition pour Séminaire"
  30. ];
  31.  
  32. function normalizeText(text) {
  33. return text
  34. .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
  35. .replace(/\s+/g, " ")
  36. .replace(/ç/g, "c")
  37. .trim().toLowerCase();
  38. }
  39.  
  40. let bookSections = {
  41. "Section jeunesse": [],
  42. "Section adulte": [],
  43. "Réserve": [],
  44. "Others": []
  45. };
  46.  
  47. let processedBooks = new Set();
  48. let outputText = "";
  49.  
  50. const allDivs = Array.from(document.querySelectorAll("div"));
  51.  
  52. let headerDivs = allDivs.filter(div =>
  53. targetHeaders.some(header => normalizeText(div.innerText).includes(normalizeText(header)))
  54. );
  55.  
  56. headerDivs = Array.from(new Set(headerDivs.map(div => div.innerText))).map(text =>
  57. allDivs.find(div => normalizeText(div.innerText) === normalizeText(text))
  58. );
  59.  
  60. // outputText += `📚 Found headers: ${headerDivs.length}\n\n`;
  61.  
  62. headerDivs.forEach(headerDiv => {
  63. let possibleTable = headerDiv.closest("td")?.parentElement?.nextElementSibling?.querySelector("table")
  64. || headerDiv.closest("tr")?.nextElementSibling?.querySelector("table")
  65. || headerDiv.closest("td")?.parentElement?.parentElement?.querySelector("table");
  66.  
  67. if (!possibleTable) {
  68. console.warn("No table found for:", headerDiv.innerText.trim());
  69. return;
  70. }
  71.  
  72. let rows = possibleTable.querySelectorAll("tbody > tr");
  73.  
  74. rows.forEach(row => {
  75. try {
  76. let columns = Array.from(row.querySelectorAll("td"));
  77.  
  78. if (columns.length < 4) {
  79. console.warn("⚠️ Skipping row due to missing columns:", row.innerText.trim());
  80. return;
  81. }
  82.  
  83. let titleCell = columns[2];
  84. let coteCell = columns[3];
  85.  
  86. if (titleCell && coteCell) {
  87. let title = titleCell.innerText.trim();
  88. let coteLines = coteCell.innerText.trim().split("\n").map(line => normalizeText(line));
  89. let coteFirstRow = coteLines.length > 0 ? coteLines[0] : "⚠️⚠️";
  90. let coteSecondRow = coteLines.length > 1 ? coteLines[1] : "⚠️⚠️";
  91. let fullCoteInfo = coteLines.join("\n");
  92.  
  93. let bookKey = `${title}-${coteSecondRow}`;
  94. if (processedBooks.has(bookKey)) {
  95. return;
  96. }
  97. processedBooks.add(bookKey);
  98.  
  99. let section = "Others";
  100.  
  101. if (coteFirstRow.includes("jeunesse")) {
  102. section = "Section jeunesse";
  103. } else if (coteFirstRow.includes("adulte")) {
  104. section = "Section adulte";
  105. } else if (coteFirstRow.includes("reserve") || coteFirstRow.includes("réserve")) {
  106. section = "Réserve";
  107. }
  108.  
  109. bookSections[section].push({ title, coteSecondRow, fullCoteInfo });
  110.  
  111. }
  112. } catch (error) {
  113. console.error("❌ Error processing row:", error);
  114. }
  115. });
  116. });
  117.  
  118. function sortByCote(a, b) {
  119. return a.coteSecondRow.localeCompare(b.coteSecondRow);
  120. }
  121.  
  122. for (let section in bookSections) {
  123. bookSections[section].sort(sortByCote);
  124. }
  125.  
  126. if (bookSections["Others"].length === 0) {
  127. delete bookSections["Others"];
  128. }
  129.  
  130. outputText += `📚 Found books: ${Object.values(bookSections).flat().length}`;
  131.  
  132. for (let section in bookSections) {
  133. outputText += `\n===================== ${section} =====================\n`;
  134.  
  135. bookSections[section].forEach(book => {
  136. outputText += `📖 ${book.title}\n👀 COTE: ${book.coteSecondRow.toUpperCase()}\n`;
  137.  
  138. if (section === "Others") {
  139. outputText += `📜 Full Cote Info:\n${book.fullCoteInfo}\n`;
  140. }
  141.  
  142. outputText += "------------------\n";
  143. });
  144. }
  145.  
  146. function downloadTextFile(filename, text) {
  147. let element = document.createElement("a");
  148. let file = new Blob([text], { type: "text/plain" });
  149. element.href = URL.createObjectURL(file);
  150. element.download = filename;
  151. document.body.appendChild(element);
  152. element.click();
  153. document.body.removeChild(element);
  154. }
  155.  
  156. downloadTextFile("pret_inter_sorted.txt", outputText);
  157. }
  158. })();