• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "moving_photo_napi.h"
17 
18 #include <fcntl.h>
19 #include <unistd.h>
20 
21 #include "directory_ex.h"
22 #include "file_uri.h"
23 #include "media_file_utils.h"
24 #include "media_library_napi.h"
25 #include "medialibrary_client_errno.h"
26 #include "medialibrary_errno.h"
27 #include "medialibrary_napi_utils.h"
28 #include "userfile_client.h"
29 #include "userfile_manager_types.h"
30 
31 using namespace std;
32 
33 namespace OHOS {
34 namespace Media {
35 
36 static const string MOVING_PHOTO_NAPI_CLASS = "MovingPhoto";
37 thread_local napi_ref MovingPhotoNapi::constructor_ = nullptr;
Init(napi_env env,napi_value exports)38 napi_value MovingPhotoNapi::Init(napi_env env, napi_value exports)
39 {
40     NapiClassInfo info = {
41         .name = MOVING_PHOTO_NAPI_CLASS,
42         .ref = &constructor_,
43         .constructor = Constructor,
44         .props = {
45             DECLARE_NAPI_FUNCTION("requestContent", JSRequestContent),
46             DECLARE_NAPI_FUNCTION("getUri", JSGetUri),
47         }
48     };
49     MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
50     return exports;
51 }
52 
Constructor(napi_env env,napi_callback_info info)53 napi_value MovingPhotoNapi::Constructor(napi_env env, napi_callback_info info)
54 {
55     napi_value newTarget = nullptr;
56     CHECK_ARGS(env, napi_get_new_target(env, info, &newTarget), JS_INNER_FAIL);
57     CHECK_COND_RET(newTarget != nullptr, nullptr, "Invalid call to constructor");
58 
59     size_t argc = ARGS_ONE;
60     napi_value argv[ARGS_ONE] = { 0 };
61     napi_value thisVar = nullptr;
62     napi_valuetype valueType;
63     CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
64     CHECK_COND_WITH_MESSAGE(env, argc == ARGS_ONE, "Number of args is invalid");
65     CHECK_ARGS(env, napi_typeof(env, argv[PARAM0], &valueType), JS_INNER_FAIL);
66     CHECK_COND_WITH_MESSAGE(env, valueType == napi_string, "Invalid argument type");
67     size_t result;
68     char photoUri[PATH_MAX];
69     CHECK_ARGS(env, napi_get_value_string_utf8(env, argv[PARAM0], photoUri, PATH_MAX, &result), JS_INNER_FAIL);
70 
71     unique_ptr<MovingPhotoNapi> obj = make_unique<MovingPhotoNapi>(string(photoUri));
72     CHECK_ARGS(env,
73         napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()), MovingPhotoNapi::Destructor, nullptr,
74             nullptr),
75         JS_INNER_FAIL);
76     obj.release();
77     return thisVar;
78 }
79 
Destructor(napi_env env,void * nativeObject,void * finalizeHint)80 void MovingPhotoNapi::Destructor(napi_env env, void* nativeObject, void* finalizeHint)
81 {
82     auto* movingPhotoNapi = reinterpret_cast<MovingPhotoNapi*>(nativeObject);
83     if (movingPhotoNapi == nullptr) {
84         return;
85     }
86 
87     delete movingPhotoNapi;
88     movingPhotoNapi = nullptr;
89 }
90 
GetUri()91 string MovingPhotoNapi::GetUri()
92 {
93     return photoUri_;
94 }
95 
GetSourceMode()96 SourceMode MovingPhotoNapi::GetSourceMode()
97 {
98     return sourceMode_;
99 }
100 
SetSourceMode(SourceMode sourceMode)101 void MovingPhotoNapi::SetSourceMode(SourceMode sourceMode)
102 {
103     sourceMode_ = sourceMode;
104 }
105 
OpenReadOnlyVideo(const std::string & videoUri,bool isMediaLibUri)106 static int32_t OpenReadOnlyVideo(const std::string& videoUri, bool isMediaLibUri)
107 {
108     if (isMediaLibUri) {
109         std::string openVideoUri = videoUri;
110         MediaFileUtils::UriAppendKeyValue(openVideoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
111             OPEN_MOVING_PHOTO_VIDEO);
112         Uri uri(openVideoUri);
113         return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
114     }
115     AppFileService::ModuleFileUri::FileUri fileUri(videoUri);
116     std::string realPath = fileUri.GetRealPath();
117     int32_t fd = open(realPath.c_str(), O_RDONLY);
118     if (fd < 0) {
119         NAPI_ERR_LOG("Failed to open read only video file, errno:%{public}d", errno);
120         return -1;
121     }
122     return fd;
123 }
124 
OpenReadOnlyImage(const std::string & imageUri,bool isMediaLibUri)125 static int32_t OpenReadOnlyImage(const std::string& imageUri, bool isMediaLibUri)
126 {
127     if (isMediaLibUri) {
128         Uri uri(imageUri);
129         return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
130     }
131     AppFileService::ModuleFileUri::FileUri fileUri(imageUri);
132     std::string realPath = fileUri.GetRealPath();
133     int32_t fd = open(realPath.c_str(), O_RDONLY);
134     if (fd < 0) {
135         NAPI_ERR_LOG("Failed to open read only image file, errno: %{public}d", errno);
136         return -1;
137     }
138     return fd;
139 }
140 
OpenReadOnlyFile(const std::string & uri,bool isReadImage)141 int32_t MovingPhotoNapi::OpenReadOnlyFile(const std::string& uri, bool isReadImage)
142 {
143     if (uri.empty()) {
144         NAPI_ERR_LOG("Failed to open read only file, uri is empty");
145         return -1;
146     }
147     std::string curUri = uri;
148     bool isMediaLibUri = MediaFileUtils::IsMediaLibraryUri(uri);
149     if (!isMediaLibUri) {
150         std::vector<std::string> uris;
151         if (!MediaFileUtils::SplitMovingPhotoUri(uri, uris)) {
152             NAPI_ERR_LOG("Failed to open read only file, split moving photo failed");
153             return -1;
154         }
155         curUri = uris[isReadImage ? MOVING_PHOTO_IMAGE_POS : MOVING_PHOTO_VIDEO_POS];
156     }
157     return isReadImage ? OpenReadOnlyImage(curUri, isMediaLibUri) : OpenReadOnlyVideo(curUri, isMediaLibUri);
158 }
159 
OpenReadOnlyLivePhoto(const string & destLivePhotoUri)160 int32_t MovingPhotoNapi::OpenReadOnlyLivePhoto(const string& destLivePhotoUri)
161 {
162     if (destLivePhotoUri.empty()) {
163         NAPI_ERR_LOG("Failed to open read only file, uri is empty");
164         return E_ERR;
165     }
166     if (MediaFileUtils::IsMediaLibraryUri(destLivePhotoUri)) {
167         std::string str = destLivePhotoUri;
168         std::string MULTI_USER_URI_FLAG = "user=";
169         size_t pos = str.find(MULTI_USER_URI_FLAG);
170         std::string userId = "";
171         if (pos != std::string::npos) {
172             pos += MULTI_USER_URI_FLAG.length();
173             size_t end = str.find_first_of("&?", pos);
174             if (end == std::string::npos) {
175                 end = str.length();
176             }
177             userId = str.substr(pos, end - pos);
178             NAPI_ERR_LOG("ReadMovingPhotoVideo for other user is %{public}s", userId.c_str());
179         }
180         string livePhotoUri = destLivePhotoUri;
181         MediaFileUtils::UriAppendKeyValue(livePhotoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
182             OPEN_PRIVATE_LIVE_PHOTO);
183         Uri uri(livePhotoUri);
184         return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY, userId !="" ? stoi(userId) : -1);
185     }
186     return E_ERR;
187 }
188 
OpenReadOnlyMetadata(const string & movingPhotoUri)189 int32_t MovingPhotoNapi::OpenReadOnlyMetadata(const string& movingPhotoUri)
190 {
191     if (movingPhotoUri.empty()) {
192         NAPI_ERR_LOG("Failed to open metadata of moving photo, uri is empty");
193         return E_ERR;
194     }
195 
196     if (!MediaFileUtils::IsMediaLibraryUri(movingPhotoUri)) {
197         NAPI_ERR_LOG("Failed to check uri of moving photo: %{private}s", movingPhotoUri.c_str());
198         return E_ERR;
199     }
200 
201     string movingPhotoMetadataUri = movingPhotoUri;
202     MediaFileUtils::UriAppendKeyValue(
203         movingPhotoMetadataUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD, OPEN_PRIVATE_MOVING_PHOTO_METADATA);
204     Uri uri(movingPhotoMetadataUri);
205     return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
206 }
207 
CopyFileFromMediaLibrary(int32_t srcFd,int32_t destFd)208 static int32_t CopyFileFromMediaLibrary(int32_t srcFd, int32_t destFd)
209 {
210     constexpr size_t bufferSize = 4096;
211     char buffer[bufferSize];
212     ssize_t bytesRead;
213     ssize_t bytesWritten;
214     while ((bytesRead = read(srcFd, buffer, bufferSize)) > 0) {
215         bytesWritten = write(destFd, buffer, bytesRead);
216         if (bytesWritten != bytesRead) {
217             NAPI_ERR_LOG("Failed to copy file from srcFd=%{public}d to destFd=%{public}d, errno=%{public}d",
218                 srcFd, destFd, errno);
219             return E_HAS_FS_ERROR;
220         }
221     }
222 
223     if (bytesRead < 0) {
224         NAPI_ERR_LOG("Failed to read from srcFd=%{public}d, errno=%{public}d", srcFd, errno);
225         return E_HAS_FS_ERROR;
226     }
227     return E_OK;
228 }
229 
WriteToSandboxUri(int32_t srcFd,const string & sandboxUri)230 static int32_t WriteToSandboxUri(int32_t srcFd, const string& sandboxUri)
231 {
232     UniqueFd srcUniqueFd(srcFd);
233 
234     AppFileService::ModuleFileUri::FileUri fileUri(sandboxUri);
235     string destPath = fileUri.GetRealPath();
236     if (!MediaFileUtils::IsFileExists(destPath) && !MediaFileUtils::CreateFile(destPath)) {
237         NAPI_ERR_LOG("Create empty dest file in sandbox failed, path:%{private}s", destPath.c_str());
238         return E_HAS_FS_ERROR;
239     }
240     int32_t destFd = MediaFileUtils::OpenFile(destPath, MEDIA_FILEMODE_READWRITE);
241     if (destFd < 0) {
242         NAPI_ERR_LOG("Open dest file failed, error: %{public}d", errno);
243         return E_HAS_FS_ERROR;
244     }
245     UniqueFd destUniqueFd(destFd);
246 
247     if (ftruncate(destUniqueFd.Get(), 0) == -1) {
248         NAPI_ERR_LOG("Truncate old file in sandbox failed, error:%{public}d", errno);
249         return E_HAS_FS_ERROR;
250     }
251     return CopyFileFromMediaLibrary(srcUniqueFd.Get(), destUniqueFd.Get());
252 }
253 
HandleFd(int32_t & fd)254 static bool HandleFd(int32_t& fd)
255 {
256     if (fd == E_ERR) {
257         fd = E_HAS_FS_ERROR;
258         return false;
259     } else if (fd < 0) {
260         NAPI_ERR_LOG("Open failed due to OpenFile failure, error: %{public}d", fd);
261         return false;
262     }
263     return true;
264 }
265 
RequestContentToSandbox(MovingPhotoAsyncContext * context)266 static int32_t RequestContentToSandbox(MovingPhotoAsyncContext* context)
267 {
268     string movingPhotoUri = context->movingPhotoUri;
269     if (context->sourceMode == SourceMode::ORIGINAL_MODE) {
270         MediaFileUtils::UriAppendKeyValue(movingPhotoUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
271     }
272     if (!context->destImageUri.empty()) {
273         int32_t imageFd = MovingPhotoNapi::OpenReadOnlyFile(movingPhotoUri, true);
274         CHECK_COND_RET(HandleFd(imageFd), imageFd, "Open source image file failed");
275         int32_t ret = WriteToSandboxUri(imageFd, context->destImageUri);
276         CHECK_COND_RET(ret == E_OK, ret, "Write image to sandbox failed");
277     }
278     if (!context->destVideoUri.empty()) {
279         int32_t videoFd = MovingPhotoNapi::OpenReadOnlyFile(movingPhotoUri, false);
280         CHECK_COND_RET(HandleFd(videoFd), videoFd, "Open source video file failed");
281         int32_t ret = WriteToSandboxUri(videoFd, context->destVideoUri);
282         CHECK_COND_RET(ret == E_OK, ret, "Write video to sandbox failed");
283     }
284     if (!context->destLivePhotoUri.empty()) {
285         int32_t livePhotoFd = MovingPhotoNapi::OpenReadOnlyLivePhoto(movingPhotoUri);
286         CHECK_COND_RET(HandleFd(livePhotoFd), livePhotoFd, "Open source video file failed");
287         int32_t ret = WriteToSandboxUri(livePhotoFd, context->destLivePhotoUri);
288         CHECK_COND_RET(ret == E_OK, ret, "Write video to sandbox failed");
289     }
290 
291     return E_OK;
292 }
293 
AcquireFdForArrayBuffer(MovingPhotoAsyncContext * context)294 static int32_t AcquireFdForArrayBuffer(MovingPhotoAsyncContext* context)
295 {
296     int32_t fd = 0;
297     string movingPhotoUri = context->movingPhotoUri;
298     if (context->sourceMode == SourceMode::ORIGINAL_MODE) {
299         MediaFileUtils::UriAppendKeyValue(movingPhotoUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
300     }
301     switch (context->resourceType) {
302         case ResourceType::IMAGE_RESOURCE: {
303             fd = MovingPhotoNapi::OpenReadOnlyFile(movingPhotoUri, true);
304             CHECK_COND_RET(HandleFd(fd), fd, "Open source image file failed");
305             return fd;
306         }
307         case ResourceType::VIDEO_RESOURCE:
308             fd = MovingPhotoNapi::OpenReadOnlyFile(movingPhotoUri, false);
309             CHECK_COND_RET(HandleFd(fd), fd, "Open source video file failed");
310             return fd;
311         case ResourceType::PRIVATE_MOVING_PHOTO_RESOURCE:
312             fd = MovingPhotoNapi::OpenReadOnlyLivePhoto(movingPhotoUri);
313             CHECK_COND_RET(HandleFd(fd), fd, "Open live photo failed");
314             return fd;
315         case ResourceType::PRIVATE_MOVING_PHOTO_METADATA:
316             fd = MovingPhotoNapi::OpenReadOnlyMetadata(movingPhotoUri);
317             CHECK_COND_RET(HandleFd(fd), fd, "Open moving photo metadata failed");
318             return fd;
319         default:
320             NAPI_ERR_LOG("Invalid resource type: %{public}d", static_cast<int32_t>(context->resourceType));
321             return -EINVAL;
322     }
323 }
324 
RequestContentToArrayBuffer(napi_env env,MovingPhotoAsyncContext * context)325 static int32_t RequestContentToArrayBuffer(napi_env env, MovingPhotoAsyncContext* context)
326 {
327     int32_t fd = AcquireFdForArrayBuffer(context);
328     if (fd < 0) {
329         return fd;
330     }
331     UniqueFd uniqueFd(fd);
332 
333     off_t fileLen = lseek(uniqueFd.Get(), 0, SEEK_END);
334     if (fileLen < 0) {
335         NAPI_ERR_LOG("Failed to get file length, error: %{public}d", errno);
336         return E_HAS_FS_ERROR;
337     }
338 
339     off_t ret = lseek(uniqueFd.Get(), 0, SEEK_SET);
340     if (ret < 0) {
341         NAPI_ERR_LOG("Failed to reset file offset, error: %{public}d", errno);
342         return E_HAS_FS_ERROR;
343     }
344 
345     size_t fileSize = static_cast<size_t>(fileLen);
346 
347     context->arrayBufferData = malloc(fileSize);
348     if (!context->arrayBufferData) {
349         NAPI_ERR_LOG("Failed to malloc array buffer data, moving photo uri is %{public}s, resource type is %{public}d",
350             context->movingPhotoUri.c_str(), static_cast<int32_t>(context->resourceType));
351         return E_HAS_FS_ERROR;
352     }
353 
354     size_t readBytes = static_cast<size_t>(read(uniqueFd.Get(), context->arrayBufferData, fileSize));
355     if (readBytes != fileSize) {
356         NAPI_ERR_LOG("read file failed, read bytes is %{public}zu, actual length is %{public}zu, "
357             "error: %{public}d", readBytes, fileSize, errno);
358         free(context->arrayBufferData);
359         context->arrayBufferData = nullptr;
360         return E_HAS_FS_ERROR;
361     }
362 
363     context->arrayBufferLength = fileSize;
364     return E_OK;
365 }
366 
IsValidResourceType(int32_t resourceType)367 static bool IsValidResourceType(int32_t resourceType)
368 {
369     return resourceType == static_cast<int32_t>(ResourceType::IMAGE_RESOURCE) ||
370            resourceType == static_cast<int32_t>(ResourceType::VIDEO_RESOURCE) ||
371            (resourceType == static_cast<int32_t>(ResourceType::PRIVATE_MOVING_PHOTO_RESOURCE) &&
372                MediaLibraryNapiUtils::IsSystemApp()) ||
373            (resourceType == static_cast<int32_t>(ResourceType::PRIVATE_MOVING_PHOTO_METADATA) &&
374                MediaLibraryNapiUtils::IsSystemApp());
375 }
376 
ParseArgsForRequestContent(napi_env env,size_t argc,const napi_value argv[],MovingPhotoNapi * thisArg,unique_ptr<MovingPhotoAsyncContext> & context)377 static napi_value ParseArgsForRequestContent(napi_env env, size_t argc, const napi_value argv[],
378     MovingPhotoNapi* thisArg, unique_ptr<MovingPhotoAsyncContext>& context)
379 {
380     CHECK_COND_WITH_MESSAGE(env, (argc == ARGS_ONE || argc == ARGS_TWO), "Invalid number of arguments");
381     CHECK_COND(env, thisArg != nullptr, JS_INNER_FAIL);
382     context->movingPhotoUri = thisArg->GetUri();
383     context->sourceMode = thisArg->GetSourceMode();
384 
385     int32_t resourceType = 0;
386     if (argc == ARGS_ONE) {
387         // return by array buffer
388         CHECK_ARGS(env, napi_get_value_int32(env, argv[ARGS_ZERO], &resourceType), JS_INNER_FAIL);
389         CHECK_COND_WITH_MESSAGE(env, IsValidResourceType(resourceType), "Invalid resource type");
390         context->requestContentMode = MovingPhotoAsyncContext::WRITE_TO_ARRAY_BUFFER;
391         context->resourceType = static_cast<ResourceType>(resourceType);
392     } else if (argc == ARGS_TWO) {
393         context->requestContentMode = MovingPhotoAsyncContext::WRITE_TO_SANDBOX;
394         napi_valuetype valueTypeFront;
395         napi_valuetype valueTypeBack;
396         CHECK_ARGS(env, napi_typeof(env, argv[ARGS_ZERO], &valueTypeFront), JS_INNER_FAIL);
397         CHECK_ARGS(env, napi_typeof(env, argv[ARGS_ONE], &valueTypeBack), JS_INNER_FAIL);
398         if (valueTypeFront == napi_string && valueTypeBack == napi_string) {
399             // write both image and video to sandbox
400             CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ZERO], context->destImageUri),
401                 JS_INNER_FAIL);
402             CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ONE], context->destVideoUri),
403                 JS_INNER_FAIL);
404         } else if (valueTypeFront == napi_number && valueTypeBack == napi_string) {
405             // write specific resource to sandbox
406             CHECK_ARGS(env, napi_get_value_int32(env, argv[ARGS_ZERO], &resourceType), JS_INNER_FAIL);
407             CHECK_COND_WITH_MESSAGE(env, IsValidResourceType(resourceType), "Invalid resource type");
408             if (resourceType == static_cast<int32_t>(ResourceType::IMAGE_RESOURCE)) {
409                 CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ONE],
410                     context->destImageUri), JS_INNER_FAIL);
411             } else if (resourceType == static_cast<int32_t>(ResourceType::VIDEO_RESOURCE)) {
412                 CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ONE],
413                     context->destVideoUri), JS_INNER_FAIL);
414             } else {
415                 CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ONE],
416                     context->destLivePhotoUri), JS_INNER_FAIL);
417             }
418             context->resourceType = static_cast<ResourceType>(resourceType);
419         } else {
420             NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, __FUNCTION__, __LINE__, "Invalid type of arguments");
421             return nullptr;
422         }
423     }
424     RETURN_NAPI_TRUE(env);
425 }
426 
RequestContentExecute(napi_env env,void * data)427 static void RequestContentExecute(napi_env env, void *data)
428 {
429     auto* context = static_cast<MovingPhotoAsyncContext*>(data);
430     int32_t ret;
431     switch (context->requestContentMode) {
432         case MovingPhotoAsyncContext::WRITE_TO_SANDBOX:
433             ret = RequestContentToSandbox(context);
434             break;
435         case MovingPhotoAsyncContext::WRITE_TO_ARRAY_BUFFER:
436             ret = RequestContentToArrayBuffer(env, context);
437             break;
438         default:
439             NAPI_ERR_LOG("Invalid request content mode: %{public}d", static_cast<int32_t>(context->requestContentMode));
440             context->error = OHOS_INVALID_PARAM_CODE;
441             return;
442     }
443     if (ret != E_OK) {
444         context->SaveError(ret);
445         return;
446     }
447 }
448 
RequestContentComplete(napi_env env,napi_status status,void * data)449 static void RequestContentComplete(napi_env env, napi_status status, void *data)
450 {
451     MovingPhotoAsyncContext *context = static_cast<MovingPhotoAsyncContext*>(data);
452     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
453 
454     napi_value outBuffer = nullptr;
455     if (context->error == E_OK && context->requestContentMode == MovingPhotoAsyncContext::WRITE_TO_ARRAY_BUFFER) {
456         napi_status status = napi_create_external_arraybuffer(
457             env, context->arrayBufferData, context->arrayBufferLength,
458             [](napi_env env, void* data, void* hint) { free(data); }, nullptr, &outBuffer);
459         if (status != napi_ok) {
460             NAPI_ERR_LOG("Failed to create array buffer object, uri is %{public}s, resource type is %{public}d",
461                 context->movingPhotoUri.c_str(), context->resourceType);
462             free(context->arrayBufferData);
463             context->arrayBufferData = nullptr;
464             context->error = JS_INNER_FAIL;
465         }
466     } else if (context->arrayBufferData != nullptr) {
467         free(context->arrayBufferData);
468         context->arrayBufferData = nullptr;
469     }
470 
471     unique_ptr<JSAsyncContextOutput> outContext = make_unique<JSAsyncContextOutput>();
472     outContext->status = false;
473     napi_get_undefined(env, &outContext->data);
474 
475     if (context->error != E_OK) {
476         context->HandleError(env, outContext->error);
477     } else {
478         outContext->status = true;
479         if (context->requestContentMode == MovingPhotoAsyncContext::WRITE_TO_ARRAY_BUFFER) {
480             outContext->data = outBuffer;
481         }
482     }
483     if (context->work != nullptr) {
484         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, nullptr,
485                                                    context->work, *outContext);
486     } else {
487         NAPI_ERR_LOG("Async work is nullptr");
488     }
489     delete context;
490 }
491 
JSRequestContent(napi_env env,napi_callback_info info)492 napi_value MovingPhotoNapi::JSRequestContent(napi_env env, napi_callback_info info)
493 {
494     size_t argc = ARGS_TWO;
495     napi_value argv[ARGS_TWO] = {0};
496     napi_value thisVar = nullptr;
497     CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
498 
499     unique_ptr<MovingPhotoAsyncContext> asyncContext = make_unique<MovingPhotoAsyncContext>();
500     MovingPhotoNapi* nativeObject;
501     CHECK_ARGS(env, napi_unwrap(env, thisVar, reinterpret_cast<void **>(&nativeObject)), JS_INNER_FAIL);
502     CHECK_NULLPTR_RET(ParseArgsForRequestContent(env, argc, argv, nativeObject, asyncContext));
503 
504     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSRequestContent",
505         RequestContentExecute, RequestContentComplete);
506 }
507 
NewMovingPhotoNapi(napi_env env,const string & photoUri,SourceMode sourceMode)508 napi_value MovingPhotoNapi::NewMovingPhotoNapi(napi_env env, const string& photoUri,
509     SourceMode sourceMode)
510 {
511     napi_value constructor = nullptr;
512     napi_value instance = nullptr;
513     napi_value napiStringUri = nullptr;
514     napi_status status = napi_create_string_utf8(env, photoUri.c_str(), NAPI_AUTO_LENGTH, &napiStringUri);
515     CHECK_COND_RET(status == napi_ok, nullptr, "Failed to create napi string, napi status: %{public}d",
516         static_cast<int>(status));
517     status = napi_get_reference_value(env, constructor_, &constructor);
518     CHECK_COND_RET(status == napi_ok, nullptr, "Failed to get reference of constructor, napi status: %{public}d",
519         static_cast<int>(status));
520     status = napi_new_instance(env, constructor, 1, &napiStringUri, &instance);
521     CHECK_COND_RET(status == napi_ok, nullptr, "Failed to get new instance of moving photo, napi status: %{public}d",
522         static_cast<int>(status));
523     CHECK_COND_RET(instance != nullptr, nullptr, "Instance is nullptr");
524 
525     MovingPhotoNapi* movingPhotoNapi = nullptr;
526     status = napi_unwrap(env, instance, reinterpret_cast<void**>(&movingPhotoNapi));
527     CHECK_COND_RET(status == napi_ok, nullptr, "Failed to unwarp instance of MovingPhotoNapi");
528     CHECK_COND_RET(movingPhotoNapi != nullptr, nullptr, "movingPhotoNapi is nullptr");
529     movingPhotoNapi->SetSourceMode(sourceMode);
530     return instance;
531 }
532 
JSGetUri(napi_env env,napi_callback_info info)533 napi_value MovingPhotoNapi::JSGetUri(napi_env env, napi_callback_info info)
534 {
535     MovingPhotoNapi *obj = nullptr;
536     napi_value thisVar = nullptr;
537     CHECK_ARGS(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr), JS_INNER_FAIL);
538     if (thisVar == nullptr) {
539         NapiError::ThrowError(env, JS_INNER_FAIL);
540         return nullptr;
541     }
542 
543     CHECK_ARGS(env, napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj)), JS_INNER_FAIL);
544     if (obj == nullptr) {
545         NapiError::ThrowError(env, JS_INNER_FAIL);
546         return nullptr;
547     }
548 
549     napi_value jsResult = nullptr;
550     CHECK_ARGS(env, napi_create_string_utf8(env, obj->GetUri().c_str(), NAPI_AUTO_LENGTH, &jsResult), JS_INNER_FAIL);
551     return jsResult;
552 }
553 } // namespace Media
554 } // namespace OHOS