Make a tool for collecting skins and automating screenshots

This commit is contained in:
Jordan Eldredge 2019-02-09 22:17:30 -05:00
parent 48cac8d619
commit 100795bba1
17 changed files with 260 additions and 3030 deletions

View file

@ -1,36 +0,0 @@
# Automated Screenshots
## Setup
Clone the Webamp repo:
```
git clone git@github.com:captbaritone/webamp.git
```
Start Webamp in dev mode:
```
cd webamp
yarn install
npm start
```
Webamp should now be running on your local port 8080.
Now, in a separate terminal:
```
cd experiments/automated_screenshots
yarn install
```
Place skins in `experiments/automatedScreenshots/skins`. Subdirectories are fine, `.wsz` files will be searched for recursively.
```
npm start
```
Wait...
Find screenshots in `experiments/automatedScreenshots/screenshots`.

View file

@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -1,34 +0,0 @@
const fs = require("fs");
const path = require("path");
const Filehound = require("filehound");
const md5File = require("md5-file");
const Shooter = require("./shooter");
(async () => {
const shooter = new Shooter();
const passedSkin = process.argv[2];
let files = [];
if (passedSkin) {
files.push(passedSkin);
} else {
files = await Filehound.create()
.ext("wsz")
.paths("skins/")
.find();
}
for (const skin of files) {
console.log("Trying", skin);
const skinMd5 = md5File.sync(skin);
const screenshotPath = `screenshots/${skinMd5}.png`;
if (fs.existsSync(screenshotPath)) {
console.log(screenshotPath, "exists already");
continue;
}
await shooter.takeScreenshot(path.join(__dirname, skin), screenshotPath, {
minify: true
});
}
await shooter.dispose();
})();

View file

