cn.yimian.xyz 4 years ago
parent 2b81413561
commit 2b889358d0
  1. 3
      .gitignore
  2. 1
      api/.gitignore
  3. 113
      api/index.js
  4. 379
      api/package-lock.json
  5. 15
      api/package.json
  6. 2
      api/startup.sh
  7. 594
      functions.php
  8. 78
      index.html
  9. 71
      makeCache.js
  10. 36
      package-lock.json
  11. 11
      phpapi/getInfo.php
  12. 18
      phpapi/getNext.php
  13. 47
      phpapi/getPreInfo.php
  14. 41
      phpapi/getQuality.php
  15. 1
      player
  16. 3
      reloadCache.php

3
.gitignore vendored

@ -0,0 +1,3 @@
.config.php
.db.key.php
cache/

1
api/.gitignore vendored

@ -0,0 +1 @@
node_modules/

@ -0,0 +1,113 @@
const express = require('express');
const fs = require('fs');
var app = express();
app.listen(30080, function () {
console.log('ACG.WATCH API listening on port 30080!');
});
app.get('/api/', (req, res)=>{
res.send('Hello from ACG.WATCH API!!');
});
app.get('/api/getAll', async (req, res) => {
res.send(video.obj);
});
app.get('/api/getClassBySeries', async (req, res)=>{
await video.getClassBySeries(req.query['class'])
.then(classess => res.send(classess))
.catch(err => {
var tmp = {};
Object.assign(tmp, video.unknownObj);
res.send(tmp);
});
});
app.get('/api/getVideoByUrl', async (req, res) => {
await video.getVideoByUrl(req.query.url)
.then(video => res.send(video))
.catch(err => {
var tmp = {};
Object.assign(tmp, video.unknownObj);
res.send(tmp);
});
});
app.get('/api/getNextByUrl', async (req, res) => {
await video.getNextByUrl(req.query.url)
.then(video => res.send(video))
.catch(err => {
var tmp = {};
Object.assign(tmp, video.unknownObj);
res.send(tmp);
});
});
var video = {
obj: {},
unknownObj: {
name: 'unknown',
season: 'unknown',
url: 'https://api.yimian.xyz/video?path=404.pm4',
description: 'unknown',
danmakuID: '40404040',
'class': 'extra'
},
getAll: () => JSON.parse(fs.readFileSync(__dirname+'/../cache/video.json')),
getClassBySeries: series => new Promise((resolve, reject) => {
Object.keys(video.obj).forEach(item => {
if(video.obj[item].hasOwnProperty(series)){
resolve(item);
}
});
reject('err in get Class By Series');
}),
getVideoByUrl: url => new Promise((resolve, reject) => {
Object.keys(video.obj).forEach(
classes => {
Object.keys(video.obj[classes]).forEach(series => {
video.obj[classes][series]['video'].forEach((vid, index) => {
if(vid.url == url){
resolve(video.getVideo(classes, series, index));
}
});
});
}
);
reject('err in getVideoByUrl');
}),
getVideo: (classes, series, vid) => {
if(vid >= video.obj[classes][series]['video'].length){
vid = 0;
}
var tmp = {
"class": classes,
"seriesID": series,
"vid": vid
};
Object.assign(tmp, video.obj[classes][series]['video'][vid]);
return tmp;
},
getNextByUrl: url => new Promise(async (resolve, reject) => {
try{
var lastVideo = await video.getVideoByUrl(url);
resolve(video.getVideo(lastVideo['class'], lastVideo.seriesID, lastVideo.vid+1));
}catch(e){
reject(e);
}
}),
reload: () => {
video.obj = video.getAll();
},
}
/* watch video.json cache */
video.reload();
fs.watch(__dirname+'/../cache/', event => video.reload());

379
api/package-lock.json generated

