Merge branch 'master' of https://github.com/MillenniumEarl/F95API into master

pull/26/head
MillenniumEarl 2020-10-16 09:27:01 +02:00
commit 09e77f6804
11 changed files with 797 additions and 1462 deletions

View File

@ -13,6 +13,7 @@ enabled = true
"nodejs", "nodejs",
"mocha" "mocha"
] ]
style_guide = "standard"
[[transformers]] [[transformers]]
name = "prettier" name = "prettier"

View File

@ -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();

View File

@ -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,
}; };

View File

@ -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",
}); });

View File

@ -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.

View File

@ -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;
} }

View File

@ -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;
}; };

File diff suppressed because it is too large Load Diff

795
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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"
}, },

View File

@ -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";