@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -1,447 +0,0 @@
{
"name": "automated_screenshots",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"agent-base": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.1.1.tgz",
"integrity": "sha512-yWGUUmCZD/33IRjG2It94PzixT8lX+47Uq8fjmd0cgQWITCMrJuXFaVIMnGDmDnZGGKAGdwTx8UGeU8lMR2urA==",
"requires": {
"es6-promisify": "5.0.0"
}
},
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"brace-expansion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
"requires": {
"balanced-match": "1.0.0",
"concat-map": "0.0.1"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"concat-stream": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
"integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
"requires": {
"inherits": "2.0.3",
"readable-stream": "2.3.3",
"typedarray": "0.0.6"
},
"dependencies": {
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"safe-buffer": "5.1.1",
"string_decoder": "1.0.3",
"util-deprecate": "1.0.2"
}
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"requires": {
"safe-buffer": "5.1.1"
}
}
}
},
"core-js": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz",
"integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU="
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"err-code": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
"integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA="
},
"es6-promise": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz",
"integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y="
},
"es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"requires": {
"es6-promise": "4.1.1"
},
"dependencies": {
"es6-promise": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz",
"integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng=="
}
}
},
"extend": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
},
"extract-zip": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.5.tgz",
"integrity": "sha1-maBnNbbqIOqbcF13ms/8yHz/BEA=",
"requires": {
"concat-stream": "1.6.0",
"debug": "2.2.0",
"mkdirp": "0.5.0",
"yauzl": "2.4.1"
},
"dependencies": {
"debug": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
"requires": {
"ms": "0.7.1"
}
},
"ms": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
"integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
}
}
},
"fd-slicer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
"integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
"requires": {
"pend": "1.2.0"
}
},
"file-js": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/file-js/-/file-js-0.3.0.tgz",
"integrity": "sha1-+rRr94I0bJKUSZ8fDSrQfYOPJdE=",
"requires": {
"bluebird": "3.5.1",
"minimatch": "3.0.4",
"proper-lockfile": "1.2.0"
}
},
"filehound": {
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/filehound/-/filehound-1.16.2.tgz",
"integrity": "sha1-5yrvpVh3I+LimQddFH6La1eMc9A=",
"requires": {
"bluebird": "3.5.1",
"file-js": "0.3.0",
"lodash": "4.17.4",
"minimatch": "3.0.4",
"moment": "2.18.1",
"unit-compare": "1.0.1"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
"https-proxy-agent": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.1.0.tgz",
"integrity": "sha512-/DTVSUCbRc6AiyOV4DBRvPDpKKCJh4qQJNaCgypX0T41quD9hp/PB5iUyx/60XobuMPQa9ce1jNV9UOUq6PnTg==",
"requires": {
"agent-base": "4.1.1",
"debug": "2.6.9"
}
},
"immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "1.4.0",
"wrappy": "1.0.2"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"jszip": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.4.tgz",
"integrity": "sha512-z6w8iYIxZ/fcgul0j/OerkYnkomH8BZigvzbxVmr2h5HkZUrPtk2kjYtLkqR9wwQxEP6ecKNoKLsbhd18jfnGA==",
"requires": {
"core-js": "2.3.0",
"es6-promise": "3.0.2",
"lie": "3.1.1",
"pako": "1.0.6",
"readable-stream": "2.0.6"
}
},
"lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
"requires": {
"immediate": "3.0.6"
}
},
"lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "1.1.8"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
"integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
"requires": {
"minimist": "0.0.8"
}
},
"moment": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz",
"integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1.0.2"
}
},
"pako": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
"integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg=="
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
},
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
},
"progress": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
"integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8="
},
"proper-lockfile": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-1.2.0.tgz",
"integrity": "sha1-zv9d2J0+XxD7deHo52vHWAGlnDQ=",
"requires": {
"err-code": "1.1.2",
"extend": "3.0.1",
"graceful-fs": "4.1.11",
"retry": "0.10.1"
}
},
"proxy-from-env": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
"integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4="
},
"puppeteer": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-0.10.2.tgz",
"integrity": "sha512-dj1MdwiYfEwTemfFeBu0inGyhcaEe3cKREvTFSA/pkGHaCwyj/FFfJHJpr9vykXvSs/PbmcApOztsUyqzd1msA==",
"requires": {
"debug": "2.6.9",
"extract-zip": "1.6.5",
"https-proxy-agent": "2.1.0",
"mime": "1.4.1",
"progress": "2.0.0",
"proxy-from-env": "1.0.0",
"rimraf": "2.6.2",
"ws": "3.2.0"
}
},
"readable-stream": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"string_decoder": "0.10.31",
"util-deprecate": "1.0.2"
}
},
"retry": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
"integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q="
},
"rimraf": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"requires": {
"glob": "7.1.2"
}
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
},
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"ultron": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz",
"integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ="
},
"unit-compare": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unit-compare/-/unit-compare-1.0.1.tgz",
"integrity": "sha1-DHRZ8OW/U2N+qHPKPO4Y3i7so4Y=",
"requires": {
"moment": "2.18.1"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.2.0.tgz",
"integrity": "sha512-hTS3mkXm/j85jTQOIcwVz3yK3up9xHgPtgEhDBOH3G18LDOZmSAG1omJeXejLKJakx+okv8vS1sopgs7rw0kVw==",
"requires": {
"async-limiter": "1.0.0",
"safe-buffer": "5.1.1",
"ultron": "1.1.0"
}
},
"yauzl": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
"integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
"requires": {
"fd-slicer": "1.0.1"
}
}
}
}

View file

@ -1,24 +0,0 @@
{
"name": "automated_screenshots",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"clean": "rm screenshots/*.png",
"sync-screenshots": "aws s3 sync --acl=public-read screenshots/ s3://webamp-uploaded-skins/screenshots/",
"sync-skins": "aws s3 sync --acl=public-read md5Skins/ s3://webamp-uploaded-skins/skins/",
"sync": "npm run sync-screenshots && npm run sync-skins"
},
"author": "",
"license": "ISC",
"dependencies": {
"filehound": "^1.16.2",
"imagemin": "^5.3.1",
"imagemin-optipng": "^5.2.1",
"imagemin-pngquant": "^5.1.0",
"jszip": "^3.1.4",
"md5-file": "^3.2.3",
"puppeteer": "^0.13.0"
}
}

View file

