first commit

This commit is contained in:
2025-01-20 23:58:11 +03:00
commit 1d307908c6
11 changed files with 713 additions and 0 deletions

48
.gitignore vendored Normal file
View File

@@ -0,0 +1,48 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
reports/
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
node_modules/
public/src/thumbs
public/avatars/
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
package-lock.json
.env
.env-mongo
.vscode/
settings.json
Procfile
dump.rdb
error.log
output.log
.env.development
.env.production

48
.npmignore Normal file
View File

@@ -0,0 +1,48 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
reports/
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
node_modules/
public/src/thumbs
public/avatars/
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
package-lock.json
.env
.env-mongo
.vscode/
settings.json
Procfile
dump.rdb
error.log
output.log
.env.development
.env.production

81
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,81 @@
pipeline {
agent any
environment {
BUILD_TIMESTAMP = new java.text.SimpleDateFormat('yyyy-MM-dd HH:mm:ss').format(new java.util.Date())
}
tools {
nodejs('22.13.0')
}
triggers {
cron('H */6 * * *') // Her 6 saatte bir çalıştır
}
stages {
stage('List directory - Before removing reports') {
steps {
echo "List directory - Before removing reports"
sh 'ls -la'
}
}
stage('Remove reports') {
steps {
echo "Remove reports files"
sh 'rm -rf reports'
}
}
stage('List directory - After removing reports') {
steps {
echo "List directory - After removing reports"
sh 'ls -la'
}
}
stage('Check for package.json changes') {
steps {
echo "Check for package.json changes"
script {
// Check if package.json has changed
def hasChanges = sh(script: 'git diff --name-only HEAD~1 | grep "package.json"', returnStatus: true) == 0
if (hasChanges) {
echo "package.json changed, running npm install"
currentBuild.result = 'SUCCESS'
sh 'npm install'
} else {
echo "package.json not changed, skipping npm install"
currentBuild.result = 'SUCCESS'
}
}
}
}
stage('Run Tests') {
steps {
echo "Runing Test.."
sh 'npm test'
}
}
stage('Publish Test Results') {
steps {
echo "Publish Test Results"
junit 'reports/test-results.xml'
echo "Send test result slack 🚚"
}
}
}
post {
always {
slackSend(
channel: '#jenkins',
tokenCredentialId: 'slack-token',
message: "Job 📦 '${env.JOB_NAME} [${env.BUILD_NUMBER}]' başarısız oldu. Detaylar: ${env.BUILD_URL}",
color: currentBuild.result == 'SUCCESS' ? 'good' : 'danger'
)
}
failure {
slackSend(
channel: '#jenkins',
tokenCredentialId: 'slack-token',
message: "Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' başarısız oldu. Detaylar: ${env.BUILD_URL}",
color: 'danger'
)
}
}
}

87
Readme.md Normal file
View File