@ -0,0 +1,379 @@
{
"name": "acgwatch-api",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"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"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
},
"mime-types": {
"version": "2.1.26",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
"requires": {
"mime-db": "1.43.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.0"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}

@ -0,0 +1,15 @@
{
"name": "acgwatch-api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "iotcat",
"license": "MIT",
"dependencies": {
"express": "^4.17.1",
"fs": "0.0.1-security"
}
}

@ -0,0 +1,2 @@
#!/bin/bash
pm2 start index.js --name acgwatch-api -o /var/log/acgwatch/api.out -e /var/log/acgwatch/api.err --watch

@ -0,0 +1,594 @@
<?php
require '/home/yimian/www/acg.watch/.db.key.php';
require '/home/yimian/www/settlement/.config.php';
include '/home/ushio/conf/dbKeys/log.db.key.php';
/* acg.watch */
function getVideoAll(){
$json_string = file_get_contents('../cache/video.json');
return json_decode($json_string);
}
function getVideoClass(){
return array_keys(getVideoClass());
}
function getSeriesByClass($class){
$obj = getVideoAll();
return array_keys($obj[$class]);
}
function getClassBySeries($series){
$obj = getVideoAll();
forEach($obj as $class => $val){
forEach($val as $subseries => $subval){
if($subseries == $series){
return $class;
}
}
}
return null;
}
function getVidByUrl($url, $arr){
forEach($arr as $index => $val){
if($val['url'] == $url){
return $index;
}
}
return -1;
}
function getVideoByUrl($url){
$obj = getVideoAll();
if(substr($url, 0, 34) != "https://onedrive.yimian.xyz/video/"){
return array(
"url" => $url,
"series" => "Unknown",
"name" => "Unknown"
);
}
$arr = explode("/", substr($url, 34));
$class = getClassBySeries($arr[0]);
var_dump((Array)($obj->$class)[$arr[0]]);
$o = array(
"class" => $class,
"series" => $obj->$class->$arr[0]['series'],
"url" => $url
);
$vid = getVidByUrl($url, $obj[$class][$arr[0]]['video']);
$array_push($o, $obj[$class][$arr[0]]['video'][$vid]);
return $o;
}
/**database connection**/
//connect to database
function db__connect($servername="",$username="",$password="",$dbname="")
{//echo $GLOBALS['g_db_serverName'];
/* reset */
if($servername=="") $servername=$GLOBALS['g_db_serverName'];
if($username=="") $username=$GLOBALS['g_db_usrName'];
if($password=="") $password=$GLOBALS['g_db_psswd'];
if($dbname=="") $dbname=$GLOBALS['g_db_dbName'];
if($servername == "log"){
$servername = $GLOBALS['g_db_log_serverName'];
$username = $GLOBALS['g_db_log_usrName'];
$password = $GLOBALS['g_db_log_psswd'];
$dbname = $GLOBALS['g_db_log_dbName'];
}elseif($servername == "yulu"){
$servername = $GLOBALS['g_db_log_serverName'];
$username = $GLOBALS['g_db_log_usrName'];
$password = $GLOBALS['g_db_log_psswd'];
$dbname = "yulu";
}
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error)
{
die("Mysql Connect Failed: " . $conn->connect_error);
}
return ($conn);
}
//get table row number::(data_cnnct var,table name) ::(row number)
function db__rowNum($conn,$table,$clmnName="",$value="",$clmnName2="",$value2="")
{
$table=db__antisql($table);
$clmnName=db__antisql($clmnName);
$value=db__antisql($value);
$clmnName2=db__antisql($clmnName2);
$value2=db__antisql($value2);
if($clmnName=="") $sql = "SELECT COUNT(*) FROM $table";
elseif($clmnName2=="") $sql = "SELECT COUNT(*) FROM $table where $clmnName='$value'";
else $sql = "SELECT COUNT(*) FROM $table where $clmnName='$value' AND $clmnName2='$value2'";
$row_count = $conn->query($sql);
list($row_num) = $row_count->fetch_row();
return ($row_num);
}
//get row data from database::(data_cnnct var, table name,column name, column value)::(row info)
function db__getData($conn,$table,$clmnName="",$value="",$clmnName2="",$value2="",$sql_plus = "")
{
$table=db__antisql($table);
$clmnName=db__antisql($clmnName);
$value=db__antisql($value);
$clmnName2=db__antisql($clmnName2);
$value2=db__antisql($value2);
if($clmnName=="") $sql = "SELECT * FROM $table $sql_plus";
elseif($clmnName2=="") $sql = "SELECT * FROM $table where $clmnName='$value' $sql_plus";
else $sql = "SELECT * FROM $table where $clmnName='$value' AND $clmnName2='$value2' $sql_plus";
$result = $conn->query($sql);
//no data
if ($result->num_rows > 0) {}else{return 404;}
$i=0;
$arr=array();
while($row = $result->fetch_assoc()) {
$arr[$i++]=$row;
}
return ($arr);
}
//fnct for insert a row to database
function db__insertData($conn,$table,$content)
{
$table=db__antisql($table);
$key=array_keys($content);
$key=db__antisql($key);
$sql="insert INTO $table (";
for($i=0;$i<count($key);$i++)
{
$sql.="$key[$i]";
if($i!=count($key)-1) $sql.=", ";
}
$sql.=") VALUES (";
for($i=0;$i<count($key);$i++)
{
$tmp_key=$key[$i];
$content[$tmp_key]=db__antisql($content[$tmp_key]);
$sql.="'$content[$tmp_key]'";
if($i!=count($key)-1) $sql.=", ";
}
$sql.=")";
if (!($conn->query($sql) === TRUE)) echo "SQL Insert Error: " . $sql . "<br>" . $conn->error;
}
//fnct for update a row to database without check
function db__updateData($conn,$table,$content,$index)
{
$key=array_keys($content);
$key=db__antisql($key);
$sql="UPDATE $table SET ";
for($i=0;$i<count($key);$i++)
{
$tmp_key=$key[$i];
$content[$tmp_key]=db__antisql($content[$tmp_key]);
$sql.="$key[$i]='$content[$tmp_key]'";
if($i!=count($key)-1) $sql.=", ";
}
$key=array_keys($index);
$key=db__antisql($key);
$sql.=" WHERE ";
for($i=0;$i<count($key);$i++)
{
$tmp_key=$key[$i];
$index[$tmp_key]=db__antisql($index[$tmp_key]);
$sql.="$tmp_key='$index[$tmp_key]'";
if($i!=count($key)-1) $sql.=" AND ";
}
if (!($conn->query($sql) === TRUE)) echo "SQL Insert Error: " . $sql . "<br>" . $conn->error;
}
//push row data from database::(data_cnnct var, table name,column name, column value)::(row info)
function db__pushData($conn,$table,$content,$index="",$is_force=1)
{
if($index)
{
$index_keys=array_keys($index);
if(count($index_keys)==1) $result=db__rowNum($conn,$table,$index_keys[0],$index[$index_keys[0]]);
elseif(count($index_keys)==2) $result=db__rowNum($conn,$table,$index_keys[0],$index[$index_keys[0]],$index_keys[1],$index[$index_keys[1]]);
else return -1;
if($result>0) db__updateData($conn,$table,$content,$index);
else if($is_force) db__insertData($conn,$table,$content);
}
else
db__insertData($conn,$table,$content);
}
function db__delData($conn, $table, $clmnName, $value)
{
$value=db__antisql($value);
$clmnName=db__antisql($clmnName);
$sql = "DELETE FROM $table WHERE $clmnName = '$value'";
$conn->query($sql);
}
//anti sql
function db__antisql($str)
{
return(str_ireplace("'","",$str));
}
/*****log******/
function yimian__log($table, $val, $index = "", $cnt = null){
if(!isset($cnt)) $cnt = db__connect("log");
if($index != "") db__pushData($cnt, $table, $val, $index);
else db__pushData($cnt, $table, $val);
}
/** get from address **/
function get_from(){
if($_SERVER['HTTP_REFERER']) return $_SERVER['HTTP_REFERER'];
elseif($_REQUEST['from']) return $_REQUEST['from'];
}
function get_from_domain(){
$str = str_replace("http://","",get_from());
$str = str_replace("https://","",$str);
$strdomain = explode("/",$str);
return $strdomain[0];
}
/*****curl*****/
function curl__post($url = '', $param) {
if(empty($url)) {
return false;
}
$o = "";
foreach ($param as $k => $v) {
$o .= "$k=".urlencode($v)."&" ;
}
$postUrl = $url;
$curlPost = substr($o,0,-1);
$ch = curl_init();//初始化curl
curl_setopt($ch, CURLOPT_URL,$postUrl);//抓取指定网页
curl_setopt($ch, CURLOPT_HEADER, 0);//设置header
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);
$data = curl_exec($ch);//运行curl
curl_close($ch);
return $data;
}
/* get IP */
function get_ip(){
return getIp();
}
function getIp()
{
if (isset($_SERVER)) {
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach ($arr as $ip) {
$ip = trim($ip);
if ($ip != 'unknown') {
$realip = $ip;
break;
}
}
} else if (isset($_SERVER['HTTP_CLIENT_IP'])) {
$realip = $_SERVER['HTTP_CLIENT_IP'];
} else if (isset($_SERVER['REMOTE_ADDR'])) {
$realip = $_SERVER['REMOTE_ADDR'];
} else {
$realip = '0.0.0.0';
}
} else if (getenv('HTTP_X_FORWARDED_FOR')) {
$realip = getenv('HTTP_X_FORWARDED_FOR');
} else if (getenv('HTTP_CLIENT_IP')) {
$realip = getenv('HTTP_CLIENT_IP');
} else {
$realip = getenv('REMOTE_ADDR');
}
preg_match('/[\\d\\.]{7,15}/', $realip, $onlineip);
$realip = (!empty($onlineip[0]) ? $onlineip[0] : '0.0.0.0');
return $realip;
}
/* obs get img */
function getImg($path, $time = 300){
return str_replace('yimian-image.obs.cn-east-2.myhuaweicloud.com:443','image.yimian.xyz',trim(exec("obs sign obs://yimian-image/". $path ." -e=". $time)));
}
function getImgsInfo($type){
$arr_os = array();
$arr = array();
ini_set("pcre.backtrack_limit" , -1); ini_set("pcre.recursion_limit" , -1); ini_set("memory_limit" , "1024M");
exec('obs ls obs://yimian-image/'.$type.' -limit=-1', $arr_os);
//echo count($arr_os);
$str = implode ($arr_os);
preg_match_all('/img_(\S*?)_(\d{2,4})x(\d{2,4})_(\S*?)_(\S*?)_(\S*?).(jpe?g|png|gif|svg)\b/', $str, $arr);
return $arr;
}
/*****gugu*****/
function yimian__gugu($body){
$body = iconv("UTF-8","gbk//TRANSLIT",$body);
$url = "http://open.memobird.cn/home/printpaper";
return curl__post($url, array("ak" => $GLOBALS['ggj_ak'], "userID" => $GLOBALS['ggj_userID'], "memobirdID" => $GLOBALS['ggj_memobirdID'], "printcontent" => "T:".base64_encode($body)."", "timestamp" => "".time().""));
}
function gugu__send($ak, $userID, $memobirdID, $body){
$body = iconv("UTF-8","gbk//TRANSLIT",$body);
$url = "http://open.memobird.cn/home/printpaper";
return curl__post($url, array("ak" => $ak, "userID" => $userID, "memobirdID" => $memobirdID, "printcontent" => "T:".base64_encode($body)."", "timestamp" => "".time().""));
}
/** function for mail **/
function yimian__mail($to, $subject, $body, $from){
if($from == "") $from = "IoTcat 呓喵酱";
if($body == "") $body = "额(⊙﹏⊙) 未找到指定的邮件内容耶( •̀ ω •́ )y<br/><br/>更多信息请咨询<a href = 'https://iotcat.me'>IoTcat</a>期待你的回应啦~";
if($subject == "") $subject = "来自IoTcat的一声问候~";
$data = array(
'fromName' => $from, // 发件人名称
'from' => "admin@iotcat.xyz", // 发件地址
'to' => $to, // 收件地址
'replyTo' => "i@iotcat.me", // 回信地址
'subject' => $subject,
'html' => $body
);
// 当前请求区域
// 杭州
// API地址
$data['api'] = 'https://dm.aliyuncs.com/';
// API版本号
$data['version'] = '2015-11-23';
// 机房信息
$data['region'] = 'cn-hangzhou';
// AccessKeyId
$data['accessid'] = $GLOBALS['aym_AccessKey'];
// AccessKeySecret
$data['accesssecret'] = $GLOBALS['aym_SecretKey'];
// 是否成功
return aliyun($data);
}
//mail alliyun api
function aliyun($param)
{
// 重新组合为阿里云所使用的参数
$data = array(
'Action' => 'SingleSendMail', // 操作接口名
'AccountName' => $param['from'], // 发件地址
'ReplyToAddress' => "true", // 回信地址
'AddressType' => 1, // 地址类型
'ToAddress' => $param['to'], // 收件地址
'FromAlias' => $param['fromName'], // 发件人名称
'Subject' => $param['subject'], // 邮件标题
'HtmlBody' => $param['html'], // 邮件内容
'Format' => 'JSON', // 返回JSON
'Version' => $param['version'], // API版本号
'AccessKeyId' => $param['accessid'], // Access Key ID
'SignatureMethod' => 'HMAC-SHA1', // 签名方式
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'), // 请求时间
'SignatureVersion' => '1.0', // 签名算法版本
'SignatureNonce' => md5(time()), // 唯一随机数
'RegionId' => $param['region'] // 机房信息
);
// 请求签名
$data['Signature'] = sign($data, $param['accesssecret']);
// 初始化Curl
$ch = curl_init();
// 设置为POST请求
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
// 请求地址
curl_setopt($ch, CURLOPT_URL, $param['api']);
// 返回数据
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
// 提交参数
curl_setopt($ch, CURLOPT_POSTFIELDS, getPostHttpBody($data));
// 关闭ssl验证
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
// 执行请求
$result = curl_exec($ch);
// 获取错误代码
$errno = curl_errno($ch);
// 获取错误信息
$error = curl_error($ch);
// 获取返回状态码
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// 关闭请求
curl_close($ch);
// 成功标识
$flag = TRUE;
// 如果开启了Debug
if (1) {
// 记录时间
$log = '[Aliyun] ' . date('Y-m-d H:i:s') . ': ' . PHP_EOL;
// 如果失败
if ( $errno ) {
// 设置失败
$flag = FALSE;
$log .= '邮件发送失败, 错误代码:' . $errno . ',错误提示: ' . $error . PHP_EOL;
}
// 如果失败
if ( 400 <= $httpCode ) {
// 设置失败
$flag = FALSE;
// 尝试转换json
if ( $json = json_decode($result) ) {
$log .= '邮件发送失败,错误代码:' . $json->Code . ',错误提示:' . $json->Message . PHP_EOL;
} else {
$log .= '邮件发送失败, 请求返回HTTP Code:' . $httpCode . PHP_EOL;
}
}
// 记录返回值
$log .= '邮件发送返回数据:' . serialize($result) . PHP_EOL;
// 写入文件
}
yimian__log("log_mail",array("timestamp" => date('Y-m-d H:i:s', time()), "to_" => $param['to'], "from_" => $param['fromName'], "subject" => $param['subject'], "body" => $param['html'], "success" => (($flag)?1:0), "return_" => $log));
// 返回结果
//echo $log;
return $flag;
}
/**
* 阿里云签名
*
* @static
* @access private
*
* @param array $param 签名参数
* @param string $accesssecret 秘钥
*
* @return string
*/
function sign($param, $accesssecret)
{
// 参数排序
ksort($param);
// 组合基础
$stringToSign = 'POST&' . percentEncode('/') . '&';
// 临时变量
$tmp = '';
// 循环参数列表
foreach ( $param as $k => $v ) {
// 组合参数
$tmp .= '&' . percentEncode($k) . '=' . percentEncode($v);
}
// 去除最后一个&
$tmp = trim($tmp, '&');
// 组合签名参数
$stringToSign = $stringToSign . percentEncode($tmp);
// 数据签名
$signature = base64_encode(hash_hmac('sha1', $stringToSign, $accesssecret . '&', TRUE));
// 返回签名
return $signature;
}
/**
* 阿里云签名编码转换
*
* @static
* @access private
*
* @param string $val 要转换的编码
*
* @return string|string[]|null
*/
function percentEncode($val)
{
// URL编码
$res = urlencode($val);
// 加号转换为%20
$res = preg_replace('/\+/', '%20', $res);
// 星号转换为%2A
$res = preg_replace('/\*/', '%2A', $res);
// %7E转换为~
$res = preg_replace('/%7E/', '~', $res);
return $res;
}
/**
* 阿里云请求参数组合
*
* @static
* @access private
*
* @param array $param 发送参数
*
* @return bool|string
*/
function getPostHttpBody($param)
{
// 空字符串
$str = "";
// 循环参数
foreach ( $param as $k => $v ) {
// 组合参数
$str .= $k . '=' . urlencode($v) . '&';
}
// 去除第一个&
return substr($str, 0, -1);
}

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ACG WATCH</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/awsm.css@3.0.4/dist/awsm_theme_pearl-lusta.min.css">
<script type="text/javascript">block_aplayer = true;</script>
<script src="https://cdn.yimian.xyz/ushio-js/ushio-head.min.js"></script>
</head>
<body>
<header>
<h1>ACG.WATCH</h1>
<p>Watch ACG video <abbr title="At ACG.WATCH">ONLINE</abbr></p>
<nav>
<ul>
<li><a href="./" aria-hidden="false">Homepage</a></li>
<li><a href="./">Playlist</a></li>
<li><a href="https://iotcat.me/">About Me</a></li>
</ul>
</nav>
</header>
<main>
<article id="content">
<section>
<aside>
<p>
<strong>S.F.</strong>
<script>document.write('<a href="https://acg.watch/player/?url='+cookie.get('last_watch')+'">Continue Your Last Watch</a>');</script>
</p>
<p>
<strong>R.L.</strong>
<a href="#" onClick="reload()">Reload Cache</a>
</p>
</aside>
</section>
</article>
</main>
<footer>
<p style="text-align: center;"> Powered By Ushio | Made with love by <a href="https://iotcat.me">iotcat</a> </p>
<br>
</footer>
<script src="https://cdn.yimian.xyz/ushio-js/ushio-footer.min.js"></script>
<script type="text/javascript">
var root = 'https://acg.watch/';
$.get(root+'api/getAll', res => {
console.log(res);
//res = JSON.parse(res);
Object.keys(res).forEach(classes => {
var section = `
<section>
<h2>${classes}</h2>`;
Object.keys(res[classes]).forEach(series => {
section += `
<details>
<summary>${res[classes][series]['series']}</summary>
<ul>`;
res[classes][series]['video'].forEach(video => {
section += `
<li><a href="${root+'player?url='+video.url}">${video.name}</a></li>`;
});
section += `
</ul>
</details>`;
});
section += `
</section>`;
$('#content').append(section);
});
});
/* reload func */
function reload(){
$.get('https://acg.watch/reloadCache.php', err => location.reload());
}
</script>
</body>
</html>

