decembre

Library Helper v.17 - Adding bibliothèque de Bobigny 93000 (France) - COR 2026.08

Aug 19th, 2026 (edited)
114
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
CSS 19.05 KB | None | 0 0
  1. // ==UserScript==
  2. // @name         v.17 - Adding bibliothèque de Bobigny 93000 (France)
  3. // @namespace    https://github.com/chihchun
  4. // @version      1.17
  5. // @description  A userscript that display links between different libraries and book stores.
  6. // @author       Rex Tsai <rex.cc.tsai@gmail.com>
  7. // @match        https://book.tpml.edu.tw/bookDetail/*
  8. // @match        https://book.douban.com/subject/*
  9. // @match        https://books.google.com.tw/books*
  10. // @match        https://books.google.com/books*
  11. // @match        https://books.google.fr/books*
  12. // @match        https://play.google.com/store/books/details/*
  13. // @match        https://play.google.com/store/books/details?id=*
  14. // @match        https://share.readmoo.com/book/*
  15. // @match        https://readmoo.com/book/*
  16. // @match        https://www.amazon.cn/dp/*
  17. // @match        https://www.amazon.cn/gp/product/*
  18. // @match        https://www.amazon.com/*/dp/*
  19. // @match        https://www.amazon.com/gp/product/*
  20. // @match        https://www.books.com.tw/products/*
  21. // @match        https://www.goodreads.com/book/show/*
  22. // @match        https://www.kobo.com/*/ebook*
  23. // @match        https://www.taaze.tw/goods/*
  24. // @match        https://www.taaze.tw/usedList.html?oid=*
  25. // @match        https://www.babelio.com/livres/*
  26.  
  27. // @match        https://bibliotheque-bobigny.siib.fr/*
  28. // @match        https://mediatheques.bobigny.fr/index.php/*
  29. // @match        https://bibliotheque.ville-bobigny.fr/detail-d-une-notice/notice/*
  30. // @match        https://mediatheques.bobigny.fr/index.php/detail-dune-notice/notice/*
  31.  
  32. // @match        https://webpac.tphcc.gov.tw/webpac/content.cfm*
  33. // @match        https://www.eslite.com/product/*
  34. // @grant        none
  35. // @require      https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.13.1/js-yaml.min.js
  36. // @run-at       document-idle
  37. // @license      MIT; https://github.com/chihchun/library-helper/blob/master/LICENSE
  38. // @copyright    2019, chihchun (https://github.com/chihchun)
  39. // @supportURL   https://github.com/chihchun/library-helper/issues
  40. // ==/UserScript==
  41.  
  42. (function() {
  43.     'use strict';
  44.  
  45.     var metadata_yaml = `
  46. amazon.com:
  47.     matches:
  48.         - https://www.amazon.com/gp/product/*
  49.     type: 'XPATH'
  50.     metadata:
  51.         title: "//span[@id='ebooksProductTitle']"
  52.         authors: "//span[contains(@data-a-popover, 'Author Dialog')]/a"
  53.         asin: "//form[@id='sendSample']/input[@name='ASIN.0']/@value"
  54. amazon.com/dp:
  55.     matches:
  56.     - https://www.amazon.com/*
  57.     type: 'XPATH'
  58.     metadata:
  59.         title: "//span[@id='ebooksProductTitle']"
  60.         authors: //span[contains(@class, 'author')]/a
  61.         asin: "//form[@id='sendSample']/input[@name='ASIN.0']/@value"
  62.  
  63. amazon.cn:
  64.     matches:
  65.         - "https://www.amazon.cn/gp/product/*"
  66.         - "https://www.amazon.cn/dp/*"
  67.     type: 'XPATH'
  68.     metadata:
  69.         title: "//span[@id='ebooksProductTitle']"
  70.         authors: "//span[contains(@class,'author')]/a"
  71.         asin: "//form[@id='sendSample']/input[@name='ASIN.0']/@value"
  72.  
  73. babelio.com:
  74.     matches:
  75.         - "https://www.babelio.com/livres/*"
  76.     type: 'XPATH'
  77.     metadata:
  78.         title: "//h1"
  79.         authors: "//span[@itemprop='name']"
  80.  
  81. mediatheques.bobigny.fr:
  82.     matches:
  83.         - "https://mediatheques.bobigny.fr/index.php/detail-dune-notice/notice/*"
  84.     type: 'XPATH'
  85.     metadata:
  86.         title: "//title"
  87.         authors: "//a[starts-with(@href, '/index.php/recherche-simple/simple/Auteur/')]"
  88.  
  89. books.com.tw:
  90.     matches:
  91.         - "https://www.books.com.tw/products/*"
  92.     type: 'XPATH'
  93.     metadata:
  94.         title: "//h1"
  95.         origtitle: "//h2/a[contains(@href,'https://search.books.com.tw/search/query/cat/all/key')]"
  96.         isbn: "//li[contains(text(),'ISBN')]"
  97.         price: "//ul[@class='price']/li/em"
  98.         sellingprice: "//b[@itemprop='price']"
  99.         authors: "//a[contains(@href,'adv_author')]"
  100.         publishdate: "//li[contains(text(),'出版日期')]"
  101.  
  102. books.google.com.tw:
  103.     matches:
  104.         - "https://books.google.com.tw/books/*"
  105.         - "https://books.google.com/books/*"
  106.         - "https://books.google.fr/books/*"
  107.     type: 'XPATH'
  108.     metadata:
  109.         title: "//meta[@property='og:title']/@content"
  110.         authors: "//a[contains(@href,'q=inauthor')]"
  111.  
  112. eslite.com:
  113.     matches:
  114.         - "https://www.eslite.com/product/*"
  115.     type: 'XPATH'
  116.     metadata:
  117.         title: "//div[@class='product-information']//h1"
  118.         authors: "//a[contains(@href,'/search?author')]"
  119.     delaytime: 2000
  120.  
  121. goodreads.com:
  122.     matches:
  123.         - "https://www.goodreads.com/book/show/*"
  124.     type: 'XPATH'
  125.     metadata:
  126.         title: "//meta[@property='og:title']/@content"
  127.         authors: "//a[@class='authorName']/span[@itemprop='name']"
  128.         isbn: "//meta[@property='books:isbn']/@content"
  129.         rating: "//span[@itemprop='ratingValue']"
  130.  
  131. kobo.com:
  132.     matches:
  133.         - "https://www.kobo.com/*"
  134.     type: 'JSON-LD'
  135.     metadata:
  136.         title: '//span[@class="title product-field"]'
  137.         authors: '//a[@class="contributor-name"]'
  138.         origtitle: "//span[contains(@class, 'subtitle')]"
  139.  
  140. play.google.com:
  141.     matches:
  142.         - "https://play.google.com/store/books/details/*"
  143.         - "https://play.google.com/store/books/details?id=*"
  144.     type: 'JSON-LD'
  145.     metadata:
  146.  
  147. readmoo.com:
  148.     matches:
  149.         - "https://share.readmoo.com/book/*"
  150.         - "https://readmoo.com/book/*"
  151.     type: 'XPATH'
  152.     metadata:
  153.         title: "//h2"
  154.         isbn: "//span[@itemprop='ISBN']"
  155.         authors: "//span[@itemprop='name']/a"
  156.  
  157. taaze.tw:
  158.     matches:
  159.         - "https://www.taaze.tw/goods/*"
  160.     type: 'XPATH'
  161.     metadata:
  162.         title: "//div[contains(@class, 'mBody')]//h1"
  163.         origtitle: "//div[contains(@class, 'mBody')]//h2"
  164.         isbn: "//meta[@property='books:isbn']/@content"
  165.         authors: "//div[@class='authorBrand']//a[contains(@href,'rwd_searchResult.html?keyType%5B%5D=2')]"
  166.  
  167. taaze.tw/used:
  168.     matches:
  169.         - "https://www.taaze.tw/usedList.html*"
  170.     type: 'XPATH'
  171.     metadata:
  172.         origtitle: "//div[contains(@class, 'hide')]//div[@class='title-next']"
  173.         authors: "//a[contains(@href,'rwd_searchResult.html?keyType%5B%5D=2')]"
  174.  
  175. tpml.edu.tw:
  176.     matches:
  177.         - "https://book.tpml.edu.tw/bookDetail/*"
  178.     type: 'XPATH'
  179.     metadata:
  180.         title: "//div[@class='bookdata']/h2"
  181.         authors: "//a[contains(@href,'searchField=PN')]"
  182.     delaytime: 1200
  183.     fixtitle: True
  184.  
  185. webpac.tphcc.gov.tw:
  186.     matches:
  187.         - "https://webpac.tphcc.gov.tw/webpac/content.cfm*"
  188.     type: 'XPATH'
  189.     metadata:
  190.         title: "//h2"
  191.         authors: "//div[@class='detail simple']/p[1]"
  192.         isbn: "//div[@class='detail simple']/p[4]"
  193.  
  194. `;
  195.  
  196. var search_yaml = `
  197. 博客來:
  198.     url: "https://search.books.com.tw/search/query/key/"
  199.     languages:
  200.         - "en"
  201.         - "en-US"
  202.         - "zh"
  203.         - "zh-TW"
  204.         - "zh-HK"
  205.         - "zh-CN"
  206.  
  207. 誠品線上:
  208.     url: "https://www.eslite.com/Search?keyword="
  209.     languages:
  210.         - "en"
  211.         - "en-US"
  212.         - "zh"
  213.         - "zh-TW"
  214.         - "zh-HK"
  215.         - "zh-CN"
  216.  
  217. Kobo:
  218.     url: "https://www.kobo.com/tw/zh/search?query="
  219.     languages:
  220.         - "en"
  221.         - "en-US"
  222.         - "zh"
  223.         - "zh-TW"
  224.         - "zh-HK"
  225.         - "zh-CN"
  226.         - "fr"
  227.         - "fr-ca"
  228.         - "fr-fr"
  229.  
  230. GooglePlay:
  231.     url: "https://play.google.com/store/search?c=books&q="
  232.     languages:
  233.         - "en"
  234.         - "en-US"
  235.         - "zh"
  236.         - "zh-TW"
  237.         - "zh-HK"
  238.         - "zh-CN"
  239.         - "fr"
  240.         - "fr-ca"
  241.         - "fr-fr"
  242.  
  243. AmazonCN:
  244.     url: "https://www.amazon.cn/s?rh=n%3A116169071&k="
  245.     languages:
  246.         - "en"
  247.         - "en-US"
  248.         - "zh"
  249.         - "zh-TW"
  250.         - "zh-HK"
  251.         - "zh-CN"
  252.  
  253. 豆瓣:
  254.     url: "https://search.douban.com/book/subject_search?search_text="
  255.     languages:
  256.         - "zh"
  257.         - "zh-TW"
  258.         - "zh-HK"
  259.         - "zh-CN"
  260.  
  261. Goodreads:
  262.     url: "https://www.goodreads.com/search?q="
  263.     languages:
  264.         - "en"
  265.         - "en-US"
  266.         - "zh"
  267.         - "zh-TW"
  268.         - "zh-HK"
  269.         - "zh-CN"
  270.         - "fr"
  271.         - "fr-ca"
  272.         - "fr-fr"
  273.  
  274. Google:
  275.     url: "https://www.google.com/search?tbm=bks&q="
  276.     languages:
  277.         - "en"
  278.         - "en-US"
  279.         - "zh"
  280.         - "zh-TW"
  281.         - "zh-HK"
  282.         - "zh-CN"
  283.         - "fr"
  284.         - "fr-ca"
  285.         - "fr-fr"
  286.  
  287. 北市圖書館:
  288.     url: "https://book.tpml.edu.tw/search?searchField=FullText&searchInput="
  289.     languages:
  290.         - "en"
  291.         - "en-US"
  292.         - "zh"
  293.         - "zh-TW"
  294.         - "zh-HK"
  295.         - "zh-CN"
  296.  
  297. 北市圖書館Hyread:
  298.     url: "https://tpml.ebook.hyread.com.tw/searchList.jsp?search_field=FullText&search_input="
  299.     languages:
  300.         - "zh"
  301.         - "zh-TW"
  302.         - "zh-HK"
  303.  
  304. 北市圖書館UDN讀書館:
  305.     url: "https://reading.udn.com/udnlib/tpml/booksearch?sort=all&opt=all&kw="
  306.     languages:
  307.         - "zh"
  308.         - "zh-TW"
  309.         - "zh-HK"
  310.  
  311. 北區資源中心UDN讀書館:
  312.     url: "https://reading.udn.com/udnlib/publiclib/booksearch?sort=all&opt=all&kw="
  313.     languages:
  314.         - "zh"
  315.         - "zh-TW"
  316.         - "zh-HK"
  317.  
  318. 台灣雲端書庫:
  319.     url: "https://www.ebookservice.tw/#search/"
  320.     languages:
  321.         - "zh"
  322.         - "zh-TW"
  323.         - "zh-HK"
  324.  
  325. 新北市圖書館:
  326.     url: "https://webpac.tphcc.gov.tw/webpac/search.cfm?m=ss&t0=k&c0=and&k0="
  327.     languages:
  328.         - "en"
  329.         - "en-US"
  330.         - "zh"
  331.         - "zh-TW"
  332.         - "zh-HK"
  333.         - "zh-CN"
  334.  
  335. 新北市圖書館Hyread:
  336.     url: "https://tphcc.ebook.hyread.com.tw/searchList.jsp?search_field=FullText&search_input="
  337.     languages:
  338.         - "zh"
  339.         - "zh-TW"
  340.         - "zh-HK"
  341.  
  342. 新北市圖書館UDN讀書館:
  343.     url: "https://reading.udn.com/udnlib/tpc/booksearch?sort=all&opt=all&kw="
  344.     languages:
  345.         - "zh"
  346.         - "zh-TW"
  347.         - "zh-HK"
  348.  
  349. 國立臺灣圖書館Hyread:
  350.     url: "https://ntledu.ebook.hyread.com.tw/searchList.jsp?search_field=FullText&search_input="
  351.     languages:
  352.         - "zh"
  353.         - "zh-TW"
  354.         - "zh-HK"
  355.  
  356. 國立臺灣圖書館UDN讀書館:
  357.     url: "https://reading.udn.com/udnlib/ncl/booksearch?sort=all&opt=all&kw="
  358.     languages:
  359.         - "zh"
  360.         - "zh-TW"
  361.         - "zh-HK"
  362.  
  363. 讀冊:
  364.     url: "https://www.taaze.tw/rwd_searchResult.html?keyword%5B%5D="
  365.     languages:
  366.         - "zh"
  367.         - "zh-TW"
  368.         - "zh-HK"
  369.         - "zh-CN"
  370.  
  371. Readmoo:
  372.     url: "https://share.readmoo.com/search/keyword?q="
  373.     languages:
  374.         - "zh"
  375.         - "zh-TW"
  376.         - "zh-HK"
  377.         - "zh-CN"
  378.  
  379. Amazon:
  380.     url: "https://www.amazon.com/s?i=digital-text&k="
  381.     languages:
  382.         - "en"
  383.         - "en-US"
  384.         - "zh"
  385.         - "zh-TW"
  386.         - "zh-HK"
  387.         - "zh-CN"
  388.         - "fr"
  389.         - "fr-ca"
  390.         - "fr-fr"
  391.  
  392. Babelio:
  393.     url: "https://www.babelio.com/resrecherche.php?Recherche="
  394.     languages:
  395.         - "fr"
  396.         - "fr-ca"
  397.         - "fr-fr"
  398.  
  399. Bobigny:
  400.     url: "https://mediatheques.bobigny.fr/index.php/recherche-simple/simple/Mots%2BNotice/-1/"
  401.     languages:
  402.         - "fr"
  403.         - "fr-ca"
  404.         - "fr-fr"
  405. `;
  406.  
  407. var keywords = ['title', 'authors', 'origtitle', 'isbn', 'asin'];
  408.  
  409.  
  410.     parse_metadata();
  411.  
  412.     function parse_metadata() {
  413.         var rules = jsyaml.load(metadata_yaml);
  414.         var data = {};
  415.  
  416.         // parse the ld+json
  417.         var jsons = evaluate('//script[@type="application/ld+json"]');
  418.         if(jsons.length > 0) {
  419.             jsons.forEach(function(json) {
  420.                 try {
  421.                     var ld = JSON.parse(json);
  422.                     // console.debug(ld);
  423.                     if(ld['@type'] == "Book") {
  424.                         data['title'] = [ld['name']];
  425.  
  426.                         if(ld['isbn'] != undefined) {
  427.                             data['isbn']= [ld['isbn']];
  428.                         }
  429.  
  430.                         if(ld['workExample'] != undefined && ld['workExample']['isbn'] != undefined ) {
  431.                         data['isbn']= [ld['workExample']['isbn']];
  432.                         }
  433.  
  434.                         data['authors'] = [];
  435.                         if(ld['author'] != undefined) {
  436.                             if(Array.isArray(ld['author'])) {
  437.                                 ld['author'].forEach(function (author) {
  438.                                     data['authors'].push(author['name']);
  439.                                 })
  440.                             } else {
  441.                                 data['authors'].push(ld['author']['name']);
  442.                             }
  443.                         }
  444.                     }
  445.                 } catch(err) {
  446.                     console.error(err, json)
  447.                 }
  448.             })
  449.         }
  450.  
  451.         for (var domain in rules) {
  452.             rules[domain]['matches'].forEach(function (match) {
  453.                 if(document.URL.match(match)) {
  454.                     var metadata = rules[domain]['metadata'];
  455.  
  456.                     // wait for page is loaded
  457.                     let delaytime = 0;
  458.                     if('delaytime' in rules[domain]) {
  459.                         delaytime = rules[domain]['delaytime'];
  460.                     }
  461.                     let fixtitle = 0;
  462.                     if('fixtitle' in rules[domain]) {
  463.                         fixtitle = true;
  464.                     }
  465.  
  466.                     // wait for page content to be loaded
  467.                     setTimeout(function() {
  468.                         // collect metadata
  469.                         for (var key in metadata) {
  470.                             data[key] = evaluate(metadata[key]);
  471.                         }
  472.  
  473.                         // updates links to other websites
  474.                         if(Object.keys(data).length > 0) {
  475.                             console.debug(data);
  476.                             var dialog = inject();
  477.                             var urlsforsearch = jsyaml.load(search_yaml);
  478.  
  479.                             for (var service in urlsforsearch) {
  480.                                 if(!isPreferLang(urlsforsearch[service]['languages'])) {
  481.                                     continue;
  482.                                 }
  483.  
  484.                                 var url = urlsforsearch[service]['url'];
  485.                                 var html = `<div>${service}: `;
  486.                                 keywords.forEach(function(key) {
  487.                                     if(data[key] != undefined) {
  488.                                         data[key].forEach(function(val) {
  489.                                             var href = url + encodeURI(val);
  490.                                             html += `<a href="${href}" target="_blank">${val}</a> `;
  491.                                         })
  492.                                     }
  493.                                 })
  494.                                 html += "</div>";
  495.                                 dialog.insertAdjacentHTML('beforeend', html)
  496.                             }
  497.                         }
  498.  
  499.                         // fix title for soem sites.
  500.                         if(fixtitle) {
  501.                             document.title = data["title"];
  502.                         }
  503.                     }, delaytime);
  504.                 }
  505.             });
  506.         }
  507.     }
  508.  
  509.     function isPreferLang(offers) {
  510.         var languages = window.navigator.userLanguage || window.navigator.languages || [window.navigator.language];
  511.         var ret = false;
  512.         languages.forEach(function(lang) {
  513.             if(offers.includes(lang)) {
  514.                 ret = true;
  515.             }
  516.         })
  517.         return ret;
  518.     }
  519.  
  520.     function inject () {
  521.         var div = document.createElement('div');
  522.         div.id = "libraryhelper";
  523.         div.className = "libraryhelper";
  524.         div.textContent = 'Library helper';
  525.         // Make the DIV element draggable:
  526.         dragElement(div);
  527.         document.body.appendChild(div);
  528.  
  529.         var style = document.createElement('style');
  530.         style.innerHTML = `
  531.   div.libraryhelper {
  532.     color: blueviolet;
  533.     border: 1px solid #d3d3d3;
  534.     background-color: rgba(255, 255, 255, 0.6);
  535.  
  536.     position: fixed;
  537.     top: 150px;
  538.     right: 0px;
  539.  
  540.     width: 30vw;
  541.     max-height: 50vh;
  542.     padding: 10px;
  543.  
  544.     overflow-x: scroll;
  545.     cursor: move;
  546.     z-index: 9999;
  547.   }
  548. `;
  549.         document.head.appendChild(style);
  550.         return div;
  551.     }
  552.  
  553.  
  554.     function dragElement(elmnt) {
  555.         var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  556.         if (document.getElementById(elmnt.id + "header")) {
  557.             // if present, the header is where you move the DIV from:
  558.             document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
  559.         } else {
  560.             // otherwise, move the DIV from anywhere inside the DIV:
  561.             elmnt.onmousedown = dragMouseDown;
  562.         }
  563.  
  564.         function dragMouseDown(e) {
  565.             e = e || window.event;
  566.             e.preventDefault();
  567.             // get the mouse cursor position at startup:
  568.             pos3 = e.clientX;
  569.             pos4 = e.clientY;
  570.             document.onmouseup = closeDragElement;
  571.             // call a function whenever the cursor moves:
  572.             document.onmousemove = elementDrag;
  573.         }
  574.  
  575.         function elementDrag(e) {
  576.             e = e || window.event;
  577.             e.preventDefault();
  578.             // calculate the new cursor position:
  579.             pos1 = pos3 - e.clientX;
  580.             pos2 = pos4 - e.clientY;
  581.             pos3 = e.clientX;
  582.             pos4 = e.clientY;
  583.             // set the element's new position:
  584.             elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
  585.             elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  586.         }
  587.  
  588.         function closeDragElement() {
  589.             // stop moving when mouse button is released:
  590.             document.onmouseup = null;
  591.             document.onmousemove = null;
  592.         }
  593.     }
  594.  
  595.     function evaluate(xpath, doc = document.documentElement) {
  596.         var evaluator = new XPathEvaluator();
  597.         var result = evaluator.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
  598.         var node = undefined;
  599.         var texts = [];
  600.  
  601.         while(node = result.iterateNext()) {
  602.             var text;
  603.  
  604.             if (node instanceof Attr) {
  605.                 text = node.value
  606.             } else {
  607.                 text = node.innerText;
  608.             }
  609.             if(text == undefined || text == 'null') {
  610.                 console.error(xpath + " not found on " + document.URL);
  611.                 continue;
  612.             }
  613.             // fixing up content
  614.             text = text.replace("ISBN:", "").
  615.                     replace("作者 :", "").
  616.                     replace("出版日期:", "").
  617.                     replace(" (電子書)", "");
  618.             const subtitles = text.split(':');
  619.             if(subtitles.length > 1) {
  620.                 texts.push(subtitles[0]);
  621.             }
  622.             texts.push(text);
  623.         }
  624.         return texts;
  625.     }
  626. })();
  627.  
Advertisement
Comments
  • User was banned
Add Comment
Please, Sign In to add comment