@@ -0,0 +1,87 @@
# Unoffical Amazon Book Search
This library allows you to retrieve book information from Amazon using only the ISBN number, without needing an API key. The library analyzes Amazon HTML structure and returns book details in JSON format. It works asynchronously (based on Promises).
At the same time, you can recreate book descriptions using Gemini AI with this library. Remember that you need an API key for the Gemini service to do this!
## ✨ Features
- 📚 Retrieves book information from Amazon using the ISBN number.
- 🖼️ Returns detailed information such as book cover, title, author, genre, and publication date.
- 🕒 Customizable delay between requests.
- ✅ Tests integrated with Mocha and Chai.
- 🌐 Web data is fetched and parsed using Axios and Cheerio.
- 🤖 Book descriptions created with Gemini AI!
## 🎯 Requirements
- Node.js (v14 veya üzeri)
- NPM
- Internet connection
- Curiosity
- Preferred Gemini AI API Key!
## 📦 Installation
```bash
npm install szbk-amazon-book-search
```
## 🚀 Usage
The following example shows how to use the API:
```javascript
const AmazonBookSearch = require("szbk-amazon-book-search");
(async () => {
try {
const BookSearch = new AmazonBookSearch("en");
const bookDetails = await BookSearch.getBookDetails("0593724283");
// const bookDetails = await BookSearch.getBookDetails("0593724283", "Gemini API Key");
console.log(bookDetails);
} catch (error) {
console.error("Error:", error.message);
}
})();
// Example Output:
{
title: 'The Desert Spear: Book Two of The Demon Cycle',
thumbImage: 'https://m.media-amazon.com/images/I/51VC3BV9KBL._SY445_SX342_.jpg',
authorName: 'Peter V. Brett',
description: 'INTERNATIONAL BESTSELLER • “[Peter V. Brett] confirms his place among epic fantasys pantheon of greats amid the likes of George R. R. Martin, Steven Erikson, and Robert Jordan.”—Fantasy Book Critic The second volume in the internationally bestselling Demon Cycle series continues the epic tale of humanitys last stand against an army of demons—and reveals a new contender for the role of savior. The sun is setting on humanity. The night now belongs to voracious demons that prey upon a dwindling population forced to cower behind half-forgotten symbols of power. Legends tell of a Deliverer: a general who once bound all mankind into a single force that defeated the demons. But is the return of the Deliverer just another myth? Out of the sands rides Ahmann Jardir, who has forged the desert tribes of Krasia into a demon-killing army. He has proclaimed himself SharDama Ka, the Deliverer, and carries ancient weapons—a spear and a crown—that give credence to his claim. But the Northerners claim their own Deliverer: the Warded Man, a dark, forbidding figure. Once, the SharDama Ka and the Warded Man were friends. Now they are fierce adversaries. Yet as old allegiances are tested and fresh alliances forged, all are unaware of the appearance of a new breed of demon, more intelligent—and deadly—than any that have come before. Includes a bonus Demon Cycle novella, Brayans Gold, and a Krasian dictionaryDont miss any of the thrilling novels in Peter V. Bretts Demon Cycle THE WARDED MAN • THE DESERT SPEAR • THE DAYLIGHT WAR • THE SKULL THRONE • THE CORE',
page: 864,
publisher: 'Del Rey',
isbn: '0593724283',
date: 'November 7, 2023',
rate: '4.5'
}
```
## 📂 Project Structure
```javascript
szbk-amazon-search-book
config
index.js # Configuration file
lib
index.js # AmazonBookSearch class
module.js # Data processing and parsing operations
test
index.js # Integration tests
index.js # Entry point
package.json # Dependencies and scripts
README.md # Documentation
```
## 🧪 Tests
To run the tests, follow these steps:
1. Install test dependencies:
```javascript
npm install
```
2. Run the tests:
```javascript
npm test
```

16
config/index.js Normal file
View File

@@ -0,0 +1,16 @@
require("dotenv").config();
const config = {
en_base_url: "https://www.amazon.com/s?k=",
en_detail_url: "https://www.amazon.com/dp/",
tr_base_url: "https://www.amazon.com.tr/s?k=",
tr_detail_url: "https://www.amazon.com.tr/dp/",
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.105 Safari/537.36",
},
// Amazon'a istek atarken, iki istek arasındaki zaman farkıdır
fetchTimeout: 2000,
};
module.exports = config;

1
index.js Normal file
View File

@@ -0,0 +1 @@
module.exports = require('./lib');

85
lib/index.js Normal file
View File

@@ -0,0 +1,85 @@
const modules = require("./module");
const config = require("../config");
const axios = require("axios");
class AmazonBookSearch {
constructor(location) {
if (location !== "tr" && location !== "en") {
throw new Error("Yanlış konum!");
}
this.url = location === "tr" ? config.tr_base_url : config.en_base_url;
const fetchBookId = async (isbn) => {
const headers = config.headers;
try {
const url = encodeURI(this.url + isbn);
const response = await axios.get(url, { headers });
if (!response.data) {
throw new Error("Kitap bilgisi bulunamadı!");
}
const bookId = modules.extractBookId(response.data);
return bookId;
} catch (error) {
throw new Error("Hata: " + error.message);
}
};
this.getBookDetails = async (isbn, geminiApiKey) => {
const headers = config.headers;
try {
const bookId = await fetchBookId(isbn);
const url = encodeURI(location == "tr" ? config.tr_detail_url + bookId : config.en_detail_url + bookId);
return new Promise((resolve, reject) => {
setTimeout(async () => {
try {
const response = await axios.get(url, { headers });
if (!response.data) {
throw new Error("Detay bilgisi bulunamadı!");
}
const details = await modules.extractBookDetails(
response.data,
isbn,
geminiApiKey,
location
);
resolve(details);
} catch (error) {
reject(error);
}
}, config.fetchTimeout);
});
} catch (error) {
throw new Error("Hata: " + error.message);
}
};
}
}
module.exports = AmazonBookSearch;
// Gemini API Key girilirse, amazon'da bulunan kitap açıklaması gemini tarafından yeniden oluşturulur.
// (async () => {
// try {
// const BookSearch = new AmazonBookSearch("tr");
// const bookDetails = await BookSearch.getBookDetails("9786257746168", "AIzaSyAY15XJcK1VIxzMRe48dyDEeNPqeqhQt2I");
// console.log(bookDetails);
// } catch (error) {
// console.log(error.message);
// }
// })();
// //
// (async () => {
// try {
// const BookSearch = new AmazonBookSearch("en");
// const bookDetails = await BookSearch.getBookDetails("0593724283");
// console.log(bookDetails);
// } catch (error) {
// console.log(error.message);
// }
// })();

106
lib/module.js Normal file
View File

@@ -0,0 +1,106 @@
const { GoogleGenerativeAI } = require("@google/generative-ai");
const cheerio = require("cheerio");
const geminiDescriptionEdit = async (description, geminiApiKey, location) => {
if (geminiApiKey != undefined) {
const genAI = new GoogleGenerativeAI(geminiApiKey);
const model = genAI.getGenerativeModel({ model: "gemini-pro" });
let prompt;
if (location == "en") {
prompt =
description +
" based on the text above, provide a detailed book description of similar length. Do not add any comments to the text, remain completely faithful to it. Only provide the book description, without using a title like book description.";
console.log(prompt);
} else {
prompt =
description +
" yukarıdaki metine bağlı kalarak, benzer uzunlukta bir kitap tanımı ver. Metine hiçbir yorum katma, metine tamamen sadık kal. Sadece kitap tanımını ver, Kitap Tanımı şeklinde bir başlık kullanma";
}
const result = await model.generateContent(prompt);
const response = await result.response;
return response.text();
} else {
return description;
}
};
const extractBookId = (html) => {
const $ = cheerio.load(html);
let bookId = null;
$("[data-csa-c-item-id]").each((index, element) => {
const itemId = $(element).attr("data-csa-c-item-id");
if (!itemId.startsWith("amzn1.asin.1.B")) {
bookId = itemId
.replace(/^amzn1\.asin(\.amzn1)?\./, "")
.replace(/^1\./, "");
return false; // Exit loop after finding the first valid bookId
}
});
return bookId;
};
const extractBookPage = ($) => {
const text = $("div.rpi-attribute-value span").text(); // İçeriği al
const numberMatch = text.match(/\d+/); // Yalnızca rakamları al
return numberMatch ? parseInt(numberMatch[0], 10) : null; // Sayıyı döndür
};
const extractBookPublisher = ($) => {
const publisherParentDiv = $(".rpi-icon.book_details-publisher").parent();
const publisherDiv = publisherParentDiv.next();
const spanInsidePublisherSibling = publisherDiv.find("span");
return spanInsidePublisherSibling.text();
};
const extractBookDate = ($) => {
const dateParentDiv = $(".rpi-icon.book_details-publication_date").parent();
const dateDiv = dateParentDiv.next();
const spanInsideDateSibling = dateDiv.find("span");
return spanInsideDateSibling.text();
};
const extractBookDetails = async (html, isbn, geminiApiKey, location) => {
const extractedText = html
.match(
/<div\s[^>]*class="a-expander-content a-expander-partial-collapse-content"[^>]*>(.*?)<\/div>/s
)?.[1]
?.replace(/<[^>]+>/g, "")
.trim();
// Sonucu yazdır
const $ = cheerio.load(html);
const title = $("#imgTagWrapperId img").attr("alt");
const thumbImage = $("#imgTagWrapperId img").attr("src");
const authorName = $(".author a").text();
const descriptionRaw = $("#bookDescription_feature_div .a-expander-content")
.text()
.trim();
const description = await geminiDescriptionEdit(
descriptionRaw,
geminiApiKey,
location
);
const page = extractBookPage($);
const publisher = extractBookPublisher($);
const date = extractBookDate($);
const ratingText = $('i[data-hook="average-star-rating"] .a-icon-alt').text();
const rate = ratingText.match(/[\d.,]+/)[0].replace(",", ".");
return {
title,
thumbImage,
authorName,
description,
page,
publisher,
isbn,
date,
rate
};
};
module.exports = { extractBookId, extractBookDetails };

11
mocha-report-config.json Normal file
View File

@@ -0,0 +1,11 @@
{
"reporterEnabled": "spec, mocha-junit-reporter",
"mochaJunitReporterReporterOptions": {
"mochaFile": "./reports/test-results.xml",
"testsuitesTitle": "📦 Amazon Book Search Integration Test",
"suiteTitleSeparatedBy": ": ",
"properties": {
"environment": "development"
}
}
}

36
package.json Normal file
View File

