refactor: Update response format for backward compatibility

This commit is contained in:
Peifan Li
2025-12-14 23:55:55 -05:00
parent 4e0dd4cd8c
commit e82ead6d60
7 changed files with 127 additions and 95 deletions

View File

@@ -5,7 +5,6 @@ import { VIDEOS_DIR } from "../config/paths";
import { ValidationError } from "../errors/DownloadErrors";
import * as storageService from "../services/storageService";
import { logger } from "../utils/logger";
import { successResponse } from "../utils/response";
/**
* Clean up temporary download files (.ytdl, .part)
@@ -67,13 +66,9 @@ export const cleanupTempFiles = async (
// Start cleanup from VIDEOS_DIR
await cleanupDirectory(VIDEOS_DIR);
res.status(200).json(
successResponse(
{
deletedCount,
...(errors.length > 0 && { errors }),
},
`Cleaned up ${deletedCount} temporary files`
)
);
// Return format expected by frontend: { deletedCount, errors? }
res.status(200).json({
deletedCount,
...(errors.length > 0 && { errors }),
});
};

View File

@@ -2,23 +2,26 @@ import { Request, Response } from "express";
import { NotFoundError, ValidationError } from "../errors/DownloadErrors";
import * as storageService from "../services/storageService";
import { Collection } from "../services/storageService";
import { successMessage, successResponse } from "../utils/response";
import { successMessage } from "../utils/response";
/**
* Get all collections
* Errors are automatically handled by asyncHandler middleware
* Note: Returns array directly for backward compatibility with frontend
*/
export const getCollections = async (
_req: Request,
res: Response
): Promise<void> => {
const collections = storageService.getCollections();
res.json(successResponse(collections));
// Return array directly for backward compatibility (frontend expects response.data to be Collection[])
res.json(collections);
};
/**
* Create a new collection
* Errors are automatically handled by asyncHandler middleware
* Note: Returns collection object directly for backward compatibility with frontend
*/
export const createCollection = async (
req: Request,
@@ -49,19 +52,20 @@ export const createCollection = async (
videoId
);
if (updatedCollection) {
res
.status(201)
.json(successResponse(updatedCollection, "Collection created"));
// Return collection object directly for backward compatibility
res.status(201).json(updatedCollection);
return;
}
}
res.status(201).json(successResponse(newCollection, "Collection created"));
// Return collection object directly for backward compatibility
res.status(201).json(newCollection);
};
/**
* Update a collection
* Errors are automatically handled by asyncHandler middleware
* Note: Returns collection object directly for backward compatibility with frontend
*/
export const updateCollection = async (
req: Request,
@@ -102,7 +106,8 @@ export const updateCollection = async (
throw new NotFoundError("Collection", id);
}
res.json(successResponse(updatedCollection));
// Return collection object directly for backward compatibility
res.json(updatedCollection);
};
/**

View File

@@ -1,7 +1,7 @@
import { Request, Response } from "express";
import downloadManager from "../services/downloadManager";
import * as storageService from "../services/storageService";
import { successMessage, successResponse } from "../utils/response";
import { successMessage } from "../utils/response";
/**
* Cancel a download
@@ -44,13 +44,15 @@ export const clearQueue = async (
/**
* Get download history
* Errors are automatically handled by asyncHandler middleware
* Note: Returns array directly for backward compatibility with frontend
*/
export const getDownloadHistory = async (
_req: Request,
res: Response
): Promise<void> => {
const history = storageService.getDownloadHistory();
res.status(200).json(successResponse(history));
// Return array directly for backward compatibility (frontend expects response.data to be DownloadHistoryItem[])
res.status(200).json(history);
};
/**

View File

@@ -234,5 +234,6 @@ export const scanFiles = async (
const message = `Scan complete. Added ${addedCount} new videos. Deleted ${deletedCount} missing videos.`;
logger.info(message);
res.status(200).json(successResponse({ addedCount, deletedCount }, message));
// Return format expected by frontend: { addedCount, deletedCount }
res.status(200).json({ addedCount, deletedCount });
};

View File

@@ -11,7 +11,7 @@ import { NotFoundError, ValidationError } from "../errors/DownloadErrors";
import downloadManager from "../services/downloadManager";
import * as storageService from "../services/storageService";
import { logger } from "../utils/logger";
import { successMessage, successResponse } from "../utils/response";
import { successMessage } from "../utils/response";
interface Settings {
loginEnabled: boolean;
@@ -57,6 +57,7 @@ const defaultSettings: Settings = {
/**
* Get application settings
* Errors are automatically handled by asyncHandler middleware
* Note: Returns data directly for backward compatibility with frontend
*/
export const getSettings = async (
_req: Request,
@@ -67,7 +68,8 @@ export const getSettings = async (
// If empty (first run), save defaults
if (Object.keys(settings).length === 0) {
storageService.saveSettings(defaultSettings);
res.json(successResponse(defaultSettings));
// Return data directly for backward compatibility
res.json(defaultSettings);
return;
}
@@ -76,7 +78,8 @@ export const getSettings = async (
// Do not send the hashed password to the frontend
const { password, ...safeSettings } = mergedSettings;
res.json(successResponse({ ...safeSettings, isPasswordSet: !!password }));
// Return data directly for backward compatibility
res.json({ ...safeSettings, isPasswordSet: !!password });
};
/**
@@ -89,7 +92,8 @@ export const migrateData = async (
): Promise<void> => {
const { runMigration } = await import("../services/migrationService");
const results = await runMigration();
res.json(successResponse(results, "Migration completed"));
// Return format expected by frontend: { results: {...} }
res.json({ results });
};
/**
@@ -128,7 +132,8 @@ export const deleteLegacyData = async (
}
}
res.json(successResponse(results, "Legacy data deletion completed"));
// Return format expected by frontend: { results: { deleted: [], failed: [] } }
res.json({ results });
};
/**
@@ -140,7 +145,8 @@ export const formatFilenames = async (
res: Response
): Promise<void> => {
const results = storageService.formatLegacyFilenames();
res.json(successResponse(results, "Filenames formatted"));
// Return format expected by frontend: { results: {...} }
res.json({ results });
};
/**
@@ -236,9 +242,11 @@ export const updateSettings = async (
// Apply settings immediately where possible
downloadManager.setMaxConcurrentDownloads(newSettings.maxConcurrentDownloads);
res.json(
successResponse({ ...newSettings, password: undefined }, "Settings updated")
);
// Return format expected by frontend: { success: true, settings: {...} }
res.json({
success: true,
settings: { ...newSettings, password: undefined },
});
};
/**
@@ -255,7 +263,8 @@ export const getPasswordEnabled = async (
// Return true only if login is enabled AND a password is set
const isEnabled = mergedSettings.loginEnabled && !!mergedSettings.password;
res.json(successResponse({ enabled: isEnabled }));
// Return format expected by frontend: { enabled: boolean }
res.json({ enabled: isEnabled });
};
/**
@@ -272,20 +281,23 @@ export const verifyPassword = async (
const mergedSettings = { ...defaultSettings, ...settings };
if (!mergedSettings.loginEnabled) {
res.json(successResponse({ verified: true }));
// Return format expected by frontend: { success: boolean }
res.json({ success: true });
return;
}
if (!mergedSettings.password) {
// If no password set but login enabled, allow access
res.json(successResponse({ verified: true }));
// Return format expected by frontend: { success: boolean }
res.json({ success: true });
return;
}
const isMatch = await bcrypt.compare(password, mergedSettings.password);
if (isMatch) {
res.json(successResponse({ verified: true }));
// Return format expected by frontend: { success: boolean }
res.json({ success: true });
} else {
throw new ValidationError("Incorrect password", "password");
}
@@ -332,7 +344,8 @@ export const checkCookies = async (
const { DATA_DIR } = require("../config/paths");
const cookiesPath = path.join(DATA_DIR, "cookies.txt");
const exists = fs.existsSync(cookiesPath);
res.json(successResponse({ exists }));
// Return format expected by frontend: { exists: boolean }
res.json({ exists });
};
/**

View File

@@ -2,7 +2,7 @@ import { Request, Response } from "express";
import { ValidationError } from "../errors/DownloadErrors";
import { subscriptionService } from "../services/subscriptionService";
import { logger } from "../utils/logger";
import { successMessage, successResponse } from "../utils/response";
import { successMessage } from "../utils/response";
/**
* Create a new subscription
@@ -23,19 +23,22 @@ export const createSubscription = async (
url,
parseInt(interval)
);
res.status(201).json(successResponse(subscription, "Subscription created"));
// Return subscription object directly for backward compatibility
res.status(201).json(subscription);
};
/**
* Get all subscriptions
* Errors are automatically handled by asyncHandler middleware
* Note: Returns array directly for backward compatibility with frontend
*/
export const getSubscriptions = async (
req: Request,
res: Response
): Promise<void> => {
const subscriptions = await subscriptionService.listSubscriptions();
res.json(successResponse(subscriptions));
// Return array directly for backward compatibility (frontend expects response.data to be Subscription[])
res.json(subscriptions);
};
/**

View File

@@ -38,6 +38,7 @@ export const upload = multer({ storage: storage });
/**
* Search for videos
* Errors are automatically handled by asyncHandler middleware
* Note: Returns { results } format for backward compatibility with frontend
*/
export const searchVideos = async (
req: Request,
@@ -57,7 +58,8 @@ export const searchVideos = async (
limit,
offset
);
res.status(200).json(successResponse({ results }));
// Return { results } format for backward compatibility (frontend expects response.data.results)
res.status(200).json({ results });
};
/**
@@ -85,7 +87,8 @@ export const checkVideoDownloadStatus = async (
const { id: sourceVideoId, platform } = extractSourceVideoId(videoUrl);
if (!sourceVideoId) {
res.status(200).json(successResponse({ found: false }));
// Return object directly for backward compatibility (frontend expects response.data.found)
res.status(200).json({ found: false });
return;
}
@@ -100,47 +103,45 @@ export const checkVideoDownloadStatus = async (
if (!existingVideo) {
// Video was deleted but not marked in download history, update it
storageService.markVideoDownloadDeleted(downloadCheck.videoId);
res.status(200).json(
successResponse({
found: true,
status: "deleted",
title: downloadCheck.title,
author: downloadCheck.author,
downloadedAt: downloadCheck.downloadedAt,
})
);
// Return object directly for backward compatibility
res.status(200).json({
found: true,
status: "deleted",
title: downloadCheck.title,
author: downloadCheck.author,
downloadedAt: downloadCheck.downloadedAt,
});
return;
}
res.status(200).json(
successResponse({
found: true,
status: "exists",
videoId: downloadCheck.videoId,
title: downloadCheck.title || existingVideo.title,
author: downloadCheck.author || existingVideo.author,
downloadedAt: downloadCheck.downloadedAt,
videoPath: existingVideo.videoPath,
thumbnailPath: existingVideo.thumbnailPath,
})
);
// Return object directly for backward compatibility
res.status(200).json({
found: true,
status: "exists",
videoId: downloadCheck.videoId,
title: downloadCheck.title || existingVideo.title,
author: downloadCheck.author || existingVideo.author,
downloadedAt: downloadCheck.downloadedAt,
videoPath: existingVideo.videoPath,
thumbnailPath: existingVideo.thumbnailPath,
});
return;
}
res.status(200).json(
successResponse({
found: true,
status: downloadCheck.status,
title: downloadCheck.title,
author: downloadCheck.author,
downloadedAt: downloadCheck.downloadedAt,
deletedAt: downloadCheck.deletedAt,
})
);
// Return object directly for backward compatibility
res.status(200).json({
found: true,
status: downloadCheck.status,
title: downloadCheck.title,
author: downloadCheck.author,
downloadedAt: downloadCheck.downloadedAt,
deletedAt: downloadCheck.deletedAt,
});
return;
}
res.status(200).json(successResponse({ found: false }));
// Return object directly for backward compatibility
res.status(200).json({ found: false });
};
// Download video
@@ -476,18 +477,21 @@ export const downloadVideo = async (
/**
* Get all videos
* Errors are automatically handled by asyncHandler middleware
* Note: Returns array directly for backward compatibility with frontend
*/
export const getVideos = async (
_req: Request,
res: Response
): Promise<void> => {
const videos = storageService.getVideos();
res.status(200).json(successResponse(videos));
// Return array directly for backward compatibility (frontend expects response.data to be Video[])
res.status(200).json(videos);
};
/**
* Get video by ID
* Errors are automatically handled by asyncHandler middleware
* Note: Returns video object directly for backward compatibility with frontend
*/
export const getVideoById = async (
req: Request,
@@ -500,7 +504,8 @@ export const getVideoById = async (
throw new NotFoundError("Video", id);
}
res.status(200).json(successResponse(video));
// Return video object directly for backward compatibility (frontend expects response.data to be Video)
res.status(200).json(video);
};
/**
@@ -524,6 +529,7 @@ export const deleteVideo = async (
/**
* Get download status
* Errors are automatically handled by asyncHandler middleware
* Note: Returns status object directly for backward compatibility with frontend
*/
export const getDownloadStatus = async (
_req: Request,
@@ -540,7 +546,8 @@ export const getDownloadStatus = async (
}
});
}
res.status(200).json(successResponse(status));
// Return status object directly for backward compatibility (frontend expects response.data to be DownloadStatus)
res.status(200).json(status);
};
/**
@@ -580,7 +587,8 @@ export const checkBilibiliParts = async (
const result = await downloadService.checkBilibiliVideoParts(videoId);
res.status(200).json(successResponse(result));
// Return result object directly for backward compatibility (frontend expects response.data.success, response.data.videosNumber)
res.status(200).json(result);
};
/**
@@ -621,12 +629,14 @@ export const checkBilibiliCollection = async (
// Check if it's a collection or series
const result = await downloadService.checkBilibiliCollectionOrSeries(videoId);
res.status(200).json(successResponse(result));
// Return result object directly for backward compatibility (frontend expects response.data.success, response.data.type)
res.status(200).json(result);
};
/**
* Get video comments
* Errors are automatically handled by asyncHandler middleware
* Note: Returns comments array directly for backward compatibility with frontend
*/
export const getVideoComments = async (
req: Request,
@@ -636,7 +646,8 @@ export const getVideoComments = async (
const comments = await import("../services/commentService").then((m) =>
m.getComments(id)
);
res.status(200).json(successResponse(comments));
// Return comments array directly for backward compatibility (frontend expects response.data to be Comment[])
res.status(200).json(comments);
};
/**
@@ -741,9 +752,11 @@ export const rateVideo = async (req: Request, res: Response): Promise<void> => {
throw new NotFoundError("Video", id);
}
res
.status(200)
.json(successResponse({ video: updatedVideo }, "Video rated successfully"));
// Return format expected by frontend: { success: true, video: ... }
res.status(200).json({
success: true,
video: updatedVideo,
});
};
/**
@@ -773,11 +786,11 @@ export const updateVideoDetails = async (
throw new NotFoundError("Video", id);
}
res
.status(200)
.json(
successResponse({ video: updatedVideo }, "Video updated successfully")
);
// Return format expected by frontend: { success: true, video: ... }
res.status(200).json({
success: true,
video: updatedVideo,
});
};
/**
@@ -864,11 +877,11 @@ export const refreshThumbnail = async (
// Return success with timestamp to bust cache
const thumbnailUrl = `${newThumbnailPath}?t=${Date.now()}`;
res
.status(200)
.json(
successResponse({ thumbnailUrl }, "Thumbnail refreshed successfully")
);
// Return format expected by frontend: { success: true, thumbnailUrl: ... }
res.status(200).json({
success: true,
thumbnailUrl,
});
};
/**
@@ -892,11 +905,11 @@ export const incrementViewCount = async (
lastPlayedAt: Date.now(),
});
res.status(200).json(
successResponse({
viewCount: updatedVideo?.viewCount,
})
);
// Return format expected by frontend: { success: true, viewCount: ... }
res.status(200).json({
success: true,
viewCount: updatedVideo?.viewCount,
});
};
/**