必应图片下载按钮

在必应首页添加一个图片下载按钮。

当前为 2020-07-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @namespace https://gf.qytechs.cn/en/users/131965-levinit
  3. // @author levinit
  4. // @name Bing Image Download Button
  5. // @name:zh-CN 必应图片下载按钮
  6. // @name:zh-TW 必應圖片下載按鈕
  7. // @name:ko Bing 이미지 다운로드 버튼
  8. // @name:fr Bouton de téléchargement d'image Bing
  9. // @name:ja Bing画像ダウンロードボタン
  10. // @description Add an image download button on Bing's home page.
  11. // @description:zh-CN 在必应首页添加一个图片下载按钮。
  12. // @description:zh-TW 在必應首頁添加一个圖片下載按鈕。
  13. // @description:ko 빙 홈페이지에 이미지 다운로드 버튼 추가
  14. // @description:fr Ajouter le bouton de téléchargement d'image à la page d'accueil Bing.
  15. // @description:ja Bingホームページに画像ダウンロードボタンを追加する。
  16. // @include *://cn.bing.com/
  17. // @include *://www.bing.com/
  18. // @include *://www.bing.com/?*
  19. // @include *://cn.bing.com/?*
  20. // @run-at document-start
  21. // @version 1.0.5
  22. // @grant none
  23. // ==/UserScript==
  24.  
  25. const bingDownloadBtnConfig = {
  26. //下载按钮样式
  27. btnStyles: {
  28. 'color': '',
  29. 'font-size': '1.5em',
  30. 'padding': '0.25em',
  31. 'border-radius': '0.25em',
  32. 'box-shadow': '0 0 3 px rgba(125, 125, 125, 0.25)',
  33. 'right': '20%',
  34. 'top': '12.5%',
  35. 'background': '#c3d1cf94',
  36. 'position': 'fixed'
  37. },
  38. //下载按钮上的文字
  39. btnText: function () {
  40. let text = 'Download Today Bing Picture' //lang en
  41. switch (navigator.language) {
  42. case 'zh':
  43. case 'zh-CN':
  44. case 'zh-SG':
  45. text = '下载今日必应图片'
  46. break;
  47. case 'zh-TW':
  48. case 'zh-HK':
  49. text = '下載今日必應圖片'
  50. break;
  51. case 'ko':
  52. case 'ko_KR':
  53. text = '오늘의 빙 이미지 다운로드'
  54. break;
  55. case 'ja':
  56. case 'ja_JP':
  57. text = '今日のBing画像をダウンロードする'
  58. break
  59. case 'fr':
  60. case 'fr_BE':
  61. case 'fr_CA':
  62. case 'fr_CH':
  63. case 'fr_FR':
  64. case 'fr_LU':
  65. text = 'Téléchargez les image de bing aujourd’hui'
  66. break
  67. default:
  68. break;
  69. }
  70. return text
  71. },
  72. //图片信息
  73. imgInfo: {
  74. url: '',
  75. name: "",
  76. 'desc-ele-id': 'sh_cp', //含有图片描述信息的元素的id
  77. 'name-rule': {
  78. 'baseName': true,
  79. 'imgNO': false,
  80. 'imgResolution': false,
  81. 'dateInfo': true,
  82. 'description': true, //图片描述信息
  83. 'copyright': false //图片版权信息
  84. },
  85. //图片分辨率 不设置则使用默认 默认分辨率一般和当前系统设置、显示器分辨率有关
  86. resolution: '' //1366x768 1280x720 1920x1080
  87. },
  88. //设置菜单
  89. menuInfo: {
  90. menuWrapStyles: {
  91. 'position': 'fixed',
  92. 'z-index': '9',
  93. 'right': '1%',
  94. 'top': '5%',
  95. 'font-size': '1.25em',
  96. 'display': 'none'
  97. },
  98. menuWrapId: 'bing-download-settings',
  99. resetBtnId: 'reset-menu-settings',
  100. closeBtnClass: 'close-settings-menu',
  101. saveBtnId: 'save-menu-settings'
  102. },
  103. //本项目信息
  104. about: {
  105. github: 'https://github.com/levinit/bing-image-download-button',
  106. greasyfork: 'https://gf.qytechs.cn/zh-TW/scripts/35070-bing-image-download-button'
  107. },
  108. //本地存储 菜单中设置的信息 使用的key
  109. localStoreKey: 'bingImgDownload'
  110. }
  111.  
  112. window.addEventListener(
  113. 'load',
  114. function () {
  115. getSavedSettings(bingDownloadBtnConfig) //从本地存储读取设置信息
  116.  
  117. /*获取图片地址
  118. 进入bing页面后 图片地址写在了一个id为'bgLink'的a元素的href属性中
  119. */
  120. const initImgUrl = document.getElementById('bgLink').href
  121.  
  122. //设置图片信息
  123. getImgInfo(bingDownloadBtnConfig.imgInfo, initImgUrl)
  124.  
  125. if (initImgUrl) {
  126. //添加下载按钮
  127. addBtn(bingDownloadBtnConfig)
  128. addMenu(bingDownloadBtnConfig)
  129. }
  130. }, {
  131. once: true
  132. })
  133.  
  134. let dateOffset = 0 //可以查看前7天图片 0-7
  135.  
  136. //从本地存储中取得设置的信息写入到bingDownloadBtn各个项中
  137. function getSavedSettings(info) {
  138. if (localStorage.getItem(info.localStoreKey)) {
  139.  
  140. //本地存储的设置信息
  141. const savedSettings = JSON.parse(localStorage.getItem(bingDownloadBtnConfig.localStoreKey))
  142. const setSettings = function (settingsObj, savedSettingsObj) {
  143. //遍历本地存储的设置信息,写入到bingDownloadBtn各个项中
  144. for (const item in savedSettingsObj) {
  145. if (settingsObj.hasOwnProperty(item)) {
  146. settingsObj[item] = savedSettingsObj[item]
  147. }
  148. }
  149. }
  150.  
  151. //写入图片设置信息 (命名规则和分辨率)
  152. setSettings(info.imgInfo, savedSettings.imgInfo)
  153.  
  154. //绑定点击上一个/下一个图片时更新日期信息的事件
  155. getDateOffset()
  156. }
  157. }
  158.  
  159. function getDateOffset() {
  160. document.getElementById("sh_lt").addEventListener('click', function (e) {
  161. e.preventDefault()
  162. dateOffset = dateOffset === -7 ? -7 : dateOffset - 1
  163. })
  164.  
  165. document.getElementById("sh_rt").addEventListener('click', function (e) {
  166. e.preventDefault()
  167. dateOffset = dateOffset === 0 ? 0 : dateOffset + 1
  168. })
  169. }
  170.  
  171. //-----获取图片信息(根据设置规则修改)
  172. function getImgInfo(imgInfo, url) {
  173. //如果没有传入url 则使用imgInfo的url
  174. url = url ? url : imgInfo.url
  175. // const re = /(?<=id=).+\.(jpg|png)/ //慎用:某些浏览器还不支持向前查找
  176. // bingDownloadBtn.imgInfo.name = /id=.+?\.(jpg|png)/.exec(url)[0].replace('id=', '')
  177. //图片地址 根据分辨率设置修改图片地址 分辨率如1920x1080 如果未设置分辨率将使用默认分辨率
  178. url = imgInfo['resolution'] ? url.replace(/\d{4}x\d{3,4}/, imgInfo['resolution']) : url
  179.  
  180. /*图片名字 根据图片地址生成图片原始名字
  181. 原始示例 AberystwythSeafront_ZH-CN9542789062_1920x1080.jpg
  182. 原始名字分成三部分 baseName imgNO resolution
  183. */
  184. //原始名字去掉前面的OHR.字样 使用_切分图片名
  185.  
  186. const nameInfo = /id=.+?\.(jpg|png)/.exec(url)[0].replace('id=', '').replace(/^OHR\./, '').split('_')
  187.  
  188. //图片格式
  189. const imgFormat = /(jpg|png)$/.exec(nameInfo)[0]
  190.  
  191. // 图片描述信息
  192. const imgDescription = document.getElementById(imgInfo['desc-ele-id']).title.split('©')
  193.  
  194. //日期信息
  195. const now = new Date()
  196.  
  197. const imgDate = new Date(now.getTime() + dateOffset * (24 * 60 * 60 * 1000))
  198.  
  199. //初始化所有项
  200. let [baseName, imgNO, resolution, description, copyright, dateInfo] = ['', '', '', '', '', '']
  201.  
  202. //根据名字生成规则修改图片名字
  203. for (const rule in imgInfo['name-rule']) {
  204. const ruleValue = imgInfo['name-rule'][rule]
  205. if (ruleValue === true) {
  206. switch (rule) {
  207. case 'baseName':
  208. baseName = `${nameInfo[0]}`
  209. break;
  210. case 'imgNO':
  211. imgNO = `_${nameInfo[1]}`
  212. break;
  213. case 'imgResolution':
  214. resolution = `_${nameInfo[2]}`.split('.')[0]
  215. break;
  216. case 'dateInfo':
  217. dateInfo = `_${imgDate.getFullYear()}-${imgDate.getMonth() + 1}-${imgDate.getDate()}`
  218. break;
  219. case 'description':
  220. description = `_${imgDescription[0].replace(/\($/, '')}`
  221. break;
  222. case 'copyright':
  223. copyright = `(©${imgDescription[1]}`
  224. break;
  225. default:
  226. break;
  227. }
  228. }
  229. }
  230.  
  231. //拼接图片名字 去掉前后可能出现的_
  232. let name = `${baseName}${imgNO}${resolution}${description}${copyright}${dateInfo}`.replace(/^_/, '').replace(/_$/, '')
  233.  
  234. //如果图片没有名字只有后缀 强行给图片加上名字
  235. if (name === `.${imgFormat}`) {
  236. name = `${nameInfo[0]}.${imgFormat}`
  237. } else {
  238. name = `${name}.${imgFormat}`
  239. }
  240.  
  241. bingDownloadBtnConfig.imgInfo.url = url
  242. bingDownloadBtnConfig.imgInfo.name = name
  243. }
  244.  
  245. //-------添加下载按钮
  246. function addBtn(info) {
  247. const btn = document.createElement('a')
  248. btn.appendChild(document.createTextNode(info.btnText()))
  249.  
  250.  
  251. btn.style.cssText = (function (styles) {
  252. let btnCssText = ''
  253. for (let style in styles) {
  254. btnCssText += `${style}: ${styles[style]}; `
  255. }
  256. return btnCssText
  257. })(info.btnStyles)
  258.  
  259. btn.href = info.imgInfo.url //图片下载地址
  260. btn.download = info.imgInfo.name //图片下载名字
  261. btn.title = `img name: ${info.imgInfo.name}
  262. 右键打开设置菜单 | Right Click this button to open settings menu`
  263. document.body.appendChild(btn)
  264.  
  265. //更新下载信息 点击了前一天或后一天按钮后 需要刷新img的下载地址
  266. btn.onmouseover = function () {
  267. /* 新图片地址将写在id为bgDiv的元素的行内style的background-image属性中
  268. background-image值示例:/th?id=OHR.GOTPath_ZH-CN1955635212_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp
  269. */
  270.  
  271. //图片新url
  272. let newUrl = document.getElementById('bgDiv').style.backgroundImage
  273. newUrl = newUrl.substring(5, newUrl.length - 2)
  274.  
  275. //点击了前一天后一天按钮后更新图片下载地址和名字
  276. //没有点击前一天或后一天按钮 background-image不存在 则newUrl内容是空的
  277. if (newUrl) {
  278. getImgInfo(info.imgInfo, newUrl)
  279. }
  280.  
  281. this.href = info.imgInfo.url
  282. this.download = info.imgInfo.name
  283. }
  284.  
  285. //右键打开设置菜单
  286. btn.oncontextmenu = function (e) {
  287. e.preventDefault()
  288. document.getElementById(info.menuInfo.menuWrapId).style.display = 'block'
  289. }
  290. }
  291.  
  292. //-----设置菜单
  293. function addMenu(info) {
  294. const menuInfo = info.menuInfo
  295.  
  296. //先前已经存储的图像分辨率设置信息
  297. const savedImgResolution = info.imgInfo['resolution']
  298. //先前已经存储的图像规则信息
  299. const savedImgNameRule = info.imgInfo['name-rule']
  300.  
  301. const menuContent = `
  302. <fieldset id="btn-settings">
  303. <legend>settings</legend>
  304. <div class="settings-content">
  305. <ul class="img-infos">
  306. <header>
  307. Image Info
  308. </header>
  309. <li>
  310. <header>
  311. Image Name contains:
  312. </header>
  313. <div>
  314. <label>Base-Name</label>
  315. <input class="img-info" type="checkbox" name="name-rule" checked data-img-name-rule="baseName" />
  316. </div>
  317. <div>
  318. <label>NO.</label>
  319. <input class="img-info" type="checkbox" name="name-rule" data-img-name-rule="imgNO"
  320. ${savedImgNameRule['imgNO'] ? 'checked' : ''} />
  321. </div>
  322. <div>
  323. <label>Resolution</label>
  324. <input class="img-info" type="checkbox" name="name-rule" data-img-name-rule="imgResolution"
  325. ${savedImgNameRule['imgResolution'] ? 'checked' : ''} />
  326. </div>
  327. <div>
  328. <label>Description</label>
  329. <input class="img-info" type="checkbox" name="name-rule" data-img-name-rule="description"
  330. ${savedImgNameRule['description'] ? 'checked' : ''} />
  331. </div>
  332. <div>
  333. <label>CopyRight</label>
  334. <input class="img-info" type="checkbox" name="name-rule" data-img-name-rule="copyright"
  335. ${savedImgNameRule['copyright'] ? 'checked' : ''} />
  336. </div>
  337. <div>
  338. <label>Date-Info</label>
  339. <input class="img-info" type="checkbox" name="name-rule" data-img-name-rule="dateInfo"
  340. ${savedImgNameRule['dateInfo'] ? 'checked' : ''} />
  341. </div>
  342. </li>
  343. <li>
  344. <header>
  345. Image Resolution
  346. </header>
  347. <div>
  348. <label>UHD</label>
  349. <input class="img-info" type="radio" name="resolution" data-img-resolution="UHD"
  350. ${savedImgResolution === 'UHD' ? 'checked' : ''} />
  351. </div>
  352. <div>
  353. <label>1920x1080</label>
  354. <input class="img-info" type="radio" name="resolution" data-img-resolution="1920x1080"
  355. ${savedImgResolution === '1920x1080' ? 'checked' : ''} />
  356. </div>
  357. <div>
  358. <label>1366x768</label>
  359. <input class="img-info" type="radio" name="resolution" data-img-resolution="1366x768"
  360. ${savedImgResolution === '1366x768' ? 'checked' : ''} />
  361. </div>
  362. <div>
  363. <label>1280x720</label>
  364. <input class="img-info" type="radio" name="resolution" data-img-resolution="1280x720"
  365. ${savedImgResolution === '1280x720' ? 'checked' : ''} />
  366. </div>
  367. <div>
  368. <label>Default</label>
  369. <input class="img-info" type="radio" name="resolution" data-img-resolution="" ${savedImgResolution === ''
  370. ? 'checked' : ''} />
  371. </div>
  372. </li>
  373. </ul>
  374. <div class="about">
  375. About:
  376. <a href="${info.about.github}">GitHub</a>
  377. <a href="${info.about.greasyfork}">GreasyFork</a>
  378. </div>
  379. </div>
  380. <footer>
  381. <button id="${menuInfo.resetBtnId}" class="reset-btn">reset</button>
  382. <button id="${menuInfo.saveBtnId}" class="${menuInfo.closeBtnClass}">save</button>
  383. <button class="${menuInfo.closeBtnClass}">cancel</button>
  384. </footer>
  385. </fieldset>
  386. <style>
  387. #btn-settings {
  388. width: 300px;
  389. border: 1px dashed gainsboro;
  390. border-radius: 8px;
  391. box-shadow: 0 0 10px gainsboro;
  392. background-color: aliceblue;
  393. }
  394.  
  395. #btn-settings legend {
  396. font-weight: bold;
  397. text-shadow: 0 0 2px gray;
  398. color: steelblue;
  399. }
  400.  
  401. #btn-settings ul {
  402. padding: 0;
  403. }
  404.  
  405. #btn-settings ul>header {
  406. width: 100%;
  407. border-bottom: 3px groove gainsboro;
  408. font-weight: bold;
  409. color: slategrey;
  410. text-shadow: 0 0 5px gainsboro;
  411. margin-bottom: 0.5em;
  412. }
  413.  
  414. #btn-settings li {
  415. list-style-type: none;
  416. border-bottom: 1px dashed gainsboro;
  417. padding-bottom: 0.5em;
  418. }
  419.  
  420. .img-infos li header {
  421. color: sienna;
  422. margin-bottom: 0.25em;
  423. }
  424.  
  425. .img-infos li label {
  426. width: 80%;
  427. display: inline-block;
  428. }
  429.  
  430. .img-infos .img-info {
  431. vertical-align:middle;
  432. }
  433.  
  434. #btn-settings .about {
  435. text-align: right;
  436. margin-bottom: 1em;
  437. }
  438.  
  439. #btn-settings .about a {
  440. margin-right: 1em;
  441. }
  442.  
  443. #btn-settings footer {
  444. text-align: right;
  445. }
  446.  
  447. #btn-settings footer button {
  448. width: 88px;
  449. cursor: pointer;
  450. font-size: 1.2em;
  451. font-weight: bold;
  452. line-height: 1.25;
  453. text-align: center;
  454. padding: 0;
  455. color: teal;
  456. }
  457.  
  458. #btn-settings footer .reset-btn {
  459. margin-right: 25px;
  460. color: tomato;
  461. }
  462. </style>
  463. `
  464. //添加菜单
  465. const menu = document.createElement('div')
  466. menu.innerHTML = menuContent
  467. menu.id = info.menuInfo.menuWrapId
  468. let cssText = ''
  469. //设置菜单样式
  470. for (const style in menuInfo.menuWrapStyles) {
  471. cssText += `${style}: ${menuInfo.menuWrapStyles[style]}; `
  472. }
  473. menu.style.cssText = cssText
  474. document.body.appendChild(menu)
  475.  
  476.  
  477. //菜单的事件绑定:保存 重置 和 取消
  478. menu.onclick = function (e) {
  479. if (e.target.classList.contains(menuInfo.closeBtnClass)) {
  480. //如果点击的保存或取消按钮 关闭设置菜单
  481. menu.style.display = 'none'
  482.  
  483. //如果点击的是保存按钮 存储设置的信息
  484. if (e.target.id === menuInfo.saveBtnId) {
  485. localStorage.setItem(info.localStoreKey, JSON.stringify(getUserSettings(info)))
  486. getSavedSettings(info)
  487. getImgInfo(info.imgInfo) //刷新图片相关信息
  488. }
  489. }
  490.  
  491. //如果点击的是重置按钮 清空设置
  492. if (e.target.id === menuInfo.resetBtnId) {
  493. localStorage.removeItem(info.localStoreKey)
  494. getSavedSettings(info)
  495. getImgInfo(info.imgInfo) //刷新图片相关信息
  496. }
  497.  
  498. }
  499. }
  500.  
  501. function getUserSettings() {
  502. //btn-styles
  503. const btnStyles = {}
  504.  
  505. for (const item of document.querySelectorAll('.btn-style')) {
  506. let value = item.value
  507. //未设置的属性 以及position设置中未选择的属性 忽略
  508. if (item.value === "" || item.previousElementSibling.type === 'radio' && item.previousElementSibling.checked === false) {
  509. continue
  510. }
  511. const property = item.getAttribute('data-property')
  512. btnStyles[property] = value
  513. }
  514.  
  515.  
  516. //img-info
  517. const imgInfo = {
  518. 'name-rule': {}
  519. }
  520.  
  521. for (const item of document.querySelectorAll('.img-info')) {
  522. switch (item.name) {
  523. //图片命名规则
  524. case 'name-rule':
  525. imgInfo['name-rule'][item.getAttribute('data-img-name-rule')] = item.checked
  526. break
  527. //分辨率
  528. case 'resolution':
  529. if(item.checked){
  530. imgInfo['resolution'] = item.getAttribute('data-img-resolution')
  531. }
  532. break
  533. default:
  534. break
  535. }
  536. }
  537. return { btnStyles, imgInfo }
  538. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址