Skip to content

Instantly share code, notes, and snippets.

@hyper-active
Last active March 5, 2026 08:42
Show Gist options
  • Select an option

  • Save hyper-active/033487147c1a514d8d66314dc16e6d7e to your computer and use it in GitHub Desktop.

Select an option

Save hyper-active/033487147c1a514d8d66314dc16e6d7e to your computer and use it in GitHub Desktop.
Exports all asset names from your Unity Asset Store “My Assets” library so you can check which ones are being removed.
// ==UserScript==
// @name Unity My Assets Export (GraphQL) - TSV
// @namespace https://fieldof.vision
// @version 1.2
// @description Exports your Unity Asset Store library via GraphQL and outputs Name + Store URL (TSV). No scrolling.
// @match https://assetstore.unity.com/account/assets*
// @grant GM_setClipboard
// @run-at document-end
// ==/UserScript==
(() => {
'use strict';
const ENDPOINT = 'https://assetstore.unity.com/api/graphql/batch';
const PAGE_SIZE = 100;
const REQUEST_DELAY_MS = 250;
const INCLUDE_HIDDEN_BIN = true;
const STORE_URL = slug => `https://assetstore.unity.com/packages/${slug}`;
const QUERY = `
query SearchMyAssets(
$page: Int,
$pageSize: Int,
$tagging: [String!]
) {
searchMyAssets(
page: $page,
pageSize: $pageSize,
tagging: $tagging
) {
results {
product {
name
slug
}
}
total
}
}
`;
function sleep(ms){ return new Promise(r=>setTimeout(r,ms)); }
function getCookie(name){
const parts=document.cookie.split(';').map(s=>s.trim());
for(const p of parts){
const eq=p.indexOf('=');
if(eq===-1)continue;
const k=p.slice(0,eq).trim();
const v=p.slice(eq+1).trim();
if(k===name)return decodeURIComponent(v);
}
return null;
}
function dedupe(items,key){
const seen=new Set();
const out=[];
for(const i of items){
const k=key(i);
if(!k||seen.has(k))continue;
seen.add(k);
out.push(i);
}
return out;
}
async function gql(page,tagging){
const csrf=getCookie('_csrf');
const body=JSON.stringify([{
query:QUERY,
variables:{
page,
pageSize:PAGE_SIZE,
tagging:tagging||[]
},
operationName:'SearchMyAssets'
}]);
const res=await fetch(ENDPOINT,{
method:'POST',
credentials:'include',
headers:{
'Content-Type':'application/json;charset=UTF-8',
'x-csrf-token':csrf,
'x-requested-with':'XMLHttpRequest',
'x-source':'storefront',
'operations':'SearchMyAssets'
},
body
});
const json=await res.json();
return json?.[0]?.data?.searchMyAssets||null;
}
async function fetchAll(label,tagging){
setStatus(`Fetching ${label}…`);
const first=await gql(0,tagging);
const total=Number(first.total||0);
const pages=Math.max(1,Math.ceil(total/PAGE_SIZE));
const results=[...first.results];
for(let p=1;p<pages;p++){
setStatus(`Fetching ${label} ${p+1}/${pages}`);
await sleep(REQUEST_DELAY_MS);
const next=await gql(p,tagging);
results.push(...next.results);
}
return {total,results};
}
let rows=[];
let ui,badge,statusEl;
let btnFetch,btnCopy,btnDownload;
function setStatus(msg,cls){
statusEl.textContent=msg||'';
statusEl.className='tm-status '+(cls||'');
}
function setCount(n){
badge.textContent=String(n);
}
function flash(btn,text){
const old=btn.textContent;
btn.textContent=text;
setTimeout(()=>btn.textContent=old,700);
}
function button(bg='rgba(255,255,255,0.12)'){
return `
all:unset;
cursor:pointer;
padding:6px 10px;
border-radius:8px;
background:${bg};
border:1px solid rgba(255,255,255,0.15);
`;
}
function download(name,text){
const blob=new Blob([text],{type:'text/tab-separated-values'});
const a=document.createElement('a');
a.href=URL.createObjectURL(blob);
a.download=name;
a.click();
}
function makeUI(){
ui=document.createElement('div');
ui.style.cssText=`
position:fixed;
right:12px;
bottom:12px;
z-index:2147483647;
font:12px sans-serif;
background:rgba(20,20,20,.92);
color:#fff;
border-radius:10px;
padding:10px;
min-width:260px;
`;
const title=document.createElement('div');
title.style.cssText=`font-weight:700;margin-bottom:8px;display:flex;gap:6px;`;
title.textContent='Unity My Assets Export';
badge=document.createElement('span');
badge.textContent='0';
badge.style.cssText=`background:rgba(255,255,255,.2);padding:2px 6px;border-radius:999px;`;
title.appendChild(badge);
const row=document.createElement('div');
row.style.cssText=`display:flex;gap:6px;flex-wrap:wrap;`;
btnFetch=document.createElement('button');
btnFetch.textContent='Fetch';
btnFetch.style.cssText=button('rgba(120,180,255,.18)');
btnFetch.onclick=runFetch;
btnCopy=document.createElement('button');
btnCopy.textContent='Copy TSV';
btnCopy.style.cssText=button();
btnCopy.disabled=true;
btnCopy.onclick=async()=>{
const text=rows.join('\n');
try{
if(typeof GM_setClipboard==='function')GM_setClipboard(text);
else await navigator.clipboard.writeText(text);
flash(btnCopy,'Copied');
}catch{
flash(btnCopy,'Failed');
}
};
btnDownload=document.createElement('button');
btnDownload.textContent='Download TSV';
btnDownload.style.cssText=button();
btnDownload.disabled=true;
btnDownload.onclick=()=>{
download('myassets_links.tsv',rows.join('\n'));
flash(btnDownload,'Saved');
};
row.append(btnFetch,btnCopy,btnDownload);
statusEl=document.createElement('div');
statusEl.style.cssText=`margin-top:6px;opacity:.8`;
ui.append(title,row,statusEl);
document.documentElement.appendChild(ui);
}
async function runFetch(){
try{
btnFetch.disabled=true;
setStatus('Starting…');
const visible=await fetchAll('visible',[]);
let hidden={results:[],total:0};
if(INCLUDE_HIDDEN_BIN){
hidden=await fetchAll('hidden (#BIN)',['#BIN']);
}
const combined=[...visible.results,...hidden.results];
const products=combined
.map(r=>r?.product)
.filter(p=>p?.name&&p?.slug);
const unique=dedupe(products,p=>p.slug);
rows=unique.map(p=>`${p.name}\t${STORE_URL(p.slug)}`);
setCount(unique.length);
btnCopy.disabled=!rows.length;
btnDownload.disabled=!rows.length;
setStatus(`Done. ${rows.length} assets`, 'good');
}catch(e){
console.error(e);
setStatus(`Error: ${e.message}`,'bad');
}finally{
btnFetch.disabled=false;
}
}
makeUI();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment