Skip to content

Commit

Permalink
Merge pull request #47 from pmenta/feat/onlinefix
Browse files Browse the repository at this point in the history
Feat/onlinefix
  • Loading branch information
Hydra authored Apr 20, 2024
2 parents 84155ad + 5f355f6 commit 70fe495
Show file tree
Hide file tree
Showing 11 changed files with 451 additions and 18 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
STEAMGRIDDB_API_KEY=YOUR_API_KEY
ONLINEFIX_USERNAME=YOUR_ONLINEFIX_USERNAME
ONLINEFIX_PASSWORD=YOUR_ONLINEFIX_PASSWORD
SENTRY_DSN=
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ pip install -r requirements.txt
## Environment variables

You'll need an SteamGridDB API Key in order to fetch the game icons on installation.
Once you have it, you can paste the `.env.example` file and put it on `STEAMGRIDDB_API_KEY`.
If you want to have onlinefix as a repacker you'll need to add your credentials to the .env

Once you have it, you can paste the `.env.example` file and put it on `STEAMGRIDDB_API_KEY`, `ONLINEFIX_USERNAME`, `ONLINEFIX_PASSWORD`.

## Running

Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@types/react-dom": "^18.2.22",
"@types/uuid": "^9.0.8",
"@types/webtorrent": "^0.109.8",
"@types/windows-1251": "^0.1.22",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"@vanilla-extract/webpack-plugin": "^2.3.7",
Expand Down Expand Up @@ -80,13 +81,15 @@
"date-fns": "^3.5.0",
"electron-squirrel-startup": "^1.0.0",
"flexsearch": "^0.7.43",
"got-scraping": "^4.0.5",
"i18next": "^23.10.1",
"i18next-browser-languagedetector": "^7.2.0",
"jsdom": "^24.0.0",
"lodash": "^4.17.21",
"lottie-react": "^2.4.0",
"parse-torrent": "9.1.5",
"ps-list": "^8.1.1",
"qs": "^6.12.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^14.1.0",
Expand All @@ -95,9 +98,11 @@
"react-router-dom": "^6.22.3",
"sqlite3": "^5.1.7",
"tasklist": "^5.0.0",
"tough-cookie": "^4.1.3",
"typeorm": "^0.3.20",
"update-electron-app": "^3.0.0",
"uuid": "^9.0.1",
"windows-1251": "^3.0.4",
"winston": "^3.12.0",
"yaml": "^2.4.1"
}
Expand Down
Binary file modified resources/hydra.db
Binary file not shown.
1 change: 1 addition & 0 deletions src/main/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const repackers = [
"CPG",
"TinyRepacks",
"GOG",
"onlinefix",
] as const;

export const months = [
Expand Down
2 changes: 2 additions & 0 deletions src/main/helpers/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export const xatabFormatter = (title: string) =>
.replace(/(v\.?([0-9]| )+)+([0-9]|\.|-|_|\/|[a-zA-Z]| )+/, "");

export const tinyRepacksFormatter = (title: string) => title;
export const onlinefixFormatter = (title: string) =>
title.replace("по сети", "").trim();

export const gogFormatter = (title: string) =>
title.replace(/(v\.[0-9]+|v[0-9]+\.|v[0-9]{4})+.+/, "");
2 changes: 2 additions & 0 deletions src/main/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
xatabFormatter,
tinyRepacksFormatter,
gogFormatter,
onlinefixFormatter,
} from "./formatters";
import { months, repackers } from "../constants";

Expand Down Expand Up @@ -40,6 +41,7 @@ export const repackerFormatter: Record<
CPG: (title: string) => title,
TinyRepacks: tinyRepacksFormatter,
GOG: gogFormatter,
onlinefix: onlinefixFormatter,
};

