mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-23 10:07:35 +00:00
Progress
Also, add Algolia logo
This commit is contained in:
parent
37d97bb2da
commit
fea404d9d6
10 changed files with 254 additions and 169 deletions
|
|
@ -21,8 +21,7 @@
|
|||
"redux-observable": "^1.0.0",
|
||||
"reselect": "^4.0.0",
|
||||
"rxjs": "^6.3.3",
|
||||
"shell-escape": "^0.2.0",
|
||||
"webamp": "^1.3.0-beta.2"
|
||||
"webamp": "^1.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const skins = require("../src/skins.json");
|
||||
const algoliasearch = require("algoliasearch");
|
||||
const { getSkinMetadata } = require("./utils");
|
||||
|
|
@ -5,6 +7,11 @@ const { getSkinMetadata } = require("./utils");
|
|||
const client = algoliasearch("HQ9I5Z6IM5", "f5357f4070cdb6ed652d9c3feeede89f");
|
||||
const index = client.initIndex("Skins");
|
||||
|
||||
const CACHE_PATH = "/Volumes/Mobile Backup/skins/cache/";
|
||||
const info = JSON.parse(
|
||||
fs.readFileSync(path.join(CACHE_PATH, "info.json"), "utf8")
|
||||
);
|
||||
|
||||
function tuncate(str, len) {
|
||||
const overflow = str.length - len;
|
||||
if (overflow < 0) {
|
||||
|
|
@ -18,38 +25,46 @@ function tuncate(str, len) {
|
|||
return `${start} ########### ${end}`;
|
||||
}
|
||||
|
||||
async function buildSkinIndex(hash) {
|
||||
const textMetadata = await getSkinMetadata(hash, "extracted-data");
|
||||
async function buildSkinIndex(skin) {
|
||||
const { md5, filePaths } = skin;
|
||||
if (!filePaths || filePaths.length === 0) {
|
||||
console.warn("no file name for ", md5);
|
||||
return;
|
||||
}
|
||||
const fileName = path.basename(filePaths[0]);
|
||||
let readmeText = null;
|
||||
if (skin.readmePath) {
|
||||
readmeText = tuncate(fs.readFileSync(skin.readmePath, "utf8"), 4800);
|
||||
}
|
||||
return {
|
||||
objectID: hash,
|
||||
md5: hash,
|
||||
fileName: skins[hash].fileName,
|
||||
emails: textMetadata.emails,
|
||||
readmeText: tuncate(textMetadata.raw, 4800)
|
||||
objectID: skin.md5,
|
||||
md5,
|
||||
fileName,
|
||||
emails: skin.emails || null,
|
||||
readmeText
|
||||
};
|
||||
}
|
||||
|
||||
const indexesPromise = Promise.all(
|
||||
Object.keys(skins).map(hash => {
|
||||
return buildSkinIndex(hash);
|
||||
})
|
||||
Object.values(info)
|
||||
.filter(skin => skin.type === "CLASSIC")
|
||||
.map(skin => {
|
||||
return buildSkinIndex(skin);
|
||||
})
|
||||
);
|
||||
|
||||
async function go() {
|
||||
console.log("Building index");
|
||||
const indexes = await indexesPromise;
|
||||
const large = indexes.filter(index => {
|
||||
return index.readmeText.length > 4790;
|
||||
});
|
||||
large.map(l => {
|
||||
return l.fileName;
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log("Writing index");
|
||||
const results = await new Promise((resolve, reject) => {
|
||||
index.saveObjects(indexes, function(err, content) {
|
||||
if (err != null) reject(err);
|
||||
resolve(content);
|
||||
});
|
||||
});
|
||||
console.log("done!", results);
|
||||
}
|
||||
|
||||
go(); // .then(content => console.log("Updated index for:", content.length));
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const Bluebird = require("bluebird");
|
||||
var { exec } = require("child_process");
|
||||
const Utils = require("./utils");
|
||||
|
||||
const reg = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/g;
|
||||
const extract = value => (value && value.match(reg)) || [];
|
||||
|
||||
async function extractTextData(path) {
|
||||
const ignoreFiles = [
|
||||
"genex.txt",
|
||||
"genexinfo.txt",
|
||||
"gen_gslyrics.txt",
|
||||
"region.txt",
|
||||
"pledit.txt",
|
||||
"viscolor.txt",
|
||||
"winampmb.txt",
|
||||
"gen_ex help.txt",
|
||||
"mbinner.txt"
|
||||
// Skinning Updates.txt ?
|
||||
];
|
||||
|
||||
const ignoreArgs = ignoreFiles
|
||||
.map(file => `-x "**/${file}" ${file}`)
|
||||
.join(" ");
|
||||
|
||||
// Change -p to -c to get context of which file is missing
|
||||
const debug = false;
|
||||
const listFlag = debug ? "-c" : "-p";
|
||||
|
||||
// TODO: Escape path
|
||||
const cmd = `unzip ${listFlag} -C "${path}" "file_id.diz" "*.txt" ${ignoreArgs}`;
|
||||
|
||||
const raw = await new Promise((resolve, reject) => {
|
||||
exec(cmd, function(error, stdout, stderr) {
|
||||
if (error != null) {
|
||||
// reject(error);
|
||||
// return;
|
||||
}
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
|
||||
return { raw, emails: extract(raw) };
|
||||
}
|
||||
|
||||
async function writeTextData(skinPath, md5) {
|
||||
try {
|
||||
const data = await extractTextData(skinPath);
|
||||
const skinMetadataPath = await Utils.writeSkinMetadata(
|
||||
md5,
|
||||
"extracted-data",
|
||||
data
|
||||
);
|
||||
console.log("Done", skinMetadataPath);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const dir = process.argv[2];
|
||||
console.log("Reading dir");
|
||||
const files = fs.readdirSync(dir);
|
||||
console.log(`Found ${files.length} files`);
|
||||
return Bluebird.map(
|
||||
files,
|
||||
file => {
|
||||
const filePath = path.join(dir, file);
|
||||
const md5 = path.basename(file, ".wsz");
|
||||
return writeTextData(filePath, md5);
|
||||
},
|
||||
{ concurrency: 10 }
|
||||
);
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -1,76 +1,29 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const exec = require("child_process").exec;
|
||||
const Bluebird = require("bluebird");
|
||||
const shellescape = require("shell-escape");
|
||||
|
||||
const getFileNames = async () => {
|
||||
const content = fs.readFileSync(
|
||||
"/Volumes/Mobile Backup/skins/pathnames.txt",
|
||||
"utf8"
|
||||
);
|
||||
const fileNames = {};
|
||||
content.split("\n").forEach(line => {
|
||||
if (!line.length) {
|
||||
return;
|
||||
}
|
||||
const [hash, pathName] = line.split(" ");
|
||||
fileNames[hash] = path.basename(pathName);
|
||||
});
|
||||
return fileNames;
|
||||
};
|
||||
|
||||
const getFavoriteCounts = () => {
|
||||
const content = fs.readFileSync(path.join(__dirname, "../likes.txt"), "utf8");
|
||||
const favorites = {};
|
||||
content.split("\n").forEach(line => {
|
||||
if (!line.length) {
|
||||
return;
|
||||
}
|
||||
const [hash, fileName] = line.split(" ");
|
||||
favorites[hash] = fileName;
|
||||
});
|
||||
return favorites;
|
||||
};
|
||||
const testFolder = path.resolve(
|
||||
__dirname,
|
||||
"/Volumes/Mobile Backup/skins/md5Screenshots"
|
||||
const info = JSON.parse(
|
||||
fs.readFileSync("/Volumes/Mobile Backup/skins/cache/info.json", "utf8")
|
||||
);
|
||||
const files = fs
|
||||
.readdirSync(testFolder)
|
||||
.filter(skinPath => skinPath.endsWith(".png"));
|
||||
|
||||
const genAverage = img => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const imgPath = shellescape([path.join(testFolder, img)]);
|
||||
const command = `convert ${imgPath} -scale 1x1\! -format '%[pixel:u]' info:-`;
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error !== null) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getFileData = async files => {
|
||||
const fileNames = await getFileNames();
|
||||
const favortes = getFavoriteCounts();
|
||||
console.log(favortes);
|
||||
const getFileData = async () => {
|
||||
const fileData = {};
|
||||
await Bluebird.map(
|
||||
files,
|
||||
async file => {
|
||||
const md5 = path.basename(file, ".png");
|
||||
const fileName = fileNames[md5];
|
||||
if (!fileName) {
|
||||
Object.values(info).filter(skin => skin.type === "CLASSIC"),
|
||||
async skin => {
|
||||
const md5 = skin.md5;
|
||||
const filePaths = skin.filePaths;
|
||||
if (!filePaths || filePaths.length === 0) {
|
||||
console.warn("no file name for ", md5);
|
||||
return;
|
||||
}
|
||||
if (skin.twitterLikes) {
|
||||
console.log({ skin });
|
||||
}
|
||||
const fileName = path.basename(filePaths[0]);
|
||||
fileData[md5] = {
|
||||
color: (await genAverage(file)).slice(1),
|
||||
favorites: favortes[md5],
|
||||
color: skin.averageColor,
|
||||
favorites: skin.twitterLikes,
|
||||
fileName
|
||||
};
|
||||
},
|
||||
|
|
@ -79,9 +32,8 @@ const getFileData = async files => {
|
|||
return fileData;
|
||||
};
|
||||
|
||||
console.log("Finding the average color of screenshots...");
|
||||
console.log("This might take a moment");
|
||||
getFileData(files).then(data => {
|
||||
console.log("Extracting skin data from cache");
|
||||
getFileData().then(data => {
|
||||
const json = JSON.stringify(data);
|
||||
fs.writeFileSync("src/skins.json", json, "utf8");
|
||||
});
|
||||
|
|
|
|||
98
src/FileExplorer.js
Normal file
98
src/FileExplorer.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import * as Actions from "./redux/actionCreators";
|
||||
|
||||
class Readme extends React.Component {
|
||||
_renderFocusedFile() {
|
||||
if (this.props.focusedFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { ext, fileName, content } = this.props.focusedFile;
|
||||
if (content == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ext) {
|
||||
case "txt":
|
||||
return (
|
||||
<div
|
||||
className={"readme"}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "300px"
|
||||
}}
|
||||
>
|
||||
<pre>{content}</pre>
|
||||
</div>
|
||||
);
|
||||
case "bmp":
|
||||
case "cur":
|
||||
const mimeType = `image/${ext}`;
|
||||
const url = URL.createObjectURL(
|
||||
new Blob([content], { type: mimeType })
|
||||
);
|
||||
return <img src={url} alt={fileName} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: "fixed",
|
||||
backgroundColor: "white",
|
||||
overflow: "scroll",
|
||||
...this.props.style
|
||||
}}
|
||||
>
|
||||
{this.props.focusedFile ? (
|
||||
<>
|
||||
<h2>{this.props.focusedFile.fileName}</h2>
|
||||
{/*
|
||||
<select onChange={e => this.props.selectSkinFile(e.target.value)}>
|
||||
{this.props.zip &&
|
||||
Object.keys(this.props.zip.files).map(fileName => (
|
||||
<option
|
||||
key={fileName}
|
||||
value={fileName}
|
||||
selected={
|
||||
this.props.focusedFile &&
|
||||
this.props.focusedFile.fileName === fileName
|
||||
}
|
||||
>
|
||||
{fileName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
*/}
|
||||
<div>{this._renderFocusedFile()}</div>
|
||||
</>
|
||||
) : (
|
||||
<h2>No File Selected</h2>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
zip: state.skinZip,
|
||||
focusedFile: state.focusedSkinFile
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
selectSkinFile(fileName) {
|
||||
dispatch(Actions.selectSkinFile(fileName));
|
||||
}
|
||||
};
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Readme);
|
||||
|
|
@ -4,6 +4,7 @@ import * as Utils from "./utils";
|
|||
import * as Selectors from "./redux/selectors";
|
||||
import * as Actions from "./redux/actionCreators";
|
||||
import Disposable from "./Disposable";
|
||||
import { ReactComponent as AlgoliaLogo } from "./searchByAlgoliaDarkbBackground.svg";
|
||||
|
||||
class Header extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -52,7 +53,14 @@ class Header extends React.Component {
|
|||
</a>
|
||||
</h1>
|
||||
<span style={{ flexGrow: 1 }} />
|
||||
<AlgoliaLogo
|
||||
style={{
|
||||
opacity: this.props.searchQuery.length > 0 ? 0.5 : 0,
|
||||
transition: "opacity ease-in 300ms"
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
style={{ marginLeft: 10 }}
|
||||
type="text"
|
||||
onChange={e => this.props.setSearchQuery(e.target.value)}
|
||||
value={this.props.searchQuery || ""}
|
||||
|
|
|
|||
88
src/redux/reducer.js
Normal file
88
src/redux/reducer.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import skins from "../skins.json";
|
||||
import { ABOUT_PAGE } from "../constants";
|
||||
|
||||
const defaultState = {
|
||||
searchQuery: null,
|
||||
selectedSkinPosition: null,
|
||||
matchingHashes: null,
|
||||
selectedSkinHash: null,
|
||||
skinZip: null,
|
||||
focusedSkinFile: null,
|
||||
fileExplorerOpen: false,
|
||||
activeContentPage: null,
|
||||
skins
|
||||
};
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
switch (action.type) {
|
||||
case "SELECTED_SKIN":
|
||||
return {
|
||||
...state,
|
||||
selectedSkinHash: action.hash,
|
||||
selectedSkinPosition: action.position,
|
||||
skinZip: null,
|
||||
focusedSkinFile: null
|
||||
};
|
||||
case "CLOSE_MODAL":
|
||||
return {
|
||||
...state,
|
||||
selectedSkinHash: null,
|
||||
selectedSkinPosition: null,
|
||||
skinZip: null,
|
||||
activeContentPage: null
|
||||
};
|
||||
case "SEARCH_QUERY_CHANGED":
|
||||
return {
|
||||
...state,
|
||||
searchQuery: action.query,
|
||||
selectedSkinHash: null,
|
||||
selectedSkinPosition: null,
|
||||
focusedSkinFile: null
|
||||
};
|
||||
case "GOT_NEW_MATCHING_HASHES":
|
||||
return {
|
||||
...state,
|
||||
matchingHashes: action.matchingHashes
|
||||
};
|
||||
case "LOADED_SKIN_ZIP":
|
||||
return {
|
||||
...state,
|
||||
skinZip: action.zip
|
||||
};
|
||||
case "SELECTED_SKIN_FILE_TO_FOCUS": {
|
||||
return {
|
||||
...state,
|
||||
focusedSkinFile: {
|
||||
content: null,
|
||||
ext: action.ext,
|
||||
fileName: action.fileName
|
||||
}
|
||||
};
|
||||
}
|
||||
case "GOT_FOCUSED_SKIN_FILE":
|
||||
return {
|
||||
...state,
|
||||
focusedSkinFile: {
|
||||
...state.focusedSkinFile,
|
||||
content: action.content
|
||||
}
|
||||
};
|
||||
case "REQUESTED_ABOUT_PAGE":
|
||||
return {
|
||||
...state,
|
||||
activeContentPage: ABOUT_PAGE
|
||||
};
|
||||
case "OPEN_FILE_EXPLORER":
|
||||
return {
|
||||
...state,
|
||||
fileExplorerOpen: true
|
||||
};
|
||||
case "CLOSE_FILE_EXPLORER":
|
||||
return {
|
||||
...state,
|
||||
fileExplorerOpen: false
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
1
src/searchByAlgoliaDarkbBackground.svg
Normal file
1
src/searchByAlgoliaDarkbBackground.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.7 KiB |
File diff suppressed because one or more lines are too long
16
yarn.lock
16
yarn.lock
|
|
@ -3579,6 +3579,10 @@ fs.realpath@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
|
||||
fscreen@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/fscreen/-/fscreen-1.0.2.tgz#c4c51d96d819d75a19d728e0df445f9be9bb984f"
|
||||
|
||||
fsevents@1.2.4, fsevents@^1.2.2, fsevents@^1.2.3:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426"
|
||||
|
|
@ -7859,10 +7863,6 @@ shebang-regex@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
||||
|
||||
shell-escape@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shell-escape/-/shell-escape-0.2.0.tgz#68fd025eb0490b4f567a027f0bf22480b5f84133"
|
||||
|
||||
shell-quote@1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
|
||||
|
|
@ -8727,9 +8727,11 @@ wbuf@^1.1.0, wbuf@^1.7.2:
|
|||
dependencies:
|
||||
minimalistic-assert "^1.0.0"
|
||||
|
||||
webamp@^1.3.0-beta.2:
|
||||
version "1.3.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/webamp/-/webamp-1.3.0-beta.2.tgz#d0493a2dd844ab146546a43fc8ed7ce75ac0504b"
|
||||
webamp@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/webamp/-/webamp-1.3.1.tgz#352ab508d4400bda92f3b5fe7ae63241fa18ac1e"
|
||||
dependencies:
|
||||
fscreen "^1.0.2"
|
||||
|
||||
webidl-conversions@^4.0.2:
|
||||
version "4.0.2"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue