📚 Added volume-related endpoints

This commit is contained in:
2024-04-14 00:27:25 -04:00
parent f5d944e906
commit 856f6809e6

View File

@@ -3,7 +3,7 @@
import { Service, ServiceBroker, Context } from "moleculer"; import { Service, ServiceBroker, Context } from "moleculer";
import axios from "axios"; import axios from "axios";
import delay from "delay"; import delay from "delay";
import { isNil } from "lodash"; import { isNil, isUndefined } from "lodash";
import { fetchReleases, FilterTypes, SortTypes } from "comicgeeks"; import { fetchReleases, FilterTypes, SortTypes } from "comicgeeks";
import { matchScorer, rankVolumes } from "../utils/searchmatchscorer.utils"; import { matchScorer, rankVolumes } from "../utils/searchmatchscorer.utils";
import { import {
@@ -11,6 +11,7 @@ import {
scrapeIssuePage, scrapeIssuePage,
} from "../utils/scraping.utils"; } from "../utils/scraping.utils";
const { calculateLimitAndOffset, paginate } = require("paginate-info"); const { calculateLimitAndOffset, paginate } = require("paginate-info");
const { MoleculerError } = require("moleculer").Errors;
const CV_BASE_URL = "https://comicvine.gamespot.com/api/"; const CV_BASE_URL = "https://comicvine.gamespot.com/api/";
console.log("ComicVine API Key: ", process.env.COMICVINE_API_KEY); console.log("ComicVine API Key: ", process.env.COMICVINE_API_KEY);
@@ -28,7 +29,7 @@ export default class ComicVineService extends Service {
format: string; format: string;
sort: string; sort: string;
query: string; query: string;
fieldList: string; field_list: string;
limit: string; limit: string;
offset: string; offset: string;
resources: string; resources: string;
@@ -53,18 +54,23 @@ export default class ComicVineService extends Service {
handler: async ( handler: async (
ctx: Context<{ ctx: Context<{
volumeURI: string; volumeURI: string;
data: {}; fieldList: string;
}> }>
) => { ) => {
const { volumeURI, fieldList } = ctx.params;
const response = await axios.request({ const response = await axios.request({
url: url:
ctx.params.volumeURI + volumeURI +
"?api_key=" + "?api_key=" +
process.env.COMICVINE_API_KEY, process.env.COMICVINE_API_KEY,
params: { params: {
format: "json", format: "json",
field_list: fieldList,
},
headers: {
Accept: "application/json",
"User-Agent": "ThreeTwo",
}, },
headers: { Accept: "application/json" },
}); });
const { data } = response; const { data } = response;
return data; return data;
@@ -161,30 +167,45 @@ export default class ComicVineService extends Service {
return { result: paginatedData, meta: paginationInfo }; return { result: paginatedData, meta: paginationInfo };
}, },
}, },
getStoryArcs: { getResource: {
rest: "POST /getStoryArcs", rest: "POST /getResource",
handler: async (ctx: Context<{ comicObject: any }>) => { handler: async (
const { comicObject } = ctx.params; ctx: Context<{
console.log(JSON.stringify(comicObject, null, 2)); resources: string;
filter: string;
fieldList: string;
}>
) => {
const { resources, filter, fieldList } = ctx.params;
console.log(JSON.stringify(ctx.params, null, 2));
console.log(
CV_BASE_URL +
`${resources}` +
"?api_key=" +
process.env.COMICVINE_API_KEY
);
// 2. Query CV and get metadata for them // 2. Query CV and get metadata for them
const storyArcs = await axios({ const response = await axios({
method: "GET",
url: url:
CV_BASE_URL + CV_BASE_URL +
"story_arcs" + `${resources}` +
"?api_key=" + "?api_key=" +
process.env.COMICVINE_API_KEY, process.env.COMICVINE_API_KEY,
params: { params: {
resources: "story_arcs", resources: `${resources}`,
limit: "100", limit: "100",
format: "json", format: "json",
filter: `volume:${comicObject.sourcedMetadata.comicvine.volumeInformation.id}`, filter: `${filter}`,
field_list: `${fieldList}`,
}, },
headers: { headers: {
Accept: "application/json", Accept: "application/json",
"User-Agent": "ThreeTwo", "User-Agent": "ThreeTwo",
}, },
}); });
return storyArcs.data; console.log(response.data);
return response.data;
}, },
}, },
volumeBasedSearch: { volumeBasedSearch: {
@@ -356,6 +377,164 @@ export default class ComicVineService extends Service {
); );
}, },
}, },
getStoryArcs: {
rest: "POST /getStoryArcs",
handler: async (
ctx: Context<{ volumeUrl: string; volumeId: number }>
) => {
const { volumeUrl, volumeId } = ctx.params;
try {
const volumeResponse = await axios({
url:
volumeUrl +
"?api_key=" +
process.env.COMICVINE_API_KEY,
method: "GET",
params: {
limit: "100",
format: "json",
resources: "volumes",
},
headers: {
Accept: "application/json",
"User-Agent": "ThreeTwo",
},
});
const volumeData = volumeResponse.data;
if (volumeData.results.issues.length > 0) {
const issuePromises =
volumeData.results.issues.map(
async (issue: any) => {
const issueUrl = `${CV_BASE_URL}issue/4000-${issue.id}/?api_key=${process.env.COMICVINE_API_KEY}&format=json&field_list=story_arc_credits,description,image`;
try {
const issueResponse =
await axios.get(issueUrl, {
params: {
limit: "100",
format: "json",
},
headers: {
Accept: "application/json",
"User-Agent":
"ThreeTwo",
},
});
const issueData =
issueResponse.data.results;
// Transform each story arc to include issue's description and image
return (
issueData.story_arc_credits?.map(
(arc: any) => ({
...arc,
issueDescription:
issueData.description,
issueImage:
issueData.image,
})
) || []
);
} catch (error) {
console.error(
"An error occurred while fetching issue data:",
error.message
);
return []; // Return an empty array on error
}
}
);
try {
const storyArcsResults: any =
await Promise.all(issuePromises);
// Flatten the array of arrays
const flattenedStoryArcs =
storyArcsResults.flat();
// Deduplicate based on arc ID, while preserving the last seen issueDescription and issueImage
const uniqueStoryArcs = Array.from(
new Map(
flattenedStoryArcs.map(
(arc: any) => [arc.id, arc]
)
).values()
);
console.log(
`Found ${uniqueStoryArcs.length} unique story arc(s) for volume ID ${volumeId}:`
);
uniqueStoryArcs.forEach((arc: any) => {
console.log(
`- ${arc.name} (ID: ${arc.id}) with issueDescription and issueImage`
);
});
return uniqueStoryArcs;
} catch (error) {
console.error(
"An error occurred while processing story arcs:",
error
);
}
} else {
console.log(
"No issues found for the specified volume."
);
}
} catch (error) {
console.error(
"An error occurred while fetching data from ComicVine:",
error
);
}
},
},
getIssuesForVolume: {
rest: "POST /getIssuesForVolume",
async handler(ctx: Context<{ volumeId: number }>) {
const { volumeId } = ctx.params;
const issuesUrl = `${CV_BASE_URL}issues/?api_key=${process.env.COMICVINE_API_KEY}&filter=volume:${volumeId}&format=json&field_list=id,name,issue_number,description,image`;
try {
const response = await axios({
url: issuesUrl,
method: "GET",
params: {
limit: "100",
format: "json",
},
headers: {
Accept: "application/json",
"User-Agent": "ThreeTwo",
},
});
// Map over the issues to ensure each issue has `description` and `image` fields
const issuesWithDescriptionAndImage =
response.data.results.map((issue: any) => ({
...issue,
description: issue.description || "", // Ensure description field exists and default to empty string if not
image: issue.image || {}, // Ensure image field exists and default to empty object if not
}));
return issuesWithDescriptionAndImage;
} catch (error) {
this.logger.error(
"Error fetching issues from ComicVine:",
error.message
);
throw new MoleculerError(
"Failed to fetch issues",
500,
"FETCH_ERROR",
{ error: error.message }
);
}
},
},
}, },
methods: { methods: {
fetchVolumesFromCV: async (payload, output: any[] = []) => { fetchVolumesFromCV: async (payload, output: any[] = []) => {