export const formatUploadDate = (str: string) => {
Expand Down
4 changes: 4 additions & 0 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getNewRepacksFromCPG,
getNewRepacksFromUser,
getNewRepacksFromXatab,
getNewRepacksFromOnlineFix,
readPipe,
startProcessWatcher,
writePipe,
Expand Down Expand Up @@ -77,6 +78,9 @@ const checkForNewRepacks = async () => {
getNewRepacksFromCPG(
existingRepacks.filter((repack) => repack.repacker === "CPG")
),
getNewRepacksFromOnlineFix(
existingRepacks.filter((repack) => repack.repacker === "onlinefix")
),
track1337xUsers(existingRepacks),
]).then(() => {
repackRepository.count().then((count) => {
Expand Down
1 change: 1 addition & 0 deletions src/main/services/repack-tracker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./1337x";
export * from "./xatab";
export * from "./cpg-repacks";
export * from "./gog";
export * from "./online-fix";
208 changes: 208 additions & 0 deletions src/main/services/repack-tracker/online-fix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { Repack } from "@main/entity";
import { savePage } from "./helpers";
import type { GameRepackInput } from "./helpers";
import { logger } from "../logger";
import { stringify } from "qs";
import parseTorrent, {
toMagnetURI,
Instance as TorrentInstance,
} from "parse-torrent";
import { JSDOM } from "jsdom";
import { gotScraping } from "got-scraping";
import { CookieJar } from "tough-cookie";

import { format, parse, sub } from "date-fns";
import { ru } from "date-fns/locale";
import { decode } from "windows-1251";
import { onlinefixFormatter } from "@main/helpers";

export const getNewRepacksFromOnlineFix = async (
existingRepacks: Repack[] = [],
page = 1,
cookieJar = new CookieJar()
): Promise<void> => {
const hasCredentials =
process.env.ONLINEFIX_USERNAME && process.env.ONLINEFIX_PASSWORD;
if (!hasCredentials) return;

const http = gotScraping.extend({
headerGeneratorOptions: {
browsers: [
{
name: "chrome",
minVersion: 87,
maxVersion: 89,
},
],
devices: ["desktop"],
locales: ["en-US"],
operatingSystems: ["windows", "linux"],
},
cookieJar: cookieJar,
});

if (page === 1) {
await http.get("https://online-fix.me/");
const preLogin =
((await http
.get("https://online-fix.me/engine/ajax/authtoken.php", {
headers: {
"X-Requested-With": "XMLHttpRequest",
Referer: "https://online-fix.me/",
},
})
.json()) as {
field: string;
value: string;
}) || undefined;

if (!preLogin.field || !preLogin.value) return;

const tokenField = preLogin.field;
const tokenValue = preLogin.value;

await http
.post("https://online-fix.me/", {
encoding: "binary",
headers: {
Referer: "https://online-fix.me",
Origin: "https://online-fix.me",
"Content-Type": "application/x-www-form-urlencoded",
},
body: stringify({
login_name: process.env.ONLINEFIX_USERNAME,
login_password: process.env.ONLINEFIX_PASSWORD,
login: "submit",
[tokenField]: tokenValue,
}),
})
.text();
}

const pageParams = page > 1 ? `${`/page/${page}`}` : "";

const home = await http.get(`https://online-fix.me${pageParams}`, {
encoding: "binary",
});
const document = new JSDOM(home.body).window.document;

const repacks: GameRepackInput[] = [];
const articles = Array.from(document.querySelectorAll(".news"));
const totalPages = Number(
document.querySelector("nav > a:nth-child(13)").textContent
);

try {
await Promise.all(
articles.map(async (article) => {
const gameName = onlinefixFormatter(
decode(article.querySelector("h2.title")?.textContent?.trim())
);

const gameLink = article.querySelector("a")?.getAttribute("href");

if (!gameLink) return;

const gamePage = await http
.get(gameLink, {
encoding: "binary",
})
.text();

const gameDocument = new JSDOM(gamePage).window.document;

const uploadDateText = gameDocument.querySelector("time").textContent;

let decodedDateText = decode(uploadDateText);

// "Вчера" means yesterday.
if (decodedDateText.includes("Вчера")) {
const yesterday = sub(new Date(), { days: 1 });
const formattedYesterday = format(yesterday, "d LLLL yyyy", {
locale: ru,
});
decodedDateText = decodedDateText.replace(
"Вчера", // "Change yesterday to the default expected date format"
formattedYesterday
);
}

const uploadDate = parse(
decodedDateText,
"d LLLL yyyy, HH:mm",
new Date(),
{
locale: ru,
}
);

const torrentButtons = Array.from(
gameDocument.querySelectorAll("a")
).filter((a) => a.textContent?.includes("Torrent"));

const torrentPrePage = torrentButtons[0]?.getAttribute("href");
if (!torrentPrePage) return;

const torrentPage = await http
.get(torrentPrePage, {
encoding: "binary",
headers: {
Referer: gameLink,
},
})
.text();

const torrentDocument = new JSDOM(torrentPage).window.document;

const torrentLink = torrentDocument
.querySelector("a:nth-child(2)")
?.getAttribute("href");

const torrentFile = Buffer.from(
await http
.get(`${torrentPrePage}/${torrentLink}`, {
responseType: "buffer",
})
.buffer()
);

const torrent = parseTorrent(torrentFile) as TorrentInstance;
const magnetLink = toMagnetURI({
infoHash: torrent.infoHash,
});

const torrentSizeInBytes = torrent.length;
const fileSizeFormatted =
torrentSizeInBytes >= 1024 ** 3
? `${(torrentSizeInBytes / 1024 ** 3).toFixed(1)}GBs`
: `${(torrentSizeInBytes / 1024 ** 2).toFixed(1)}MBs`;

repacks.push({
fileSize: fileSizeFormatted,
magnet: magnetLink,
page: 1,
repacker: "onlinefix",
title: gameName,
uploadDate: uploadDate,
});
})
);
} catch (err) {
logger.error(err.message, { method: "getNewRepacksFromOnlineFix" });
}

const newRepacks = repacks.filter(
(repack) =>
repack.uploadDate &&
!existingRepacks.some(
(existingRepack) => existingRepack.title === repack.title
)
);

if (!newRepacks.length) return;
if (page === totalPages) return;

await savePage(newRepacks);

return getNewRepacksFromOnlineFix(existingRepacks, page + 1, cookieJar);
};
Loading

0 comments on commit 70fe495

Please sign in to comment.