Last active
February 27, 2022 07:18
-
-
Save Klaster1/b26153ad86d3e62ec6069c9bc06ddc0e to your computer and use it in GitHub Desktop.
Discogs paste release
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Bandcamp (paste release data) | |
// @version 1.2 | |
// @author Klaster_1 | |
// @match https://www.discogs.com/release/edit/* | |
// @match https://www.discogs.com/release/add | |
// @grant unsafeWindow | |
// @icon https://www.discogs.com/favicon.ico | |
// @downloadURL https://gist.github.com/Klaster1/b26153ad86d3e62ec6069c9bc06ddc0e/raw/discogs-paste-release.user.js | |
// @updateURL https://gist.github.com/Klaster1/b26153ad86d3e62ec6069c9bc06ddc0e/raw/discogs-paste-release.user.js | |
// ==/UserScript== | |
// Known issues: the "Paste release" button appears only after reloading the release edit page. Save the draft and hit F5. | |
const script = () => { | |
let win = window | |
const prompForData = () => JSON.parse(prompt()) | |
const getAddTrackButton = () => Array.from(document.querySelectorAll('button')).filter(b=>b.textContent === 'Add Tracks').pop() | |
const getTrackRowByIndex = (index) => win.document.querySelectorAll(`table.subform_tracklist tbody`)[index] | |
const getPositionInput = (row) => row.querySelector('[data-ref-overview="track_pos"] input') | |
const getTitleInput = (row) => row.querySelector('[data-ref-overview="track_title"] input') | |
const getDurationInput = (row) => row.querySelector('[data-ref-overview="track_duration"] input') | |
const getAddArtistButton = (row) => row.querySelector('[data-ref-overview="track_artist"] button') | |
const getAddArtistInputByIndex = (row, index) => row.querySelectorAll('[data-ref-overview="track_artist"] input[placeholder="Name"]')[index] | |
const wait = (duration) => new Promise(res => setTimeout(() => res(), duration)) | |
const findInstance = (el, fn) => { | |
let owner = el?._reactInternalComponent?._currentElement?._owner | |
while (owner) { | |
if (fn(owner._instance)) return owner._instance | |
owner = owner._currentElement._owner | |
} | |
} | |
const setValue = async (input, value) => { | |
if (value === undefined || input === undefined) { | |
console.warn('no value or input', input, value) | |
return Promise.resolve(true) | |
} | |
const instance = findInstance(input, instance => { | |
return instance.state | |
}) | |
if (!instance) return Promise.resolve(true) | |
input.focus() | |
await wait(100) | |
const nativeInputValueSetter = Object.getOwnPropertyDescriptor( | |
win.HTMLInputElement.prototype, | |
"value" | |
).set | |
const event = new Event('change', { | |
bubbles: true, | |
cancelable: true | |
}); | |
nativeInputValueSetter.call(input, value.toString()) | |
input.dispatchEvent(event) | |
instance?.handleChange(event) | |
await wait(100) | |
try { | |
if (instance?.state && 'value' in instance.state) instance.setState(state => ({value: value.toString()})) | |
if (instance?.state && 'data' in instance.state) instance.setState(state => ({ | |
data: state.data.setIn(['value'], value).setIn(['changed'], true) | |
})) | |
} catch (e) { | |
win.console.log(e) | |
} | |
input.blur() | |
await wait(100) | |
} | |
const fillInData = async (data) => { | |
for (const [index, track] of data.entries()) { | |
let row = getTrackRowByIndex(index) | |
if (!row) { | |
getAddTrackButton().click() | |
await wait(100) | |
} | |
row = getTrackRowByIndex(index) | |
if (!row) { | |
throw new Error(`Row for index not found: ${index}`) | |
} | |
for (const [index, artist] of track.artists.entries()) { | |
if (!getAddArtistInputByIndex(row, index)) getAddArtistButton(row).click() | |
await wait(300) | |
await setValue(getAddArtistInputByIndex(row, index), artist) | |
} | |
await setValue(getPositionInput(row), track.number) | |
await setValue(getTitleInput(row), track.title) | |
await setValue(getDurationInput(row), track.time) | |
} | |
} | |
const addButton = () => { | |
const button = document.createElement('button') | |
Object.assign(button.style, { | |
position: 'fixed', | |
bottom: '20px', | |
right: '20px' | |
}) | |
button.classList.add('button', 'button-small') | |
button.textContent = 'Paste Bandcamp release' | |
button.onclick = () => fillInData(prompForData()) | |
document.body.appendChild(button) | |
} | |
addButton() | |
} | |
const el = document.createElement('script') | |
el.type = 'text/javascript'; | |
el.innerHTML = `(${script.toString()})()` | |
setTimeout(() => document.body.appendChild(el)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment