必应图片下载按钮

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

当前为 2021-05-11 提交的版本,查看 最新版本

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

QingJ © 2025

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