Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update asset.ts - fix file path translation from Windows > Linux #9253

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
40 changes: 24 additions & 16 deletions cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
},
"dependencies": {
"fast-glob": "^3.3.2",
"lodash-es": "^4.17.21"
"lodash-es": "^4.17.21",
"upath": "^2.0.1"
},
"volta": {
"node": "20.13.1"
Expand Down
77 changes: 57 additions & 20 deletions cli/src/commands/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { stat, unlink } from 'node:fs/promises';
import os from 'node:os';
import path, { basename } from 'node:path';
import { BaseOptions, authenticate, crawl, sha1 } from 'src/utils';
import * as upath from 'upath';

const s = (count: number) => (count === 1 ? '' : 's');

Expand All @@ -37,26 +38,30 @@ interface UploadOptionsDto {
}

class UploadFile extends File {
constructor(
private filepath: string,
private _size: number,
) {
super([], basename(filepath));
private filepath: string;
private _size: number;

constructor(filepath: string, size: number) {
super([], upath.basename(filepath)); // Call super() before using `this`
this.filepath = upath.toUnix(upath.normalize(filepath));
this._size = size;
}

get size() {
return this._size;
}

stream() {
return createReadStream(this.filepath) as any;
return createReadStream(upath.toUnix(this.filepath)) as any;
}
}

export const upload = async (paths: string[], baseOptions: BaseOptions, options: UploadOptionsDto) => {
await authenticate(baseOptions);
// Normalizing paths before scanning
const normalizedPaths = paths.map((path) => upath.toUnix(upath.normalize(path)));
const scanFiles = await scan(normalizedPaths, options);

const scanFiles = await scan(paths, options);
if (scanFiles.length === 0) {
console.log('No files found, exiting');
return;
Expand Down Expand Up @@ -90,7 +95,9 @@ const checkForDuplicates = async (files: string[], { concurrency, skipHash }: Up
}

const progressBar = new SingleBar(
{ format: 'Checking files | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets' },
{
format: 'Checking files | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets',
},
Presets.shades_classic,
);

Expand All @@ -102,8 +109,15 @@ const checkForDuplicates = async (files: string[], { concurrency, skipHash }: Up
try {
// TODO refactor into a queue
for (const items of chunk(files, concurrency)) {
const dto = await Promise.all(items.map(async (filepath) => ({ id: filepath, checksum: await sha1(filepath) })));
const { results } = await checkBulkUpload({ assetBulkUploadCheckDto: { assets: dto } });
const dto = await Promise.all(
items.map(async (filepath) => ({
id: filepath,
checksum: await sha1(filepath),
})),
);
const { results } = await checkBulkUpload({
assetBulkUploadCheckDto: { assets: dto },
});

for (const { id: filepath, assetId, action } of results as AssetBulkUploadCheckResults) {
if (action === Action.Accept) {
Expand Down Expand Up @@ -145,11 +159,16 @@ const uploadFiles = async (files: string[], { dryRun, concurrency }: UploadOptio
}

const uploadProgress = new SingleBar(
{ format: 'Uploading assets | {bar} | {percentage}% | ETA: {eta_formatted} | {value_formatted}/{total_formatted}' },
{
format: 'Uploading assets | {bar} | {percentage}% | ETA: {eta_formatted} | {value_formatted}/{total_formatted}',
},
Presets.shades_classic,
);
uploadProgress.start(totalSize, 0);
uploadProgress.update({ value_formatted: 0, total_formatted: byteSize(totalSize) });
uploadProgress.update({
value_formatted: 0,
total_formatted: byteSize(totalSize),
});

let duplicateCount = 0;
let duplicateSize = 0;
Expand All @@ -175,7 +194,9 @@ const uploadFiles = async (files: string[], { dryRun, concurrency }: UploadOptio
successSize += stats.size ?? 0;
}

uploadProgress.update(successSize, { value_formatted: byteSize(successSize + duplicateSize) });
uploadProgress.update(successSize, {
value_formatted: byteSize(successSize + duplicateSize),
});

return response;
}),
Expand Down Expand Up @@ -251,7 +272,9 @@ const deleteFiles = async (files: string[], options: UploadOptionsDto): Promise<
console.log('Deleting assets that have been uploaded...');

const deletionProgress = new SingleBar(
{ format: 'Deleting local assets | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets' },
{
format: 'Deleting local assets | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets',
},
Presets.shades_classic,
);
deletionProgress.start(files.length, 0);
Expand All @@ -273,7 +296,7 @@ const updateAlbums = async (assets: Asset[], options: UploadOptionsDto) => {
const { dryRun, concurrency } = options;

const albums = await getAllAlbums({});
const existingAlbums = new Map(albums.map((album) => [album.albumName, album.id]));
const existingAlbums = new Map(albums.map((album: { albumName: string; id: string }) => [album.albumName, album.id]));
const newAlbums: Set<string> = new Set();
for (const { filepath } of assets) {
const albumName = getAlbumName(filepath, options);
Expand All @@ -290,7 +313,9 @@ const updateAlbums = async (assets: Asset[], options: UploadOptionsDto) => {
}

const progressBar = new SingleBar(
{ format: 'Creating albums | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} albums' },
{
format: 'Creating albums | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} albums',
},
Presets.shades_classic,
);
progressBar.start(newAlbums.size, 0);
Expand All @@ -313,30 +338,42 @@ const updateAlbums = async (assets: Asset[], options: UploadOptionsDto) => {
console.log(`Successfully updated ${assets.length} asset${s(assets.length)}`);

const albumToAssets = new Map<string, string[]>();

for (const asset of assets) {
const albumName = getAlbumName(asset.filepath, options);
if (!albumName) {
continue;
}
const albumId = existingAlbums.get(albumName);
const albumId = String(existingAlbums.get(albumName));
if (albumId) {
if (!albumToAssets.has(albumId)) {
albumToAssets.set(albumId, []);
}
albumToAssets.get(albumId)?.push(asset.id);
// Ensure asset.id is a string and albumId is not undefined
if (typeof asset.id === 'string' && typeof albumId === 'string') {
albumToAssets.get(albumId)?.push(asset.id);
}
} else {
// Handle the case where albumId is undefined
console.error(`Album ID for album name "${albumName}" not found.`);
}
}

const albumUpdateProgress = new SingleBar(
{ format: 'Adding assets to albums | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets' },
{
format: 'Adding assets to albums | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets',
},
Presets.shades_classic,
);
albumUpdateProgress.start(assets.length, 0);

try {
for (const [albumId, assets] of albumToAssets.entries()) {
for (const assetBatch of chunk(assets, Math.min(1000 * concurrency, 65_000))) {
await addAssetsToAlbum({ id: albumId, bulkIdsDto: { ids: assetBatch } });
await addAssetsToAlbum({
id: albumId,
bulkIdsDto: { ids: assetBatch },
});
albumUpdateProgress.increment(assetBatch.length);
}
}
Expand Down
9 changes: 7 additions & 2 deletions cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
"skipLibCheck": true,
"esModuleInterop": true,
"baseUrl": "./",
"types": ["vitest/globals"]
"types": [
"vitest/globals"
]
},
"exclude": ["dist", "node_modules"]
"exclude": [
"dist",
"node_modules"
]
}