DLSite Product Information Injector

Inject the information of a DLSite product to your restaurant.

  1. // ==UserScript==
  2. // @name DLSite Product Information Injector
  3. // @description Inject the information of a DLSite product to your restaurant.
  4. // @namespace dl_injecter
  5. // @version 2.0.0
  6. // @grant GM_xmlhttpRequest
  7. // @match https://arca.live/b/simya/*
  8. // @run-at document-end
  9. // @noframes
  10. // ==/UserScript==
  11. var fullRegexs = [/(RJ)[0-9]{6}\b|(RJ)[0-9]{8}\b/gi, /(VJ)[0-9]{6}\b|(VJ)[0-9]{8}\b/gi];
  12.  
  13. var partialRegexs = [/[0-9]{8}|[0-9]{6}/gi];
  14.  
  15. var languageRegexs = [/(거|퍼)[0-9]{6}\b|(거|퍼)[0-9]{8}\b/gi];
  16.  
  17. var defaultPrefix = 'RJ';
  18. var default2Prefix = 'VJ';
  19.  
  20. // Get matched products from text.
  21. function getMatchedProducts(text) {
  22. // Preprocess the inputted text.
  23. text = String(text);
  24. text = text.replace(/https?:\/\/(arca.live\/b\/simya\/)[0-9]*/g, ""); // Ignore arca live post url.
  25.  
  26. // Get matches from the text with regexs.
  27. var matches = [];
  28.  
  29. for (let i = 0; i < fullRegexs.length; i++) {
  30. let tempArr = [];
  31. tempArr = text.match(fullRegexs[i]);
  32.  
  33. if (tempArr == null) {
  34. continue;
  35. }
  36.  
  37. for (let j = 0; j < tempArr.length; j++) {
  38. if (tempArr[j] != null) {
  39. matches.push(tempArr[j].toUpperCase());
  40. }
  41. }
  42. }
  43.  
  44. for (let i = 0; i < languageRegexs.length; i++) {
  45. let tempArr = [];
  46. tempArr = text.match(languageRegexs[i]);
  47.  
  48. if (tempArr == null) {
  49. continue;
  50. }
  51.  
  52. for (let j = 0; j < tempArr.length; j++) {
  53. if (tempArr[j] != null) {
  54. // If the product code is already exist, ignore current index.
  55. var text_check = tempArr[j].substr(0, 1);
  56. tempArr[j] = tempArr[j].substr(1);
  57.  
  58. if ("거" == text_check) {
  59. matches.push(defaultPrefix.toUpperCase() + tempArr[j]);
  60. }
  61.  
  62. if ("퍼" == text_check) {
  63. matches.push(default2Prefix.toUpperCase() + tempArr[j]);
  64. }
  65. }
  66. }
  67. }
  68.  
  69. for (let i = 0; i < partialRegexs.length; i++) {
  70. let tempArr = [];
  71. tempArr = text.match(partialRegexs[i]);
  72.  
  73. if (tempArr == null) {
  74. continue;
  75. }
  76.  
  77. for (let j = 0; j < tempArr.length; j++) {
  78. if (tempArr[j] != null) {
  79. // If the product code is already exist, ignore current index.
  80. if (matches.includes("RJ" + tempArr[j]) || matches.includes("VJ" + tempArr[j])) {
  81. break;
  82. }
  83. if (matches.includes("거" + tempArr[j]) || matches.includes("퍼" + tempArr[j])) {
  84. break;
  85. }
  86.  
  87. matches.push(defaultPrefix.toUpperCase() + tempArr[j]);
  88. }
  89. }
  90. }
  91.  
  92. // Remove duplicated elements.
  93. var result = [];
  94.  
  95. matches.forEach((element) => {
  96. if (!result.includes(element)) {
  97. result.push(element);
  98. }
  99. });
  100.  
  101. return result;
  102. }
  103.  
  104. // Provide Fetch feature.
  105. function doFetch(url, options = {
  106. method: 'GET',
  107. responseType: 'document'
  108. }, silent = false) {
  109. return new Promise((resolve, reject) => {
  110. GM_xmlhttpRequest({
  111. url: url,
  112. method: options.method,
  113. responseType: options.responseType,
  114. headers: options.headers,
  115. data: options.data,
  116. onload: result => {
  117. console.debug(result)
  118. if (result.status == 200) {
  119. resolve(result.response);
  120. } else {
  121. if (!silent) {
  122. console.log(result)
  123. alert("알 수 없는 오류로 인해 데이터를 불러오지 못했습니다. " + url)
  124. reject(result.status);
  125. } else {
  126. console.debug(result)
  127. reject(result.status);
  128. }
  129. }
  130. }
  131. });
  132. });
  133. }
  134.  
  135. // Format number as bytes string.
  136. function formatBytes(bytes, decimals = 2) {
  137. if (bytes === 0) {
  138. return '-';
  139. }
  140.  
  141. const k = 1024;
  142. const dm = decimals < 0 ? 0 : decimals;
  143. const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  144. const i = Math.floor(Math.log(bytes) / Math.log(k));
  145.  
  146. return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
  147. }
  148.  
  149.  
  150. // Convert a array to a string.
  151. function arr2str(arr, opt = ", ") {
  152. let idx = arr.indexOf("")
  153.  
  154. while (idx > -1) {
  155. arr.splice(idx, 1)
  156. idx = arr.indexOf("")
  157. }
  158.  
  159. return String(arr).replace(/"/g, '').replace('[', "").replace(']', "").replace(/,/g, opt)
  160. }
  161.  
  162. // Get a product information from DLSite.
  163. function getProductInformation(productCode) {
  164. return new Promise((resolve, reject) => {
  165. const url = `https://www.dlsite.com/maniax/api/=/product.json?workno=${productCode}&locale=ko-KR`
  166. doFetch(url, {
  167. method: 'GET',
  168. responseType: 'application/json'
  169. })
  170. .then(result => {
  171. const json = JSON.parse(result)[0];
  172.  
  173. if (json) {
  174. const processedJson = Object()
  175.  
  176. processedJson.thumbnailImage = [`<img width='180' height='120' src='https:${json.image_main.url}' alt='이미지'>`]
  177.  
  178. processedJson.workType = [`${json.work_type} / ${json.work_type_string}`]
  179. processedJson.title = [`<a target='_blank' href='https://www.dlsite.com/home/work/=/product_id/${productCode}.html'>${json.work_name}</a>`]
  180. processedJson.intro = [json.intro_s]
  181. processedJson.maker = [json.maker_name]
  182.  
  183. if (json.genres) {
  184. processedJson.genres = []
  185.  
  186. json.genres.forEach(genre => {
  187. processedJson.genres.push(genre.name)
  188. })
  189.  
  190. processedJson.genres = [arr2str(processedJson.genres)]
  191. }
  192.  
  193. processedJson.fileInfo = [`${json.file_type}(${formatBytes(json.contents_file_size)})`]
  194.  
  195. processedJson.workUrl = [`<a target='_blank' href='https://www.dlsite.com/home/work/=/product_id/${productCode}.html'>${productCode}</a>`]
  196.  
  197. processedJson.circleUrl = [`<a target='_blank' href='https://www.dlsite.com/home/circle/profile/=/maker_id/${json.circle_id}.html'>${processedJson.maker}</a>`]
  198.  
  199. resolve(processedJson);
  200. } else {
  201. reject(new Error("Request is failed."));
  202. }
  203. })
  204. });
  205. }
  206.  
  207. function createProductElement(productCode) {
  208. return new Promise((resolve, reject) => {
  209. getProductInformation(productCode).then(function(data) {
  210. var html = '<table width="100%" border="0">' +
  211. '<tbody>' +
  212. '<tr><!-- 첫번째 줄 시작 -->' +
  213. `<td style="width: 1px; white-space: nowrap;" rowspan="2">${data.thumbnailImage}</td>` +
  214. '<td valign="center">' +
  215. '<div>' +
  216. `<div><span class="badge badge-success" style="display:inline-block; margin:0px 0px 0px 10px">${data.workUrl}</span><span class="badge badge-success" style="display:inline-block; margin:0px 4px 0px 4px">${data.workType}</span>${data.title}</div>` +
  217. `<div style="display:block; margin:0px 0px 0px 16px; font-size: 0.8375em; opacity: 0.8;"><li>설명 : ${data.intro}</li></div>` +
  218. `<div style="display:block; margin:0px 0px 0px 16px; font-size: 0.8375em; opacity: 0.8;"><li>서클 : ${data.circleUrl}</li></div>` +
  219. `<div style="display:block; margin:0px 0px 0px 16px; font-size: 0.8375em; opacity: 0.8;"><li>태그 : ${data.genres}</li></div>` +
  220. `<div style="display:block; margin:0px 0px 0px 16px; font-size: 0.8375em; opacity: 0.8;"><li>파일(용량) : ${data.fileInfo}</li></div>` +
  221. `<div></div>` +
  222. '</div>' +
  223. '</td>' +
  224. '</tr><!-- 첫번째 줄 끝 -->' +
  225. '</tbody>' +
  226. '</table>';
  227.  
  228. resolve(html);
  229. });
  230. });
  231. }
  232.  
  233. (function() {
  234. 'use strict';
  235.  
  236. // Get article wrapper element.
  237. let articleView = document.getElementsByClassName("article-view")[0];
  238. let articleWrapper = document.getElementsByClassName("article-wrapper")[0];
  239.  
  240. // Add the headline.
  241. let contentsElement = document.createElement('p');
  242. contentsElement.innerHTML = '<span class="ion-android-archive" style="display:inline-block; width:20px; margin:4px 0px 0px 12px;"></span>게시글에서 언급된 게임 목록';
  243.  
  244. articleView.insertBefore(contentsElement, articleWrapper);
  245.  
  246. // Get product codes in article.
  247. var products = [];
  248. products = getMatchedProducts(articleWrapper.innerText);
  249.  
  250. console.log(products);
  251.  
  252. if (products.length > 0) {
  253. // Insert elements into article-view element.
  254. for (var i = 0; i < products.length; i++) {
  255. createProductElement(products[i]).then(function(html) {
  256. let contentsElement = document.createElement('div');
  257. contentsElement.setAttribute("class", "alert alert-info");
  258. contentsElement.innerHTML = html;
  259.  
  260. articleView.insertBefore(contentsElement, articleWrapper);
  261. });
  262. }
  263. }
  264. })();