Skip to content

Instantly share code, notes, and snippets.

@ryanml
Created August 2, 2020 21:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryanml/092a841c76b9201859698861d467eab1 to your computer and use it in GitHub Desktop.
Save ryanml/092a841c76b9201859698861d467eab1 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Ebay NGC/PCGS Lookup
// @namespace https://ebay.com
// @version 0.1
// @description Extracts Coin cert information from item photos
// @author ryanml
// @match https://www.ebay.com/itm/*
// @grant none
// @require https://unpkg.com/tesseract.js@v2.1.0/dist/tesseract.min.js
// ==/UserScript==
(function() {
class CoinLookup {
constructor () {
this.mainImg = null
this.statusNode = null
this.currentGrader = null
this.patterns = {
'pcgs': [
/\/[0-9]{7,8}/,
/[0-9]{4,5}.[0-9]{2}.[0-9]{7,8}/
]
}
this.schemes = {
'pcgs': 'https://www.pcgs.com/cert/'
}
}
init () {
if (!this._isCoinItem()) return
this.mainImg = document.querySelector('#icImg')
if (!this.mainImg) {
console.error('Could not initialize CoinLookup')
return
}
this._buildUI()
}
_isCoinItem () {
const breadCrumbs = document.querySelector('.vi-bc-topM')
if (!breadCrumbs) return false
return breadCrumbs.textContent.indexOf('Coins') > -1
}
_buildUI () {
const infoArea = document.querySelector('.u-cb')
infoArea.style.padding = '30px 62px'
this.fetchButton = document.createElement('button')
this.fetchButton.className = 'btn btn-ter'
this.fetchButton.textContent = 'Lookup coin info'
this.fetchButton.addEventListener('click', (event) => {
this.mainImg.src && this._processImage(this.mainImg.src)
})
this.statusNode = document.createElement('span')
this.statusNode.style.marginLeft = '5px'
infoArea.appendChild(this.fetchButton)
infoArea.appendChild(this.statusNode)
}
_processImage (src) {
const largeSrc = this._getLargeSrc(src)
Tesseract.recognize(
largeSrc,
'eng',
{ logger: this._logStatus.bind(this) }
).then(this._processResult.bind(this))
}
_getLargeSrc (src) {
const split = src.split('/')
split.pop()
return `${split.join('/')}/s-l1600.png`
}
_updateStatusText (text) {
this.statusNode.textContent = text
}
_logStatus (logEvent) {
const { progress, status } = logEvent
if (status !== 'recognizing text') {
this._updateStatusText('Preparing to crunch...')
return
}
const percentage = Math.ceil(progress * 100).toFixed(0)
this._updateStatusText(`Crunching image: ${percentage}%`)
}
_processResult ({ data: { text } }) {
console.log('Parsed image text', text)
let certCode = this._searchPatterns(text)
if (!certCode) {
this._updateStatusText('Could not extract cert info.')
return
}
this._updateStatusText('')
const extractedCode = this._extractCertPart(certCode)
console.log('Parsed cert #', extractedCode)
this._openCertView(extractedCode)
}
_searchPatterns (text) {
for (const grader in this.patterns) {
const patterns = this.patterns[grader]
for (const pattern in patterns) {
const result = text.match(patterns[pattern])
if (result) {
this.currentGrader = grader
return result[0]
}
}
}
return null
}
_extractCertPart (code) {
const dotPivot = code.indexOf('.')
if (dotPivot === -1) {
return code.replace('/', '')
}
const breakChar = [...code][dotPivot + 3]
return code.split(breakChar)[1]
}
_openCertView (cert) {
window.open(`${this.schemes[this.currentGrader]}${cert}`, '_blank')
}
}
new CoinLookup().init()
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment