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