@gallop.software/studio 2.3.43 → 2.3.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1758,7 +1758,21 @@ async function handleMoveStream(request) {
1758
1758
  }
1759
1759
  return count;
1760
1760
  };
1761
- totalFiles += await countFilesRecursive(absolutePath);
1761
+ const localFileCount = await countFilesRecursive(absolutePath);
1762
+ const folderPrefix = oldKey + "/";
1763
+ let cloudOnlyCount = 0;
1764
+ for (const metaKey of Object.keys(meta)) {
1765
+ if (metaKey.startsWith(folderPrefix)) {
1766
+ const relPath = metaKey.slice(folderPrefix.length);
1767
+ const localPath = path6.join(absolutePath, relPath);
1768
+ try {
1769
+ await fs6.access(localPath);
1770
+ } catch {
1771
+ cloudOnlyCount++;
1772
+ }
1773
+ }
1774
+ }
1775
+ totalFiles += localFileCount + cloudOnlyCount;
1762
1776
  expandedItems.push({ itemPath, safePath, itemName, oldKey, newKey, newAbsolutePath, isVirtualFolder: false });
1763
1777
  } else if (!hasLocalItem) {
1764
1778
  const folderPrefix = oldKey + "/";
@@ -1979,74 +1993,187 @@ async function handleMoveStream(request) {
1979
1993
  continue;
1980
1994
  } catch {
1981
1995
  }
1982
- await fs6.rename(absolutePath, newAbsolutePath);
1983
- const stats = await fs6.stat(newAbsolutePath);
1984
- if (stats.isFile() && isImage && entry) {
1985
- const oldThumbPaths = getAllThumbnailPaths(oldKey);
1986
- const newThumbPaths = getAllThumbnailPaths(newKey);
1987
- for (let j = 0; j < oldThumbPaths.length; j++) {
1988
- const oldThumbPath = getPublicPath(oldThumbPaths[j]);
1989
- const newThumbPath = getPublicPath(newThumbPaths[j]);
1990
- try {
1991
- await fs6.access(oldThumbPath);
1992
- sourceFolders.add(path6.dirname(oldThumbPath));
1993
- await fs6.mkdir(path6.dirname(newThumbPath), { recursive: true });
1994
- await fs6.rename(oldThumbPath, newThumbPath);
1995
- } catch {
1996
+ const stats = await fs6.stat(absolutePath);
1997
+ if (stats.isFile()) {
1998
+ await fs6.mkdir(path6.dirname(newAbsolutePath), { recursive: true });
1999
+ await fs6.rename(absolutePath, newAbsolutePath);
2000
+ if (isImage && entry) {
2001
+ const oldThumbPaths = getAllThumbnailPaths(oldKey);
2002
+ const newThumbPaths = getAllThumbnailPaths(newKey);
2003
+ for (let j = 0; j < oldThumbPaths.length; j++) {
2004
+ const oldThumbPath = getPublicPath(oldThumbPaths[j]);
2005
+ const newThumbPath = getPublicPath(newThumbPaths[j]);
2006
+ try {
2007
+ await fs6.access(oldThumbPath);
2008
+ sourceFolders.add(path6.dirname(oldThumbPath));
2009
+ await fs6.mkdir(path6.dirname(newThumbPath), { recursive: true });
2010
+ await fs6.rename(oldThumbPath, newThumbPath);
2011
+ } catch {
2012
+ }
2013
+ }
2014
+ const fileIsInCloud = entry.c !== void 0;
2015
+ const fileCdnUrl2 = fileIsInCloud ? cdnUrls[entry.c] : void 0;
2016
+ const fileIsInR2 = fileIsInCloud && fileCdnUrl2 === r2PublicUrl;
2017
+ const fileHasThumbs = isProcessed(entry);
2018
+ if (fileIsInR2) {
2019
+ await deleteFromCdn(oldKey, fileHasThumbs);
2020
+ await uploadOriginalToCdn(newKey);
2021
+ if (fileHasThumbs) {
2022
+ await uploadToCdn(newKey);
2023
+ }
1996
2024
  }
2025
+ delete meta[oldKey];
2026
+ meta[newKey] = entry;
1997
2027
  }
1998
- delete meta[oldKey];
1999
- meta[newKey] = entry;
2028
+ processedFiles++;
2029
+ sendEvent({
2030
+ type: "progress",
2031
+ current: processedFiles,
2032
+ total: totalFiles,
2033
+ moved: moved.length,
2034
+ percent: Math.round(processedFiles / totalFiles * 100),
2035
+ currentFile: itemName
2036
+ });
2037
+ moved.push(itemPath);
2000
2038
  } else if (stats.isDirectory()) {
2001
2039
  const oldPrefix = oldKey + "/";
2002
2040
  const newPrefix = newKey + "/";
2003
- for (const key of Object.keys(meta)) {
2004
- if (key.startsWith(oldPrefix)) {
2005
- const newMetaKey = newPrefix + key.slice(oldPrefix.length);
2006
- meta[newMetaKey] = meta[key];
2007
- delete meta[key];
2008
- }
2009
- }
2010
- const oldThumbRelPath = oldKey.slice(1);
2011
- const newThumbRelPath = newKey.slice(1);
2012
- const imagesDir = getPublicPath("images");
2013
- const oldThumbFolder = path6.join(imagesDir, oldThumbRelPath);
2014
- const newThumbFolder = path6.join(imagesDir, newThumbRelPath);
2015
- try {
2016
- await fs6.access(oldThumbFolder);
2017
- sourceFolders.add(oldThumbFolder);
2018
- await fs6.mkdir(path6.dirname(newThumbFolder), { recursive: true });
2019
- await fs6.rename(oldThumbFolder, newThumbFolder);
2020
- } catch {
2021
- }
2022
- }
2023
- moved.push(itemPath);
2024
- if (stats.isDirectory()) {
2025
- const countFilesInDir = async (dir) => {
2026
- let count = 0;
2041
+ const localFiles = [];
2042
+ const collectLocalFiles = async (dir, relativeDir) => {
2027
2043
  const entries = await fs6.readdir(dir, { withFileTypes: true });
2028
- for (const entry2 of entries) {
2029
- if (entry2.isDirectory()) {
2030
- count += await countFilesInDir(path6.join(dir, entry2.name));
2044
+ for (const dirEntry of entries) {
2045
+ const entryRelPath = relativeDir ? `${relativeDir}/${dirEntry.name}` : dirEntry.name;
2046
+ if (dirEntry.isDirectory()) {
2047
+ await collectLocalFiles(path6.join(dir, dirEntry.name), entryRelPath);
2031
2048
  } else {
2032
- count++;
2049
+ localFiles.push({ relativePath: entryRelPath, isImage: isImageFile(dirEntry.name) });
2033
2050
  }
2034
2051
  }
2035
- return count;
2036
2052
  };
2037
- const filesInDir = await countFilesInDir(newAbsolutePath);
2038
- processedFiles += filesInDir;
2039
- } else {
2040
- processedFiles++;
2053
+ await collectLocalFiles(absolutePath, "");
2054
+ const cloudOnlyFiles = [];
2055
+ for (const [metaKey, metaEntry] of Object.entries(meta)) {
2056
+ if (metaKey.startsWith(oldPrefix) && metaEntry && typeof metaEntry === "object") {
2057
+ const relPath = metaKey.slice(oldPrefix.length);
2058
+ const localPath = path6.join(absolutePath, relPath);
2059
+ try {
2060
+ await fs6.access(localPath);
2061
+ } catch {
2062
+ cloudOnlyFiles.push({
2063
+ oldKey: metaKey,
2064
+ newKey: newPrefix + relPath,
2065
+ entry: metaEntry
2066
+ });
2067
+ }
2068
+ }
2069
+ }
2070
+ for (const localFile of localFiles) {
2071
+ const fileOldPath = path6.join(absolutePath, localFile.relativePath);
2072
+ const fileNewPath = path6.join(newAbsolutePath, localFile.relativePath);
2073
+ const fileOldKey = oldPrefix + localFile.relativePath;
2074
+ const fileNewKey = newPrefix + localFile.relativePath;
2075
+ const fileEntry = meta[fileOldKey];
2076
+ sourceFolders.add(path6.dirname(fileOldPath));
2077
+ await fs6.mkdir(path6.dirname(fileNewPath), { recursive: true });
2078
+ await fs6.rename(fileOldPath, fileNewPath);
2079
+ if (localFile.isImage && fileEntry) {
2080
+ const oldThumbPaths = getAllThumbnailPaths(fileOldKey);
2081
+ const newThumbPaths = getAllThumbnailPaths(fileNewKey);
2082
+ for (let t = 0; t < oldThumbPaths.length; t++) {
2083
+ const oldThumbPath = getPublicPath(oldThumbPaths[t]);
2084
+ const newThumbPath = getPublicPath(newThumbPaths[t]);
2085
+ try {
2086
+ await fs6.access(oldThumbPath);
2087
+ sourceFolders.add(path6.dirname(oldThumbPath));
2088
+ await fs6.mkdir(path6.dirname(newThumbPath), { recursive: true });
2089
+ await fs6.rename(oldThumbPath, newThumbPath);
2090
+ } catch {
2091
+ }
2092
+ }
2093
+ const fileIsInCloud = fileEntry.c !== void 0;
2094
+ const fileCdnUrl2 = fileIsInCloud ? cdnUrls[fileEntry.c] : void 0;
2095
+ const fileIsInR2 = fileIsInCloud && fileCdnUrl2 === r2PublicUrl;
2096
+ const fileHasThumbs = isProcessed(fileEntry);
2097
+ if (fileIsInR2) {
2098
+ await deleteFromCdn(fileOldKey, fileHasThumbs);
2099
+ await uploadOriginalToCdn(fileNewKey);
2100
+ if (fileHasThumbs) {
2101
+ await uploadToCdn(fileNewKey);
2102
+ }
2103
+ }
2104
+ delete meta[fileOldKey];
2105
+ meta[fileNewKey] = fileEntry;
2106
+ }
2107
+ processedFiles++;
2108
+ sendEvent({
2109
+ type: "progress",
2110
+ current: processedFiles,
2111
+ total: totalFiles,
2112
+ moved: moved.length,
2113
+ percent: Math.round(processedFiles / totalFiles * 100),
2114
+ currentFile: path6.basename(localFile.relativePath)
2115
+ });
2116
+ }
2117
+ for (const cloudFile of cloudOnlyFiles) {
2118
+ const cloudEntry = cloudFile.entry;
2119
+ const cloudIsInCloud = cloudEntry.c !== void 0;
2120
+ const cloudCdnUrl = cloudIsInCloud ? cdnUrls[cloudEntry.c] : void 0;
2121
+ const cloudIsInR2 = cloudIsInCloud && cloudCdnUrl === r2PublicUrl;
2122
+ const cloudHasThumbs = isProcessed(cloudEntry);
2123
+ if (cloudIsInR2) {
2124
+ try {
2125
+ const cloudLocalPath = getPublicPath(cloudFile.newKey);
2126
+ const buffer = await downloadFromCdn(cloudFile.oldKey);
2127
+ await fs6.mkdir(path6.dirname(cloudLocalPath), { recursive: true });
2128
+ await fs6.writeFile(cloudLocalPath, buffer);
2129
+ if (cloudHasThumbs) {
2130
+ const oldThumbPaths = getAllThumbnailPaths(cloudFile.oldKey);
2131
+ const newThumbPaths = getAllThumbnailPaths(cloudFile.newKey);
2132
+ for (let t = 0; t < oldThumbPaths.length; t++) {
2133
+ try {
2134
+ const thumbBuffer = await downloadFromCdn(oldThumbPaths[t]);
2135
+ const newThumbLocalPath = getPublicPath(newThumbPaths[t]);
2136
+ await fs6.mkdir(path6.dirname(newThumbLocalPath), { recursive: true });
2137
+ await fs6.writeFile(newThumbLocalPath, thumbBuffer);
2138
+ } catch {
2139
+ }
2140
+ }
2141
+ }
2142
+ await deleteFromCdn(cloudFile.oldKey, cloudHasThumbs);
2143
+ await uploadOriginalToCdn(cloudFile.newKey);
2144
+ if (cloudHasThumbs) {
2145
+ await uploadToCdn(cloudFile.newKey);
2146
+ }
2147
+ try {
2148
+ await fs6.unlink(cloudLocalPath);
2149
+ } catch {
2150
+ }
2151
+ if (cloudHasThumbs) {
2152
+ await deleteLocalThumbnails(cloudFile.newKey);
2153
+ }
2154
+ await deleteEmptyFolders(path6.dirname(cloudLocalPath));
2155
+ } catch (err) {
2156
+ console.error(`Failed to move cloud file ${cloudFile.oldKey}:`, err);
2157
+ }
2158
+ }
2159
+ delete meta[cloudFile.oldKey];
2160
+ meta[cloudFile.newKey] = cloudEntry;
2161
+ processedFiles++;
2162
+ sendEvent({
2163
+ type: "progress",
2164
+ current: processedFiles,
2165
+ total: totalFiles,
2166
+ moved: moved.length,
2167
+ percent: Math.round(processedFiles / totalFiles * 100),
2168
+ currentFile: path6.basename(cloudFile.newKey)
2169
+ });
2170
+ }
2171
+ sourceFolders.add(absolutePath);
2172
+ const oldThumbRelPath = oldKey.slice(1);
2173
+ const oldThumbFolder = path6.join(getPublicPath("images"), oldThumbRelPath);
2174
+ sourceFolders.add(oldThumbFolder);
2175
+ moved.push(itemPath);
2041
2176
  }
2042
- sendEvent({
2043
- type: "progress",
2044
- current: processedFiles,
2045
- total: totalFiles,
2046
- moved: moved.length,
2047
- percent: Math.round(processedFiles / totalFiles * 100),
2048
- currentFile: itemName
2049
- });
2050
2177
  }
2051
2178
  } catch (err) {
2052
2179
  console.error(`Failed to move ${itemName}:`, err);