@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
assets/*

View file

@ -0,0 +1,43 @@
# Skin Archive Tools
A collection of scripts for managing the Internet Archive's collection of Winamp Skins.
It works as a data pipeline. The phases are:
## 1. Collect Skins
I collect skins into a single directory on my computer. There is no real organizational strategy within this directory. It's deeply nested and there are many duplicates, as it consists of multiple collections, small and large, from multiple sources.
We will also extrack skins for "packs" which are zip files containing multiple skins.
## 2. Skins Are Deduped
A script creates a single flat directory that contains each skin named only by its md5 hash.
## 3. Screenshots Are Taken
Yup.
## 4. Skins Are Synced to the Internet Archive
TODO
## Usage
```bash
yarn start
```
Note: This will probably break the first time it's run, since it requires a directory structure that is currently not checked in due to `.gitignore`.
Something like:
```
$ tree assets -d -L 1
assets
├── md5Invalids
├── md5Packs
├── md5Screenshots
├── md5Skins
└── skins
```

View file

@ -0,0 +1,82 @@
const path = require("path");
const fs = require("fs");
const fsPromises = require("fs").promises;
const childProcess = require("child_process");
const { memoize } = require("lodash");
const md5File = require("md5-file/promise");
const Filehound = require("filehound");
const Utils = require("./utils");
const memoizedMd5File = memoize(md5File);
module.exports = async function collectSkins({
inputDir,
outputDir,
filenamesPath
}) {
const files = await Filehound.create()
.ext(["zip", "wsz"])
.paths(inputDir)
.find();
const uniqueFiles = await Utils.unique(files, memoizedMd5File);
const collectionInfo = await Promise.all(
uniqueFiles.map(async filePath => {
const md5 = await memoizedMd5File(filePath);
const skinType = await Utils.skinType(filePath);
const md5Path = path.join(outputDir, `${md5}.wsz`);
const info = { filePath, md5, skinType, moved: false, md5Path };
if (info.skinType !== "CLASSIC") {
return info;
}
const exists = fs.existsSync(info.md5Path);
if (!exists) {
await fsPromises.link(filePath, info.md5Path);
info.moved = true;
}
return info;
})
);
const pathList = collectionInfo
.map(info => `${info.md5} ${path.relative(inputDir, info.filePath)}`)
.join("\n");
fsPromises.writeFile(filenamesPath, pathList);
collectionInfo
.filter(info => info.skinType === "PACK")
.forEach(pack => {
const packPath = path.resolve(`./assets/md5Packs/${pack.md5}`);
if (fs.existsSync(packPath)) {
return;
}
try {
childProcess.execSync(`unzip "${pack.filePath}" -d "${packPath}"`);
} catch (e) {
console.log(e);
}
});
collectionInfo
.filter(info => info.skinType === "INVALID")
.forEach(async invalid => {
const filePath = path.resolve(`./assets/md5Invalids/${invalid.md5}.wsz`);
if (fs.existsSync(filePath)) {
return;
}
await fsPromises.link(invalid.filePath, filePath, invalid.md5Path);
});
return {
total: files.length,
unique: uniqueFiles.length,
moved: collectionInfo.filter(info => info.moved).length,
newScreenshots: collectionInfo.filter(info => info.screenshotCreated)
.length,
classic: collectionInfo.filter(info => info.skinType === "CLASSIC").length,
packs: collectionInfo.filter(info => info.skinType === "PACK").length,
invalid: collectionInfo.filter(info => info.skinType === "INVALID").length,
modern: collectionInfo.filter(info => info.skinType === "MODERN").length,
// TODO: Tell modern skins from packs
nonClassic: collectionInfo.filter(info => !info.classic).length
};
};

View file

@ -0,0 +1,50 @@
const path = require("path");
const collectSkins = require("./collectSkins");
const takeScreenshots = require("./takeScreenshots");
/**
* Usage
*
* command <input_dir> <output_dir> <screenshot_dir>
*/
// eslint-disable-next-line no-unused-vars
const [
_node,
_thisFile,
rawInputDir,
rawOutputDir,
rawScreenshotDir,
rawFilenamesPath
] = process.argv;
const inputDir = path.resolve(rawInputDir);
const outputDir = path.resolve(rawOutputDir);
const screenshotDir = path.resolve(rawScreenshotDir);
const filenamesPath = path.resolve(rawFilenamesPath);
const collect = true;
const screenshots = true;
async function main() {
if (collect) {
const collectionInfo = await collectSkins({
filenamesPath,
inputDir,
outputDir
});
console.log(collectionInfo);
const packCollectionInfo = await collectSkins({
filenamesPath,
inputDir: path.resolve(path.join("assets", "md5Packs")),
outputDir
});
console.log(packCollectionInfo);
}
if (screenshots) {
await takeScreenshots(outputDir, screenshotDir);
}
}
// Blastoff!
main();