@ -0,0 +1,71 @@
const fs = require('fs');
const md5 = require('md5');
const _dir = '/mnt/onedrive/public/video/';
var video = {};
fs.readdir(_dir, (err, series)=>{
series.forEach((item)=>{
var conf = fs.readFileSync(
_dir + item + '/config.json',
{encoding:"utf-8"}
);
conf = JSON.parse(conf);
if(!video.hasOwnProperty(conf.class)){
video[conf.class] = {};
}
video[conf.class][item] = {};
video[conf.class][item]['series'] = conf.series;
video[conf.class][item]['video'] = [];
// console.log(conf);
/* get Series Info */
var no = fs.readdirSync(_dir + item);
no.splice(no.findIndex(fName => fName == 'config.json'), 1);
no.forEach(fName => {
var videoInfo = getVideoInfo(fName, conf, item);
video[conf.class][item]['video'].push(videoInfo);
if(conf.hasOwnProperty('video') && conf.video.hasOwnProperty(videoInfo.no)){
Object.assign(video[conf.class][item]['video'][video[conf.class][item]['video'].length - 1], conf.video[videoInfo.no]);
}
});
/* video sort */
video[conf.class][item]['video'].sort((a, b)=>a.no-b.no);
});
console.log(video.acg.YuruYuri.video);
fs.writeFile(__dirname+'/cache/video.json', JSON.stringify(video), 'utf8', err=>console.log(err));
});
var getVideoInfo = (fName, conf, item) => {
var o = {};
var arr = fName.split('.');
arr = arr[0].split('_');
o['season'] = getVideoSeason(arr);
o['no'] = getVideoNo(arr);
o['name'] = getVideoName(arr, conf);
o['danmakuID'] = md5(item+o.no).substring(0, 8);
o['url'] = 'https://onedrive.yimian.xyz/video/' + item + '/' + fName;
return o;
}
var getVideoSeason = arr => parseInt(arr[0].replace(/[^0-9]/ig,""));
var getVideoNo = arr => parseInt(arr[0].replace(/[^0-9]/ig,"")) * 1000 + parseInt(arr[1].replace(/[^0-9]/ig,""));
var getVideoName = (arr, conf) => {
var season = getVideoSeason(arr);
if(arr.length >= 3){
var alias = arr[2];
}else{
var alias = '第' + getVideoNo(arr) % 1000 + '集';
}
return conf.series + '--第' + season + '季-' + alias;
}

