use webdav for files
This commit is contained in:
+24
-4
@@ -18,6 +18,15 @@ import { franc } from 'franc-min';
|
||||
import { anyTo_iso_639_3 } from '@metazla/filename-tools';
|
||||
import type { PluginManifest, ProcessRequest, CallbackPayload } from './types.js';
|
||||
import { MetaCoreClient } from './meta-core-client.js';
|
||||
import { createWebDAVClient, WebDAVClient } from './webdav-client.js';
|
||||
|
||||
// Initialize WebDAV client if WEBDAV_URL is set
|
||||
const webdavClient = createWebDAVClient();
|
||||
if (webdavClient) {
|
||||
console.log('[jellyfin-nfo] Using WebDAV for file access');
|
||||
} else {
|
||||
console.log('[jellyfin-nfo] Using direct filesystem access');
|
||||
}
|
||||
|
||||
const englishIsoCode = 'eng';
|
||||
|
||||
@@ -49,6 +58,9 @@ export const manifest: PluginManifest = {
|
||||
};
|
||||
|
||||
async function fileExists(path: string): Promise<boolean> {
|
||||
if (webdavClient) {
|
||||
return webdavClient.exists(path);
|
||||
}
|
||||
try {
|
||||
await access(path);
|
||||
return true;
|
||||
@@ -57,8 +69,15 @@ async function fileExists(path: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
async function readNfoContent(nfoPath: string): Promise<string> {
|
||||
if (webdavClient) {
|
||||
return webdavClient.readText(nfoPath, 'utf8');
|
||||
}
|
||||
return readFile(nfoPath, 'utf8');
|
||||
}
|
||||
|
||||
async function parseNfoFile(nfoPath: string): Promise<any> {
|
||||
const content = await readFile(nfoPath, 'utf8');
|
||||
const content = await readNfoContent(nfoPath);
|
||||
const parser = new Parser({ explicitArray: false, mergeAttrs: true });
|
||||
return parser.parseStringPromise(content);
|
||||
}
|
||||
@@ -95,7 +114,7 @@ export async function process(
|
||||
const nfoPath = filePath.replace(/\.[^.]+$/, '.nfo');
|
||||
if (await fileExists(nfoPath)) {
|
||||
try {
|
||||
const nfoContent = await readFile(nfoPath, 'utf8');
|
||||
const nfoContent = await readNfoContent(nfoPath);
|
||||
const parser = new Parser({ explicitArray: false, mergeAttrs: true });
|
||||
const parsed = await parser.parseStringPromise(nfoContent);
|
||||
await extractNfoData(metaCore, cid, parsed, filePath);
|
||||
@@ -109,7 +128,7 @@ export async function process(
|
||||
const tvShowNfoPath = join(dirPath, 'tvshow.nfo');
|
||||
if (await fileExists(tvShowNfoPath)) {
|
||||
try {
|
||||
const nfoContent = await readFile(tvShowNfoPath, 'utf8');
|
||||
const nfoContent = await readNfoContent(tvShowNfoPath);
|
||||
const parser = new Parser({ explicitArray: false, mergeAttrs: true });
|
||||
const parsed = await parser.parseStringPromise(nfoContent);
|
||||
await extractNfoData(metaCore, cid, parsed, filePath);
|
||||
@@ -118,7 +137,8 @@ export async function process(
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[jellyfin-nfo] Processed NFO for ${filePath}`);
|
||||
const mode = webdavClient ? 'WebDAV' : 'filesystem';
|
||||
console.log(`[jellyfin-nfo] Processed NFO for ${filePath} (${mode})`);
|
||||
|
||||
await sendCallback({
|
||||
taskId: request.taskId,
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* WebDAV Client for Plugin File Access
|
||||
*
|
||||
* Provides HTTP-based file access to meta-sort's WebDAV endpoint.
|
||||
* Replaces direct filesystem access for containerized plugins.
|
||||
*/
|
||||
|
||||
export interface FileStats {
|
||||
size: number;
|
||||
mtime?: Date;
|
||||
}
|
||||
|
||||
export class WebDAVClient {
|
||||
private baseUrl: string;
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
// Remove trailing slash if present
|
||||
this.baseUrl = baseUrl.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert absolute file path to WebDAV URL
|
||||
* Strips /files prefix and appends to base URL
|
||||
*/
|
||||
toWebDAVUrl(filePath: string): string {
|
||||
// filePath is like "/files/watch/movie.mkv"
|
||||
// We need to convert to "http://meta-sort-dev/webdav/watch/movie.mkv"
|
||||
let relativePath = filePath;
|
||||
|
||||
// Strip /files prefix if present
|
||||
if (relativePath.startsWith('/files/')) {
|
||||
relativePath = relativePath.substring(6); // Remove "/files"
|
||||
} else if (relativePath.startsWith('/files')) {
|
||||
relativePath = relativePath.substring(6);
|
||||
}
|
||||
|
||||
// Ensure path starts with /
|
||||
if (!relativePath.startsWith('/')) {
|
||||
relativePath = '/' + relativePath;
|
||||
}
|
||||
|
||||
return this.baseUrl + relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file stats (size, mtime) via HTTP HEAD request
|
||||
*/
|
||||
async stat(filePath: string): Promise<FileStats> {
|
||||
const url = this.toWebDAVUrl(filePath);
|
||||
|
||||
const response = await fetch(url, { method: 'HEAD' });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`WebDAV HEAD failed for ${filePath}: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const contentLength = response.headers.get('content-length');
|
||||
const lastModified = response.headers.get('last-modified');
|
||||
|
||||
return {
|
||||
size: contentLength ? parseInt(contentLength, 10) : 0,
|
||||
mtime: lastModified ? new Date(lastModified) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Read entire file as buffer (for small files like NFO)
|
||||
*/
|
||||
async readFile(filePath: string): Promise<Buffer> {
|
||||
const url = this.toWebDAVUrl(filePath);
|
||||
|
||||
const response = await fetch(url, { method: 'GET' });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`WebDAV GET failed for ${filePath}: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return Buffer.from(arrayBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read file as text (for NFO XML files)
|
||||
*/
|
||||
async readText(filePath: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
|
||||
const buffer = await this.readFile(filePath);
|
||||
return buffer.toString(encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file exists via HTTP HEAD
|
||||
*/
|
||||
async exists(filePath: string): Promise<boolean> {
|
||||
const url = this.toWebDAVUrl(filePath);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { method: 'HEAD' });
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WebDAV client from environment variables
|
||||
*/
|
||||
export function createWebDAVClient(): WebDAVClient | null {
|
||||
const webdavUrl = process.env.WEBDAV_URL;
|
||||
|
||||
if (!webdavUrl) {
|
||||
console.warn('[webdav-client] WEBDAV_URL not set, WebDAV client unavailable');
|
||||
return null;
|
||||
}
|
||||
|
||||
return new WebDAVClient(webdavUrl);
|
||||
}
|
||||
Reference in New Issue
Block a user