View file

@ -21,7 +21,7 @@ class Shooter {
this._page = await this._browser.newPage();
this._page.setViewport({ width: 275, height: 116 * 3 });
this._page.on("console", (...args) => {
console.log("PAGE LOG:", ...args);
// console.log("PAGE LOG:", ...args);
});
const url = `http://localhost:8080/?screenshot=1`;
await this._page.goto(url);
@ -33,18 +33,6 @@ class Shooter {
this._initialized = true;
}
_validateZip(u) {
return new Promise((resolve, reject) => {
try {
fs.readFile(u, (err, buffer) => {
JSZip.loadAsync(buffer).then(resolve, reject);
});
} catch (e) {
reject(e);
}
});
}
async _ensureInitialized() {
if (!this._initialized) {
await this.init();
@ -55,17 +43,15 @@ class Shooter {
await this._ensureInitialized();
console.log("Going to try", screenshotPath);
try {
await this._validateZip(skin);
} catch (e) {
console.log("Error parsing", skin, e);
return;
}
try {
console.log("geting input");
const handle = await this._page.$("#webamp-file-input");
console.log("uploading skin");
await handle.uploadFile(skin);
console.log("waiting for skin to load...");
await this._page.evaluate(() => {
return window.__webamp.skinIsLoaded();
});
console.log("waiting for screenshot");
await this._page.screenshot({
path: screenshotPath,
omitBackground: true, // Make screenshot transparent

View file

@ -0,0 +1,23 @@
const path = require("path");
const fs = require("fs");
const md5File = require("md5-file/promise");
const Filehound = require("filehound");
const Shooter = require("./shooter");
module.exports = async function takeScreenshots(skinDir, screenshotDir) {
const files = await Filehound.create()
.ext(["wsz"])
.paths(skinDir)
.find();
const shooter = new Shooter();
for (const filePath of files) {
const md5 = await md5File(filePath);
const screenshotPath = path.join(screenshotDir, `${md5}.png`);
if (fs.existsSync(screenshotPath)) {
continue;
}
await shooter.takeScreenshot(filePath, screenshotPath, {
minify: true
});
}
};

View file

@ -0,0 +1,35 @@
const JSZip = require("jszip");
const fsPromises = require("fs").promises;
// Reduce an array down to it's unique value given an async hasher function
async function unique(arr, hasher) {
const seen = new Set();
const items = [];
await Promise.all(
arr.map(async item => {
const key = await hasher(item);
if (seen.has(key)) {
return;
}
seen.add(key);
items.push(item);
})
);
return items;
}
async function skinType(skinPath) {
const buffer = await fsPromises.readFile(skinPath);
try {
const zip = await JSZip.loadAsync(buffer);
if (zip.file(/.*\.(wsz|wal|zip)$/i).length) {
// This is a collection of skins
return "PACK";
}
return zip.file(/^main\.bmp$/i).length === 1 ? "CLASSIC" : "MODERN";
} catch (e) {
return "INVALID";
}
}
module.exports = { unique, skinType };

View file

@ -0,0 +1,21 @@
{
"name": "skinarchivetools",
"version": "1.0.0",
"description": "A collection of scripts for managing the Internet Archive's collection of Winamp Skins.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node lib/index.js ./assets/skins ./assets/md5Skins ./assets/md5Screenshots ./assets/filenames.txt",
"clean": "cd md5Skins && find . -delete"
},
"author": "",
"license": "ISC",
"dependencies": {
"filehound": "^1.17.0",
"imagemin": "^6.1.0",
"imagemin-optipng": "^6.0.0",
"lodash": "^4.17.11",
"md5-file": "^4.0.0",
"puppeteer": "^1.12.2"
}
}