123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- import {
- Controller,
- Post,
- UseInterceptors,
- UploadedFiles,
- Body,
- UseGuards,
- Get,
- Param,
- ValidationPipe,
- StreamableFile,
- Query,
- Response,
- Headers,
- BadRequestException,
- } 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 { 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 { 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';
- const fileInfo = promisify(stat);
- @UseGuards(JwtAuthGuard)
- @Controller('asset')
- export class AssetController {
- constructor(
- private readonly assetService: AssetService,
- private readonly assetOptimizeService: AssetOptimizeService,
- ) {}
- @Post('upload')
- @UseInterceptors(FilesInterceptor('files', 30, multerOption))
- async uploadFile(
- @GetAuthUser() authUser,
- @UploadedFiles() files: Express.Multer.File[],
- @Body(ValidationPipe) assetInfo: CreateAssetDto,
- ) {
- files.forEach(async (file) => {
- const savedAsset = await this.assetService.createUserAsset(authUser, assetInfo, file.path, file.mimetype);
- if (savedAsset && savedAsset.type == AssetType.IMAGE) {
- await this.assetOptimizeService.resizeImage(savedAsset);
- }
- if (savedAsset && savedAsset.type == AssetType.VIDEO) {
- await this.assetOptimizeService.getVideoThumbnail(savedAsset, file.originalname);
- }
- });
- return 'ok';
- }
- @Get('/file')
- async serveFile(
- @Headers() headers,
- @GetAuthUser() authUser: AuthUserDto,
- @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 */
- console.log('Sendinf file with type ', asset.mimeType);
- 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');
- }
- @Get('/new')
- async getNewAssets(@GetAuthUser() authUser: AuthUserDto, @Query(ValidationPipe) query: GetNewAssetQueryDto) {
- return await this.assetService.getNewAssets(authUser, query.latestDate);
- }
- @Get('/all')
- async getAllAssets(@GetAuthUser() authUser: AuthUserDto, @Query(ValidationPipe) query: GetAllAssetQueryDto) {
- return await this.assetService.getAllAssets(authUser, query);
- }
- @Get('/:deviceId')
- async getUserAssetsByDeviceId(@GetAuthUser() authUser: AuthUserDto, @Param('deviceId') deviceId: string) {
- return await this.assetService.getUserAssetsByDeviceId(authUser, deviceId);
- }
- }
|