Merge branch 'master' of https://github.com/MillenniumEarl/F95API into master
commit
09e77f6804
|
@ -13,6 +13,7 @@ enabled = true
|
||||||
"nodejs",
|
"nodejs",
|
||||||
"mocha"
|
"mocha"
|
||||||
]
|
]
|
||||||
|
style_guide = "standard"
|
||||||
|
|
||||||
[[transformers]]
|
[[transformers]]
|
||||||
name = "prettier"
|
name = "prettier"
|
||||||
|
|
15
app/index.js
15
app/index.js
|
@ -98,7 +98,6 @@ var _browser = null;
|
||||||
* @returns {Promise<LoginResult>} Result of the operation
|
* @returns {Promise<LoginResult>} Result of the operation
|
||||||
*/
|
*/
|
||||||
module.exports.login = async function (username, password) {
|
module.exports.login = async function (username, password) {
|
||||||
|
|
||||||
if (shared.isLogged) {
|
if (shared.isLogged) {
|
||||||
if (shared.debug) console.log("Already logged in");
|
if (shared.debug) console.log("Already logged in");
|
||||||
let result = new LoginResult();
|
let result = new LoginResult();
|
||||||
|
@ -146,7 +145,6 @@ module.exports.login = async function (username, password) {
|
||||||
* @returns {Promise<Boolean>} Result of the operation
|
* @returns {Promise<Boolean>} Result of the operation
|
||||||
*/
|
*/
|
||||||
module.exports.loadF95BaseData = async function () {
|
module.exports.loadF95BaseData = async function () {
|
||||||
|
|
||||||
if (!shared.isLogged || !shared.cookies) {
|
if (!shared.isLogged || !shared.cookies) {
|
||||||
console.warn("User not authenticated, unable to continue");
|
console.warn("User not authenticated, unable to continue");
|
||||||
return false;
|
return false;
|
||||||
|
@ -196,7 +194,6 @@ module.exports.loadF95BaseData = async function () {
|
||||||
* @returns {Promise<Boolean>} true if an update is available, false otherwise
|
* @returns {Promise<Boolean>} true if an update is available, false otherwise
|
||||||
*/
|
*/
|
||||||
module.exports.chekIfGameHasUpdate = async function (info) {
|
module.exports.chekIfGameHasUpdate = async function (info) {
|
||||||
|
|
||||||
if (!shared.isLogged || !shared.cookies) {
|
if (!shared.isLogged || !shared.cookies) {
|
||||||
console.warn("user not authenticated, unable to continue");
|
console.warn("user not authenticated, unable to continue");
|
||||||
return info.version;
|
return info.version;
|
||||||
|
@ -227,7 +224,6 @@ module.exports.chekIfGameHasUpdate = async function (info) {
|
||||||
* an identified game (in the case of homonymy). If no games were found, null is returned
|
* an identified game (in the case of homonymy). If no games were found, null is returned
|
||||||
*/
|
*/
|
||||||
module.exports.getGameData = async function (name, includeMods) {
|
module.exports.getGameData = async function (name, includeMods) {
|
||||||
|
|
||||||
if (!shared.isLogged || !shared.cookies) {
|
if (!shared.isLogged || !shared.cookies) {
|
||||||
console.warn("user not authenticated, unable to continue");
|
console.warn("user not authenticated, unable to continue");
|
||||||
return null;
|
return null;
|
||||||
|
@ -265,7 +261,6 @@ module.exports.getGameData = async function (name, includeMods) {
|
||||||
* @returns {Promise<GameInfo>} Information about the game. If no game was found, null is returned
|
* @returns {Promise<GameInfo>} Information about the game. If no game was found, null is returned
|
||||||
*/
|
*/
|
||||||
module.exports.getGameDataFromURL = async function (url) {
|
module.exports.getGameDataFromURL = async function (url) {
|
||||||
|
|
||||||
if (!shared.isLogged || !shared.cookies) {
|
if (!shared.isLogged || !shared.cookies) {
|
||||||
console.warn("user not authenticated, unable to continue");
|
console.warn("user not authenticated, unable to continue");
|
||||||
return null;
|
return null;
|
||||||
|
@ -292,7 +287,6 @@ module.exports.getGameDataFromURL = async function (url) {
|
||||||
* @returns {Promise<UserData>} Data of the user currently logged in or null if an error arise
|
* @returns {Promise<UserData>} Data of the user currently logged in or null if an error arise
|
||||||
*/
|
*/
|
||||||
module.exports.getUserData = async function () {
|
module.exports.getUserData = async function () {
|
||||||
|
|
||||||
if (!shared.isLogged || !shared.cookies) {
|
if (!shared.isLogged || !shared.cookies) {
|
||||||
console.warn("user not authenticated, unable to continue");
|
console.warn("user not authenticated, unable to continue");
|
||||||
return null;
|
return null;
|
||||||
|
@ -339,7 +333,6 @@ module.exports.getUserData = async function () {
|
||||||
* You **must** be logged in to the portal before calling this method.
|
* You **must** be logged in to the portal before calling this method.
|
||||||
*/
|
*/
|
||||||
module.exports.logout = async function () {
|
module.exports.logout = async function () {
|
||||||
|
|
||||||
if (!shared.isLogged || !shared.cookies) {
|
if (!shared.isLogged || !shared.cookies) {
|
||||||
console.warn("user not authenticated, unable to continue");
|
console.warn("user not authenticated, unable to continue");
|
||||||
return;
|
return;
|
||||||
|
@ -493,10 +486,12 @@ async function loginF95(browser, username, password) {
|
||||||
|
|
||||||
await page.type(selectors.USERNAME_INPUT, username); // Insert username
|
await page.type(selectors.USERNAME_INPUT, username); // Insert username
|
||||||
await page.type(selectors.PASSWORD_INPUT, password); // Insert password
|
await page.type(selectors.PASSWORD_INPUT, password); // Insert password
|
||||||
await page.click(selectors.LOGIN_BUTTON); // Click on the login button
|
await Promise.all([
|
||||||
await page.waitForNavigation({
|
page.click(selectors.LOGIN_BUTTON), // Click on the login button
|
||||||
|
page.waitForNavigation({
|
||||||
waitUntil: shared.WAIT_STATEMENT,
|
waitUntil: shared.WAIT_STATEMENT,
|
||||||
}); // Wait for page to load
|
}), // Wait for page to load
|
||||||
|
]);
|
||||||
|
|
||||||
// Prepare result
|
// Prepare result
|
||||||
let result = new LoginResult();
|
let result = new LoginResult();
|
||||||
|
|
|
@ -65,6 +65,11 @@ class GameInfo {
|
||||||
* @type Boolean
|
* @type Boolean
|
||||||
*/
|
*/
|
||||||
this.isMod = false;
|
this.isMod = false;
|
||||||
|
/**
|
||||||
|
* Changelog for the last version.
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
this.changelog = null;
|
||||||
/**
|
/**
|
||||||
* Directory containing the local copy of the game
|
* Directory containing the local copy of the game
|
||||||
* @type String
|
* @type String
|
||||||
|
@ -96,6 +101,7 @@ class GameInfo {
|
||||||
lastUpdate: this.lastUpdate,
|
lastUpdate: this.lastUpdate,
|
||||||
lastPlayed: this.lastPlayed,
|
lastPlayed: this.lastPlayed,
|
||||||
isMod: this.isMod,
|
isMod: this.isMod,
|
||||||
|
changelog: this.changelog,
|
||||||
gameDir: this.gameDir,
|
gameDir: this.gameDir,
|
||||||
downloadInfo: this.downloadInfo,
|
downloadInfo: this.downloadInfo,
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,4 +28,5 @@ module.exports = Object.freeze({
|
||||||
DOWNLOAD_LINKS_CONTAINER: 'span[style="font-size: 18px"]',
|
DOWNLOAD_LINKS_CONTAINER: 'span[style="font-size: 18px"]',
|
||||||
SEARCH_THREADS_RESULTS_BODY: "div.contentRow-main",
|
SEARCH_THREADS_RESULTS_BODY: "div.contentRow-main",
|
||||||
SEARCH_THREADS_MEMBERSHIP: "li > a:not(.username)",
|
SEARCH_THREADS_MEMBERSHIP: "li > a:not(.username)",
|
||||||
|
THREAD_LAST_CHANGELOG: "div.bbCodeBlock-content > div:first-of-type",
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,6 +46,7 @@ module.exports.getGameInfo = async function (browser, url) {
|
||||||
let structuredText = await getMainPostStructuredText(page);
|
let structuredText = await getMainPostStructuredText(page);
|
||||||
let overview = getOverview(structuredText, info.isMod);
|
let overview = getOverview(structuredText, info.isMod);
|
||||||
let parsedInfos = parseConversationPage(structuredText);
|
let parsedInfos = parseConversationPage(structuredText);
|
||||||
|
let changelog = getLastChangelog(page);
|
||||||
|
|
||||||
// Fill in the GameInfo element with the information obtained
|
// Fill in the GameInfo element with the information obtained
|
||||||
info.name = await title;
|
info.name = await title;
|
||||||
|
@ -60,6 +61,8 @@ module.exports.getGameInfo = async function (browser, url) {
|
||||||
? parsedInfos["UPDATED"]
|
? parsedInfos["UPDATED"]
|
||||||
: parsedInfos["THREAD UPDATED"];
|
: parsedInfos["THREAD UPDATED"];
|
||||||
info.previewSource = await previewSource;
|
info.previewSource = await previewSource;
|
||||||
|
let temp = await changelog;
|
||||||
|
info.changelog = temp ? temp : "Unknown changelog";
|
||||||
//info.downloadInfo = await downloadData;
|
//info.downloadInfo = await downloadData;
|
||||||
/* Downloading games without going directly to
|
/* Downloading games without going directly to
|
||||||
* the platform appears to be prohibited by
|
* the platform appears to be prohibited by
|
||||||
|
@ -280,6 +283,28 @@ async function parsePrefixes(page, info) {
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* Get the last changelog available for the game.
|
||||||
|
* @param {puppeteer.Page} page Page containing the changelog
|
||||||
|
* @returns {Promise<String>} Changelog for the last version or null if no changelog is found
|
||||||
|
*/
|
||||||
|
async function getLastChangelog(page) {
|
||||||
|
// Gets the first post, where are listed all the game's informations
|
||||||
|
let post = (await page.$$(selectors.THREAD_POSTS))[0];
|
||||||
|
|
||||||
|
let spoiler = await post.$(selectors.THREAD_LAST_CHANGELOG);
|
||||||
|
if (!spoiler) return null;
|
||||||
|
|
||||||
|
let changelogHTML = await page.evaluate(
|
||||||
|
/* istanbul ignore next */
|
||||||
|
(e) => e.innerText,
|
||||||
|
spoiler
|
||||||
|
);
|
||||||
|
let parsedText = HTMLParser.parse(changelogHTML).structuredText;
|
||||||
|
return parsedText.replace("Spoiler", "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* Get game download links for different platforms.
|
* Get game download links for different platforms.
|
||||||
|
|
|
@ -8,6 +8,7 @@ const shared = require("./shared.js");
|
||||||
const constURLs = require("./constants/urls.js");
|
const constURLs = require("./constants/urls.js");
|
||||||
const selectors = require("./constants/css-selectors.js");
|
const selectors = require("./constants/css-selectors.js");
|
||||||
const { preparePage } = require("./puppeteer-helper.js");
|
const { preparePage } = require("./puppeteer-helper.js");
|
||||||
|
const { isF95URL } = require("./urls-helper.js");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @protected
|
* @protected
|
||||||
|
@ -32,10 +33,12 @@ module.exports.getSearchGameResults = async function (browser, gamename) {
|
||||||
|
|
||||||
await page.type(selectors.SEARCH_FORM_TEXTBOX, gamename); // Type the game we desire
|
await page.type(selectors.SEARCH_FORM_TEXTBOX, gamename); // Type the game we desire
|
||||||
await page.click(selectors.TITLE_ONLY_CHECKBOX); // Select only the thread with the game in the titles
|
await page.click(selectors.TITLE_ONLY_CHECKBOX); // Select only the thread with the game in the titles
|
||||||
await page.click(selectors.SEARCH_BUTTON); // Execute search
|
await Promise.all([
|
||||||
await page.waitForNavigation({
|
page.click(selectors.SEARCH_BUTTON), // Execute search
|
||||||
|
page.waitForNavigation({
|
||||||
waitUntil: shared.WAIT_STATEMENT,
|
waitUntil: shared.WAIT_STATEMENT,
|
||||||
}); // Wait for page to load
|
}), // Wait for page to load
|
||||||
|
]);
|
||||||
|
|
||||||
// Select all conversation titles
|
// Select all conversation titles
|
||||||
let resultsThread = await page.$$(selectors.SEARCH_THREADS_RESULTS_BODY);
|
let resultsThread = await page.$$(selectors.SEARCH_THREADS_RESULTS_BODY);
|
||||||
|
@ -114,6 +117,10 @@ async function getThreadURL(page, handle) {
|
||||||
(e) => e.querySelector("a").href,
|
(e) => e.querySelector("a").href,
|
||||||
handle
|
handle
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Some game already have a full URL
|
||||||
|
if (isF95URL(relativeURLThread)) return relativeURLThread;
|
||||||
|
|
||||||
let urlThread = new URL(relativeURLThread, constURLs.F95_BASE_URL).toString();
|
let urlThread = new URL(relativeURLThread, constURLs.F95_BASE_URL).toString();
|
||||||
return urlThread;
|
return urlThread;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,17 @@ const shared = require("./shared.js");
|
||||||
*/
|
*/
|
||||||
module.exports.prepareBrowser = async function () {
|
module.exports.prepareBrowser = async function () {
|
||||||
// Create a headless browser
|
// Create a headless browser
|
||||||
let browser = await puppeteer.launch({
|
let browser = null;
|
||||||
|
if (shared.chromiumLocalPath) {
|
||||||
|
browser = await puppeteer.launch({
|
||||||
executablePath: shared.chromiumLocalPath,
|
executablePath: shared.chromiumLocalPath,
|
||||||
headless: !shared.debug, // Use GUI when debug = true
|
headless: !shared.debug, // Use GUI when debug = true
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
browser = await puppeteer.launch({
|
||||||
|
headless: !shared.debug, // Use GUI when debug = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return browser;
|
return browser;
|
||||||
};
|
};
|
||||||
|
|
1370
coverage.lcov
1370
coverage.lcov
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"main": "./app/index.js",
|
"main": "./app/index.js",
|
||||||
"name": "f95api",
|
"name": "f95api",
|
||||||
"version": "1.2.3",
|
"version": "1.2.4",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Millennium Earl"
|
"name": "Millennium Earl"
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,7 +11,6 @@ const GameDownload = require("../app/scripts/classes/game-download.js");
|
||||||
|
|
||||||
debug(true);
|
debug(true);
|
||||||
main();
|
main();
|
||||||
//downloadGameNOPY();
|
|
||||||
//downloadGameMEGA();
|
//downloadGameMEGA();
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
@ -19,7 +18,7 @@ async function main() {
|
||||||
|
|
||||||
if (loginResult.success) {
|
if (loginResult.success) {
|
||||||
await loadF95BaseData();
|
await loadF95BaseData();
|
||||||
let gameData = await getGameData("employee benefits", false);
|
let gameData = await getGameData("queen's brothel", false);
|
||||||
console.log(gameData);
|
console.log(gameData);
|
||||||
|
|
||||||
// let userData = await getUserData();
|
// let userData = await getUserData();
|
||||||
|
@ -28,15 +27,6 @@ async function main() {
|
||||||
logout();
|
logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadGameNOPY() {
|
|
||||||
let gd = new GameDownload();
|
|
||||||
gd.hosting = "NOPY";
|
|
||||||
gd.link = "https://nopy.to/50jmNQbo/Kingdom_of_Deception-pc0.10.8.zip";
|
|
||||||
let savepath = join(__dirname, "Kingdom_of_Deception-pc0.10.8.zip");
|
|
||||||
let result = await gd.download(savepath);
|
|
||||||
console.log(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function downloadGameMEGA() {
|
async function downloadGameMEGA() {
|
||||||
let gd = new GameDownload();
|
let gd = new GameDownload();
|
||||||
gd.hosting = "NOPY";
|
gd.hosting = "NOPY";
|
||||||
|
|
Loading…
Reference in New Issue