@@ -0,0 +1,36 @@
{
"name": "szbk-amazon-book-search",
"version": "1.1.0",
"description": "Allows searching for books on Amazon. To print the book description with gemini-ai you need an api key.",
"main": "index.js",
"scripts": {
"test": "mocha --reporter mocha-multi-reporters --reporter-options configFile=mocha-report-config.json"
},
"repository": {
"type": "git",
"url": ""
},
"author": "szbk",
"keywords": [
"amazon",
"book",
"isbn",
"search",
"library",
"api"
],
"license": "MIT",
"dependencies": {
"@google/generative-ai": "^0.8.0",
"axios": "^1.6.8",
"cheerio": "^1.0.0-rc.12",
"dotenv": "^16.4.5"
},
"devDependencies": {
"chai": "^5.1.2",
"mocha": "^11.0.1",
"mocha-junit-reporter": "^2.2.1",
"mocha-multi-reporters": "^1.5.1"
}
}

194
test/index.js Normal file
View File

@@ -0,0 +1,194 @@
const { expect } = require("chai");
const AmazonBookSearch = require("../index");
describe("📦 Amazon Book Search (Turkish) Integration Test", () => {
let bookSearch;
const isbn = "9944824453";
const geminiApiKey = "AIzaSyAY15XJcK1VIxzMRe48dyDEeNPqeqhQt2I";
const timeoutDuration = 30000;
beforeEach(() => {
bookSearch = new AmazonBookSearch("tr");
});
it('Is the ISBN "9944824453" 🔥', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("isbn");
expect(bookDetails.isbn).to.equal("9944824453");
done();
})
.catch(done);
});
it('Is the book title "Dövmeli Adam: İblis Döngüsü 1" 🚀', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("title");
expect(bookDetails.title).to.equal("Dövmeli Adam: İblis Döngüsü 1");
done();
})
.catch(done);
});
it('Checks if the book thumb image is not empty 🌄', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("thumbImage");
expect(bookDetails.thumbImage).to.be.a("string");
done();
})
.catch(done);
});
it('Is the book\'s publication date "1 Temmuz 2016" ⏰', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("date");
expect(bookDetails.date).to.equal("1 Temmuz 2016");
done();
})
.catch(done);
});
it('Is the page count "638" 📋', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("page");
expect(bookDetails.page).to.equal(638);
done();
})
.catch(done);
});
it('Is the publisher "Epsilon Yayınları" 📖', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("publisher");
expect(bookDetails.publisher).to.equal("Epsilon Yayınları");
done();
})
.catch(done);
});
it('Is the Gemini API test working for the book description 🤖', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn, geminiApiKey)
.then((bookDetails) => {
expect(bookDetails).to.have.property("description");
expect(bookDetails.description).to.be.a("string");
done();
})
.catch(done);
});
});
describe("📦 Amazon Book Search (English) Integration Test", () => {
let bookSearch;
const isbn = "0593724283";
const timeoutDuration = 30000;
const geminiApiKey = "AIzaSyAY15XJcK1VIxzMRe48dyDEeNPqeqhQt2I";
beforeEach(() => {
bookSearch = new AmazonBookSearch("en");
});
it('Is the ISBN "0593724283" 🔥', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("isbn");
expect(bookDetails.isbn).to.equal("0593724283");
done();
})
.catch(done);
});
it('Is the book title "The Desert Spear: Book Two of The Demon Cycle" 🚀', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("title");
expect(bookDetails.title).to.equal("The Desert Spear: Book Two of The Demon Cycle");
done();
})
.catch(done);
});
it('Checks if the book thumb image is not empty 🌄', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("thumbImage");
expect(bookDetails.thumbImage).to.be.a("string");
done();
})
.catch(done);
});
it('Is the book\'s publication date "November 7, 2023" ⏰', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("date");
expect(bookDetails.date).to.equal("November 7, 2023");
done();
})
.catch(done);
});
it('Is the page count "864" 📋', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("page");
expect(bookDetails.page).to.equal(864);
done();
})
.catch(done);
});
it('Is the publisher "Del Rey" 📖', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn)
.then((bookDetails) => {
expect(bookDetails).to.have.property("publisher");
expect(bookDetails.publisher).to.equal("Del Rey");
done();
})
.catch(done);
});
it('Is the Gemini API test working for the book description 🤖', function (done) {
this.timeout(timeoutDuration);
bookSearch
.getBookDetails(isbn, geminiApiKey)
.then((bookDetails) => {
expect(bookDetails).to.have.property("description");
expect(bookDetails.description).to.be.a("string");
done();
})
.catch(done);
});
});