36
package-lock.json generated

@ -0,0 +1,36 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"charenc": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
},
"crypt": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
},
"fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ="
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"md5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
"integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
"requires": {
"charenc": "~0.0.1",
"crypt": "~0.0.1",
"is-buffer": "~1.1.1"
}
}
}
}

@ -0,0 +1,11 @@
<?php
include '../functions.php';
$url = $_REQUEST['url'];
$url = 'https://onedrive.yimian.xyz/video/YuruYuri/s1_01.mp4';
if(!isset($url)) die();
echo json_encode(getVideoByUrl($url));

@ -0,0 +1,18 @@
<?php
include '../functions.php';
$url = $_REQUEST['url'];
if(!isset($url)) die();
$conn = db__connect();
if(!db__rowNum($conn, "video", "url", $url)) die();
$res = db__getData($conn, "video", "url", $url);
$next = db__getData($conn, "video", "vid", $res[0]["link"]);
echo json_encode(array(
url => $next[0]["url"]
));

@ -0,0 +1,47 @@
<?php
include '../functions.php';
$url = $_REQUEST['url'];
if(!isset($url)) die();
$conn = db__connect();
if(!db__rowNum($conn, "video", "url", $url)) {
echo json_encode(array(
quality => array(
name => "首选",
url => $url
),
danmaku => substr(md5($url), 0, 8)
));
die();
}
$res = db__getData($conn, "video", "url", $url);
$arr = Array();
array_push($arr, array(
name => "首选",
url => $url
));
for($i = 1; $i <= 3; $i ++){
if($res[0]["url".$i] !== ""){
array_push($arr, array(
name => "备选".$i,
url => $res[0]["url".$i]
));
}
}
echo json_encode(Array(
quality => $arr,
danmaku => substr(md5($res[0]['vid']),0, 8)
));

@ -0,0 +1,41 @@
<?php
include '../functions.php';
$url = $_REQUEST['url'];
if(!isset($url)) die();
$conn = db__connect();
if(!db__rowNum($conn, "video", "url", $url)) {
echo json_encode(array(
name => "首选",
url => $url
));
die();
}
$res = db__getData($conn, "video", "url", $url);
$arr = Array();
array_push($arr, array(
name => "首选",
url => $url
));
for($i = 1; $i <= 3; $i ++){
if($res[0]["url".$i] !== ""){
array_push($arr, array(
name => "备选".$i,
url => $res[0]["url".$i]
));
}
}
echo json_encode($arr);

@ -0,0 +1 @@
Subproject commit eb1126607dcb134ba51e9857337f65ab5dc8617a

@ -0,0 +1,3 @@
<?php
echo exec('node makeCache.js');
Loading…
Cancel
Save