🔨 graphql refactor for missingFiles flag
This commit is contained in:
@@ -767,7 +767,7 @@ export const resolvers = {
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
@@ -780,6 +780,111 @@ export const resolvers = {
|
||||
throw new Error(`Failed to fetch active import session: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
searchComicVine: async (
|
||||
_: any,
|
||||
{ searchTerms, exactMatch }: { searchTerms: string; exactMatch?: boolean },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
return await broker.call("library.volumeBasedSearch", { searchTerms, exactMatch });
|
||||
} catch (error) {
|
||||
console.error("Error searching ComicVine:", error);
|
||||
throw new Error(`Failed to search ComicVine: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
settings: async (
|
||||
_: any,
|
||||
{ settingsKey }: { settingsKey?: string },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
return await broker.call("settings.getSettings", settingsKey ? { settingsKey } : {});
|
||||
} catch (error) {
|
||||
console.error("Error fetching settings:", error);
|
||||
throw new Error(`Failed to fetch settings: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
hubs: async (
|
||||
_: any,
|
||||
{ host }: { host: any },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
return await broker.call("airdcpp.getHubs", { host });
|
||||
} catch (error) {
|
||||
console.error("Error fetching hubs:", error);
|
||||
throw new Error(`Failed to fetch hubs: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
bundles: async (
|
||||
_: any,
|
||||
{ comicObjectId, config }: { comicObjectId: string; config?: any },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
return await broker.call("library.getBundles", { comicObjectId, config });
|
||||
} catch (error) {
|
||||
console.error("Error fetching bundles:", error);
|
||||
throw new Error(`Failed to fetch bundles: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
torrentJobs: async (
|
||||
_: any,
|
||||
{ trigger }: { trigger: string },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
return await broker.call("torrentjobs.getTorrentData", { trigger });
|
||||
} catch (error) {
|
||||
console.error("Error fetching torrent jobs:", error);
|
||||
throw new Error(`Failed to fetch torrent jobs: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
searchTorrents: async (
|
||||
_: any,
|
||||
{ query }: { query: string },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
return await broker.call("prowlarr.search", { query });
|
||||
} catch (error) {
|
||||
console.error("Error searching torrents:", error);
|
||||
throw new Error(`Failed to search torrents: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
walkFolders: async (
|
||||
_: any,
|
||||
{ basePathToWalk, extensions }: { basePathToWalk: string; extensions?: string[] },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
return await broker.call("library.walkFolders", { basePathToWalk, extensions });
|
||||
} catch (error) {
|
||||
console.error("Error walking folders:", error);
|
||||
throw new Error(`Failed to walk folders: ${error.message}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
Mutation: {
|
||||
@@ -1547,7 +1652,7 @@ export const resolvers = {
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
@@ -1567,6 +1672,52 @@ export const resolvers = {
|
||||
throw new Error(`Failed to force complete session: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
applyComicVineMatch: async (
|
||||
_: any,
|
||||
{ comicObjectId, match }: { comicObjectId: string; match: any },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
return await broker.call("library.applyComicVineMetadata", { comicObjectId, match });
|
||||
} catch (error) {
|
||||
console.error("Error applying ComicVine match:", error);
|
||||
throw new Error(`Failed to apply ComicVine match: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
analyzeImage: async (
|
||||
_: any,
|
||||
{ imageFilePath }: { imageFilePath: string },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
return await broker.call("imagetransformation.analyze", { imageFilePath });
|
||||
} catch (error) {
|
||||
console.error("Error analyzing image:", error);
|
||||
throw new Error(`Failed to analyze image: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
uncompressArchive: async (
|
||||
_: any,
|
||||
{ filePath, comicObjectId, options }: { filePath: string; comicObjectId: string; options?: any },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
if (!broker) throw new Error("Broker not available in context");
|
||||
await broker.call("library.uncompressFullArchive", { filePath, comicObjectId, options });
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error uncompressing archive:", error);
|
||||
throw new Error(`Failed to uncompress archive: ${error.message}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1681,6 +1832,29 @@ export const resolvers = {
|
||||
}));
|
||||
},
|
||||
},
|
||||
|
||||
// Custom scalars
|
||||
JSON: {
|
||||
serialize: (value: any) => value,
|
||||
parseValue: (value: any) => value,
|
||||
parseLiteral: (ast: any) => {
|
||||
// Handle basic scalar literals; complex objects are passed as variables
|
||||
switch (ast.kind) {
|
||||
case "StringValue": return ast.value;
|
||||
case "IntValue": return parseInt(ast.value, 10);
|
||||
case "FloatValue": return parseFloat(ast.value);
|
||||
case "BooleanValue": return ast.value;
|
||||
case "NullValue": return null;
|
||||
default: return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
PredicateInput: {
|
||||
serialize: (value: any) => value,
|
||||
parseValue: (value: any) => value,
|
||||
parseLiteral: (ast: any) => ast.value ?? null,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -73,6 +73,9 @@ import { gql } from "graphql-tag";
|
||||
* ```
|
||||
*/
|
||||
export const typeDefs = gql`
|
||||
# Arbitrary JSON scalar
|
||||
scalar JSON
|
||||
|
||||
# Metadata source enumeration
|
||||
enum MetadataSource {
|
||||
COMICVINE
|
||||
@@ -353,6 +356,27 @@ export const typeDefs = gql`
|
||||
|
||||
# Get active import session (if any)
|
||||
getActiveImportSession: ImportSession
|
||||
|
||||
# Search ComicVine for volumes by name
|
||||
searchComicVine(searchTerms: String!, exactMatch: Boolean): ComicVineSearchResult!
|
||||
|
||||
# Get all app settings (optionally filtered by key)
|
||||
settings(settingsKey: String): AppSettings
|
||||
|
||||
# Get AirDC++ hubs for a given host
|
||||
hubs(host: HostInput!): [Hub!]!
|
||||
|
||||
# Get AirDC++ bundles for a comic object
|
||||
bundles(comicObjectId: ID!, config: JSON): [Bundle!]!
|
||||
|
||||
# Enqueue a repeating torrent data polling job
|
||||
torrentJobs(trigger: String!): TorrentJob
|
||||
|
||||
# Search Prowlarr for torrents
|
||||
searchTorrents(query: String!): [TorrentSearchResult!]!
|
||||
|
||||
# Walk a folder and return matching comic file paths
|
||||
walkFolders(basePathToWalk: String!, extensions: [String!]): [String!]!
|
||||
}
|
||||
|
||||
# Mutations
|
||||
@@ -406,6 +430,15 @@ export const typeDefs = gql`
|
||||
|
||||
# Force complete a stuck import session
|
||||
forceCompleteSession(sessionId: String!): ForceCompleteResult!
|
||||
|
||||
# Apply a ComicVine volume match to a comic
|
||||
applyComicVineMatch(comicObjectId: ID!, match: ComicVineMatchInput!): Comic!
|
||||
|
||||
# Analyze an image file for color and metadata
|
||||
analyzeImage(imageFilePath: String!): ImageAnalysisResult!
|
||||
|
||||
# Uncompress an archive (enqueues background job)
|
||||
uncompressArchive(filePath: String!, comicObjectId: ID!, options: JSON): Boolean
|
||||
}
|
||||
|
||||
# Input types
|
||||
@@ -793,4 +826,128 @@ export const typeDefs = gql`
|
||||
filesSucceeded: Int!
|
||||
filesFailed: Int!
|
||||
}
|
||||
|
||||
# Host configuration (used by AirDC++, bittorrent, prowlarr)
|
||||
type HostConfig {
|
||||
hostname: String
|
||||
port: String
|
||||
protocol: String
|
||||
username: String
|
||||
password: String
|
||||
}
|
||||
|
||||
input HostInput {
|
||||
hostname: String!
|
||||
port: String!
|
||||
protocol: String!
|
||||
username: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
# App settings
|
||||
type DirectConnectClient {
|
||||
host: HostConfig
|
||||
airDCPPUserSettings: JSON
|
||||
hubs: [JSON]
|
||||
}
|
||||
|
||||
type DirectConnectSettings {
|
||||
client: DirectConnectClient
|
||||
}
|
||||
|
||||
type BittorrentClient {
|
||||
name: String
|
||||
host: HostConfig
|
||||
}
|
||||
|
||||
type BittorrentSettings {
|
||||
client: BittorrentClient
|
||||
}
|
||||
|
||||
type ProwlarrClient {
|
||||
host: HostConfig
|
||||
apiKey: String
|
||||
}
|
||||
|
||||
type ProwlarrSettings {
|
||||
client: ProwlarrClient
|
||||
}
|
||||
|
||||
type AppSettings {
|
||||
directConnect: DirectConnectSettings
|
||||
bittorrent: BittorrentSettings
|
||||
prowlarr: ProwlarrSettings
|
||||
}
|
||||
|
||||
# AirDC++ Hub
|
||||
type Hub {
|
||||
id: Int
|
||||
name: String
|
||||
description: String
|
||||
userCount: Int
|
||||
}
|
||||
|
||||
# AirDC++ Bundle
|
||||
type Bundle {
|
||||
id: Int
|
||||
name: String
|
||||
size: String
|
||||
status: String
|
||||
speed: String
|
||||
}
|
||||
|
||||
# Torrent search result (from Prowlarr)
|
||||
type TorrentSearchResult {
|
||||
title: String
|
||||
size: Float
|
||||
seeders: Int
|
||||
leechers: Int
|
||||
downloadUrl: String
|
||||
guid: String
|
||||
publishDate: String
|
||||
indexer: String
|
||||
}
|
||||
|
||||
# Torrent job reference
|
||||
type TorrentJob {
|
||||
id: String
|
||||
name: String
|
||||
}
|
||||
|
||||
# Image analysis result
|
||||
type ImageAnalysisResult {
|
||||
analyzedData: JSON
|
||||
colorHistogramData: JSON
|
||||
}
|
||||
|
||||
# ComicVine volume search result
|
||||
type ComicVineVolume {
|
||||
id: Int
|
||||
name: String
|
||||
publisher: Publisher
|
||||
start_year: String
|
||||
count_of_issues: Int
|
||||
image: VolumeImage
|
||||
api_detail_url: String
|
||||
site_detail_url: String
|
||||
description: String
|
||||
}
|
||||
|
||||
type ComicVineSearchResult {
|
||||
results: [ComicVineVolume!]!
|
||||
total: Int!
|
||||
limit: Int
|
||||
offset: Int
|
||||
}
|
||||
|
||||
# Input for applying a ComicVine match
|
||||
input ComicVineMatchInput {
|
||||
volume: ComicVineVolumeRefInput!
|
||||
volumeInformation: JSON
|
||||
}
|
||||
|
||||
input ComicVineVolumeRefInput {
|
||||
api_detail_url: String!
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user