Compare commits

..

No commits in common. "a07ec1f958f009f59deb7c8edbcd75b735be638f" and "39fa5065d23348b55b8aec51aa644d05e50dda79" have entirely different histories.

4 changed files with 29 additions and 82 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "twitch-vod-manager",
"version": "4.5.4",
"version": "4.5.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "twitch-vod-manager",
"version": "4.5.4",
"version": "4.5.3",
"license": "MIT",
"dependencies": {
"axios": "^1.6.0",

View File

@ -1,6 +1,6 @@
{
"name": "twitch-vod-manager",
"version": "4.5.4",
"version": "4.5.3",
"description": "Twitch VOD Manager - Download Twitch VODs easily",
"main": "dist/main.js",
"author": "xRangerDE",

View File

@ -269,16 +269,8 @@ function loadConfig(): Config {
}
function saveConfig(config: Config): void {
const tmpPath = CONFIG_FILE + '.tmp';
try {
fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
try {
fs.renameSync(tmpPath, CONFIG_FILE);
} catch {
// On Windows, rename can fail if target exists in some edge cases
fs.copyFileSync(tmpPath, CONFIG_FILE);
try { fs.unlinkSync(tmpPath); } catch { }
}
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
} catch (e) {
console.error('Error saving config:', e);
}
@ -329,16 +321,8 @@ function writeQueueToDisk(queue: QueueItem[]): void {
return;
}
const tmpPath = QUEUE_FILE + '.tmp';
try {
fs.writeFileSync(tmpPath, JSON.stringify(queue, null, 2));
try {
fs.renameSync(tmpPath, QUEUE_FILE);
} catch {
// On Windows, rename can fail if target exists in some edge cases
fs.copyFileSync(tmpPath, QUEUE_FILE);
try { fs.unlinkSync(tmpPath); } catch { }
}
fs.writeFileSync(QUEUE_FILE, JSON.stringify(queue, null, 2));
} catch (e) {
console.error('Error saving queue:', e);
}
@ -1649,18 +1633,13 @@ async function getVODs(userId: string, forceRefresh = false): Promise<VOD[]> {
if (!(await ensureTwitchAuth())) return await getVodsViaPublicApi();
const MAX_VOD_PAGES = 50; // 50 pages x 100 per page = 5000 VODs max
const fetchVodsPage = async (cursor?: string) => {
const params: Record<string, string | number> = {
user_id: userId,
type: 'archive',
first: 100
};
if (cursor) params.after = cursor;
const fetchVods = async () => {
return await axios.get('https://api.twitch.tv/helix/videos', {
params,
params: {
user_id: userId,
type: 'archive',
first: 100
},
headers: {
'Client-ID': config.client_id,
'Authorization': `Bearer ${accessToken}`
@ -1669,38 +1648,26 @@ async function getVODs(userId: string, forceRefresh = false): Promise<VOD[]> {
});
};
const fetchAllVodPages = async (): Promise<VOD[]> => {
const allVods: VOD[] = [];
let cursor: string | undefined;
let pageCount = 0;
do {
const response = await fetchVodsPage(cursor);
const pageVods = response.data.data || [];
allVods.push(...pageVods);
if (pageCount === 0) {
const login = pageVods[0]?.user_login;
if (login) {
userIdLoginCache.set(userId, normalizeLogin(login));
}
}
cursor = response.data.pagination?.cursor;
pageCount++;
} while (cursor && pageCount < MAX_VOD_PAGES);
return allVods;
};
try {
const vods = await fetchAllVodPages();
const response = await fetchVods();
const vods = response.data.data || [];
const login = vods[0]?.user_login;
if (login) {
userIdLoginCache.set(userId, normalizeLogin(login));
}
setCachedValue(vodListCache, cacheKey, vods, MAX_VOD_LIST_CACHE_ENTRIES);
return vods;
} catch (e) {
if (axios.isAxiosError(e) && e.response?.status === 401 && (await ensureTwitchAuth(true))) {
try {
const vods = await fetchAllVodPages();
const retryResponse = await fetchVods();
const vods = retryResponse.data.data || [];
const login = vods[0]?.user_login;
if (login) {
userIdLoginCache.set(userId, normalizeLogin(login));
}
setCachedValue(vodListCache, cacheKey, vods, MAX_VOD_LIST_CACHE_ENTRIES);
return vods;
} catch (retryError) {
@ -1939,17 +1906,7 @@ async function cutVideo(
proc.on('close', (code) => {
currentProcess = null;
if (code === 0 && fs.existsSync(outputFile)) {
const stats = fs.statSync(outputFile);
if (stats.size <= 256) {
appendDebugLog('cut-video-empty-output', { outputFile, bytes: stats.size });
resolve(false);
return;
}
resolve(true);
} else {
resolve(false);
}
resolve(code === 0 && fs.existsSync(outputFile));
});
proc.on('error', () => {

View File

@ -805,28 +805,17 @@ async function confirmClipDialog(): Promise<void> {
const startSec = parseTimeToSeconds(byId<HTMLInputElement>('clipStartTime').value);
const endSec = parseTimeToSeconds(byId<HTMLInputElement>('clipEndTime').value);
const durationSec = endSec - startSec;
const startPartStr = byId<HTMLInputElement>('clipStartPart').value.trim();
const startPart = startPartStr ? parseInt(startPartStr, 10) : 1;
const filenameFormat = getSelectedFilenameFormat();
const filenameTemplate = byId<HTMLInputElement>('clipFilenameTemplate').value.trim();
if (isNaN(startSec) || isNaN(endSec) || isNaN(durationSec)) {
alert('Invalid time values');
return;
}
if (startSec < 0) {
alert(UI_TEXT.clips.outOfRange);
return;
}
if (durationSec <= 0) {
if (endSec <= startSec) {
alert(UI_TEXT.clips.endBeforeStart);
return;
}
if (endSec > clipTotalSeconds) {
if (startSec < 0 || endSec > clipTotalSeconds) {
alert(UI_TEXT.clips.outOfRange);
return;
}
@ -844,6 +833,7 @@ async function confirmClipDialog(): Promise<void> {
}
}
const durationSec = endSec - startSec;
const customClip: CustomClip = {
startSec,
durationSec,