|
@@ -12,27 +12,22 @@ import {
|
|
|
Query,
|
|
|
Response,
|
|
|
Headers,
|
|
|
- BadRequestException,
|
|
|
+ Delete,
|
|
|
} from '@nestjs/common';
|
|
|
import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard';
|
|
|
import { AssetService } from './asset.service';
|
|
|
-import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
|
|
|
+import { FilesInterceptor } from '@nestjs/platform-express';
|
|
|
import { multerOption } from '../../config/multer-option.config';
|
|
|
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
|
|
|
import { CreateAssetDto } from './dto/create-asset.dto';
|
|
|
-import { createReadStream } from 'fs';
|
|
|
import { ServeFileDto } from './dto/serve-file.dto';
|
|
|
import { AssetOptimizeService } from '../../modules/image-optimize/image-optimize.service';
|
|
|
-import { AssetType } from './entities/asset.entity';
|
|
|
+import { AssetEntity, AssetType } from './entities/asset.entity';
|
|
|
import { GetAllAssetQueryDto } from './dto/get-all-asset-query.dto';
|
|
|
import { Response as Res } from 'express';
|
|
|
-import { promisify } from 'util';
|
|
|
-import { stat } from 'fs';
|
|
|
-import { pipeline } from 'stream';
|
|
|
import { GetNewAssetQueryDto } from './dto/get-new-asset-query.dto';
|
|
|
import { BackgroundTaskService } from '../../modules/background-task/background-task.service';
|
|
|
-
|
|
|
-const fileInfo = promisify(stat);
|
|
|
+import { DeleteAssetDto } from './dto/delete-asset.dto';
|
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
@Controller('asset')
|
|
@@ -73,75 +68,7 @@ export class AssetController {
|
|
|
@Response({ passthrough: true }) res: Res,
|
|
|
@Query(ValidationPipe) query: ServeFileDto,
|
|
|
): Promise<StreamableFile> {
|
|
|
- let file = null;
|
|
|
- const asset = await this.assetService.findOne(authUser, query.did, query.aid);
|
|
|
-
|
|
|
- // Handle Sending Images
|
|
|
- if (asset.type == AssetType.IMAGE || query.isThumb == 'true') {
|
|
|
- res.set({
|
|
|
- 'Content-Type': asset.mimeType,
|
|
|
- });
|
|
|
-
|
|
|
- if (query.isThumb === 'false' || !query.isThumb) {
|
|
|
- file = createReadStream(asset.originalPath);
|
|
|
- } else {
|
|
|
- file = createReadStream(asset.resizePath);
|
|
|
- }
|
|
|
-
|
|
|
- return new StreamableFile(file);
|
|
|
- } else if (asset.type == AssetType.VIDEO) {
|
|
|
- // Handle Handling Video
|
|
|
- const { size } = await fileInfo(asset.originalPath);
|
|
|
- const range = headers.range;
|
|
|
-
|
|
|
- if (range) {
|
|
|
- /** Extracting Start and End value from Range Header */
|
|
|
- let [start, end] = range.replace(/bytes=/, '').split('-');
|
|
|
- start = parseInt(start, 10);
|
|
|
- end = end ? parseInt(end, 10) : size - 1;
|
|
|
-
|
|
|
- if (!isNaN(start) && isNaN(end)) {
|
|
|
- start = start;
|
|
|
- end = size - 1;
|
|
|
- }
|
|
|
- if (isNaN(start) && !isNaN(end)) {
|
|
|
- start = size - end;
|
|
|
- end = size - 1;
|
|
|
- }
|
|
|
-
|
|
|
- // Handle unavailable range request
|
|
|
- if (start >= size || end >= size) {
|
|
|
- console.error('Bad Request');
|
|
|
- // Return the 416 Range Not Satisfiable.
|
|
|
- res.status(416).set({
|
|
|
- 'Content-Range': `bytes */${size}`,
|
|
|
- });
|
|
|
-
|
|
|
- throw new BadRequestException('Bad Request Range');
|
|
|
- }
|
|
|
-
|
|
|
- /** Sending Partial Content With HTTP Code 206 */
|
|
|
-
|
|
|
- res.status(206).set({
|
|
|
- 'Content-Range': `bytes ${start}-${end}/${size}`,
|
|
|
- 'Accept-Ranges': 'bytes',
|
|
|
- 'Content-Length': end - start + 1,
|
|
|
- 'Content-Type': asset.mimeType,
|
|
|
- });
|
|
|
-
|
|
|
- const videoStream = createReadStream(asset.originalPath, { start: start, end: end });
|
|
|
-
|
|
|
- return new StreamableFile(videoStream);
|
|
|
- } else {
|
|
|
- res.set({
|
|
|
- 'Content-Type': asset.mimeType,
|
|
|
- });
|
|
|
-
|
|
|
- return new StreamableFile(createReadStream(asset.originalPath));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- console.log('SHOULD NOT BE HERE');
|
|
|
+ return this.assetService.serveFile(authUser, query, res, headers);
|
|
|
}
|
|
|
|
|
|
@Get('/new')
|
|
@@ -154,6 +81,11 @@ export class AssetController {
|
|
|
return await this.assetService.getAllAssets(authUser, query);
|
|
|
}
|
|
|
|
|
|
+ @Get('/')
|
|
|
+ async getAllAssetsNoPagination(@GetAuthUser() authUser: AuthUserDto) {
|
|
|
+ return await this.assetService.getAllAssetsNoPagination(authUser);
|
|
|
+ }
|
|
|
+
|
|
|
@Get('/:deviceId')
|
|
|
async getUserAssetsByDeviceId(@GetAuthUser() authUser: AuthUserDto, @Param('deviceId') deviceId: string) {
|
|
|
return await this.assetService.getUserAssetsByDeviceId(authUser, deviceId);
|
|
@@ -163,4 +95,24 @@ export class AssetController {
|
|
|
async getAssetById(@GetAuthUser() authUser: AuthUserDto, @Param('assetId') assetId) {
|
|
|
return this.assetService.getAssetById(authUser, assetId);
|
|
|
}
|
|
|
+
|
|
|
+ @Delete('/')
|
|
|
+ async deleteAssetById(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) assetIds: DeleteAssetDto) {
|
|
|
+ const deleteAssetList: AssetEntity[] = [];
|
|
|
+
|
|
|
+ assetIds.ids.forEach(async (id) => {
|
|
|
+ const assets = await this.assetService.getAssetById(authUser, id);
|
|
|
+ deleteAssetList.push(assets);
|
|
|
+ });
|
|
|
+
|
|
|
+ const result = await this.assetService.deleteAssetById(authUser, assetIds);
|
|
|
+
|
|
|
+ result.forEach((res) => {
|
|
|
+ deleteAssetList.filter((a) => a.id == res.id && res.status == 'success');
|
|
|
+ });
|
|
|
+
|
|
|
+ await this.backgroundTaskService.deleteFileOnDisk(deleteAssetList);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
}
|