import fs from "fs" import { basename } from "node:path" class UploadFile extends File { constructor( private filepath: string, //private _size: number ) { super([], basename(filepath)) } //get size() { // return this._size //} stream() { return fs.createReadStream(this.filepath) } } const apiKeySockenklaus = "bxmlNuUlPiY4YRRIcRuJ5eflZCU7aOHQPOgGmts4f8" const apiKeyTestUser = "xOUY572Rd6Fw3qoE3EFBFy3G3sITaIQsgPPc8TAbQ" const settings = { apiBaseUrl: "https://immich.sockenklaus.duckdns.org/api/", apiKey: apiKeyTestUser, immichMount: "/immich/" } const args = { tdarrPath: "/immich/6c0138fb-62c4-4a99-aeed-721a845f90f3/2025/07/SampleVideo_1280x720_10mb.mp4", newFile: "./SampleVideo_1280x720_30mb.mp4" } const originalPath = args.tdarrPath.replace(settings.immichMount, "") const headers = { "Accept": "application/json", "x-api-key": settings.apiKey } async function getAssetByOriginalPath(path) { const searchRequest = new Request(settings.apiBaseUrl + "search/metadata", { method: "POST", body: JSON.stringify({ originalPath: path }), headers: headers }) try { const responsePromise = await fetch(searchRequest) const responseBody = await responsePromise.json() if(responseBody.assets.total !== 1) { console.error(`Found ${body.assets.total} assets, stopping here`) return {} } return { id: responseBody.assets.items[0].id, deviceAssetId: responseBody.assets.items[0].deviceAssetId, deviceId: responseBody.assets.items[0].deviceId, fileCreatedAt: responseBody.assets.items[0].fileCreatedAt, fileModifiedAt: responseBody.assets.items[0].fileModifiedAt } } catch(error) { console.error(error) } } async function replaceAsset(assetInfo, newFilePath) { const file = new UploadFile(newFilePath) const uploadData = new FormData() uploadData.set("assetData", file) uploadData.set("deviceAssetId", assetInfo.deviceAssetId) uploadData.set("deviceId", assetInfo.deviceId) uploadData.set("fileCreatedAt", assetInfo.fileCreatedAt) uploadData.set("fileModifiedAt", assetInfo.fileModifiedAt) const replaceRequest = new Request(settings.apiBaseUrl + "assets/"+assetInfo.id+"/original", { method: "PUT", headers: { "Accept": "application/json", "x-api-key": settings.apiKey }, body: uploadData }) const responsePromise = await fetch(replaceRequest) const responseBody = await responsePromise.json() return responseBody } const assetInfo = await getAssetByOriginalPath(originalPath) console.log(JSON.stringify(assetInfo, undefined, 2)) console.log(replaceAsset(assetInfo, args.newFile)) /* fetch(searchRequest) .then(response => { if(!response.ok){ throw new Error(`Search failed: ${response.status} - ${response.statusText}`) } return response.json() }) .then(body => { console.log(JSON.stringify(body, undefined, 2)) if (body.assets.total !== 1) { console.log(`Found ${body.assets.total} assets, stopping here`) return Promise.resolve(null) } const id = body.assets.items[0].id const file = fs.readFileSync(args.newFile) const uploadData = new FormData() uploadData.set("assetData", file) uploadData.set("deviceAssetId", body.assets.items[0].deviceAssetId) uploadData.set("deviceId", body.assets.items[0].deviceId) uploadData.set("fileCreatedAt", body.assets.items[0].fileCreatedAt) uploadData.set("fileModifiedAt", body.assets.items[0].fileModifiedAt) //console.dir(uploadData.get("assetData")) const replaceRequest = new Request(settings.apiBaseUrl + "assets/"+id+"/original", { method: "PUT", headers: { "Accept": "application/json", "x-api-key": settings.apiKey }, body: uploadData }) return fetch(replaceRequest) }) .then(putResponse => { if(!putResponse) { throw new Error(`No put request was made`) } if(!putResponse.ok) { throw new Error(`Replace failed: ${putResponse.status} - ${putResponse.statusText}`) } return putResponse.json() }) .then(replaceBody => { console.log(JSON.stringify(replaceBody, undefined, 2)) }) .catch(error => console.log(error)) */ //await fetch(replaceRequest) // .then(response => response.json())