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 #define MLOG_TAG "MovingPhotoProcessor"
17 
18 #include "moving_photo_processor.h"
19 
20 #include <fcntl.h>
21 
22 #include "abs_rdb_predicates.h"
23 #include "cloud_sync_helper.h"
24 #include "directory_ex.h"
25 #include "media_column.h"
26 #include "media_file_utils.h"
27 #include "media_log.h"
28 #include "medialibrary_db_const.h"
29 #include "medialibrary_errno.h"
30 #include "medialibrary_rdb_utils.h"
31 #include "medialibrary_rdbstore.h"
32 #include "medialibrary_unistore_manager.h"
33 #include "mimetype_utils.h"
34 #include "moving_photo_file_utils.h"
35 #include "parameters.h"
36 #include "rdb_store.h"
37 #include "rdb_utils.h"
38 #include "result_set_utils.h"
39 #include "scanner_utils.h"
40 #include "userfile_manager_types.h"
41 #include "values_bucket.h"
42 
43 using namespace std;
44 using namespace OHOS::NativeRdb;
45 
46 namespace OHOS {
47 namespace Media {
48 static constexpr int32_t MOVING_PHOTO_PROCESS_NUM = 100;
49 static constexpr int32_t DIRTY_NOT_UPLOADING = -1;
50 static constexpr int32_t DEFAULT_EXTRA_DATA_SIZE = MIN_STANDARD_SIZE;
51 static constexpr int32_t LIVE_PHOTO_QUERY_NUM = 3000;
52 static constexpr int32_t LIVE_PHOTO_PROCESS_NUM = 200;
53 
54 static const string MOVING_PHOTO_PROCESS_FLAG = "multimedia.medialibrary.cloneFlag";
55 static const string LIVE_PHOTO_COMPAT_DONE = "0";
56 
57 bool MovingPhotoProcessor::isProcessing_ = false;
58 
IsCloudLivePhotoRefreshed()59 static bool IsCloudLivePhotoRefreshed()
60 {
61     string refreshStatus = system::GetParameter(REFRESH_CLOUD_LIVE_PHOTO_FLAG, CLOUD_LIVE_PHOTO_REFRESHED);
62     return refreshStatus.compare(CLOUD_LIVE_PHOTO_REFRESHED) == 0;
63 }
64 
StartProcessMovingPhoto()65 void MovingPhotoProcessor::StartProcessMovingPhoto()
66 {
67     auto resultSet = QueryMovingPhoto();
68     if (resultSet == nullptr) {
69         MEDIA_ERR_LOG("Failed to query moving photo");
70         return;
71     }
72 
73     MovingPhotoDataList dataList;
74     ParseMovingPhotoData(resultSet, dataList);
75     if (dataList.movingPhotos.empty()) {
76         MEDIA_DEBUG_LOG("No moving photo need to be processed");
77         return;
78     }
79 
80     isProcessing_ = true;
81     CompatMovingPhoto(dataList);
82 }
83 
GetLivePhotoCompatId()84 static string GetLivePhotoCompatId()
85 {
86     return system::GetParameter(COMPAT_LIVE_PHOTO_FILE_ID, LIVE_PHOTO_COMPAT_DONE);
87 }
88 
IsLivePhotoCompatDone()89 static bool IsLivePhotoCompatDone()
90 {
91     string currentFileId = GetLivePhotoCompatId();
92     return currentFileId.compare(LIVE_PHOTO_COMPAT_DONE) == 0;
93 }
94 
SetLivePhotoCompatId(string fileId)95 static void SetLivePhotoCompatId(string fileId)
96 {
97     bool ret = system::SetParameter(COMPAT_LIVE_PHOTO_FILE_ID, fileId);
98     if (!ret) {
99         MEDIA_ERR_LOG("Failed to set parameter for compating local live photo: %{public}s", fileId.c_str());
100     }
101 }
102 
StartProcessLivePhoto()103 void MovingPhotoProcessor::StartProcessLivePhoto()
104 {
105     if (IsLivePhotoCompatDone()) {
106         MEDIA_DEBUG_LOG("Live photo compat done or no need to compat");
107         return;
108     }
109 
110     auto resultSet = QueryCandidateLivePhoto();
111     if (resultSet == nullptr) {
112         MEDIA_ERR_LOG("Failed to query candidate live photo");
113         return;
114     }
115 
116     LivePhotoDataList dataList;
117     ParseLivePhotoData(resultSet, dataList);
118     if (dataList.livePhotos.empty()) {
119         SetLivePhotoCompatId(LIVE_PHOTO_COMPAT_DONE);
120         MEDIA_INFO_LOG("No live photo need to compat");
121         return;
122     }
123 
124     isProcessing_ = true;
125     CompatLivePhoto(dataList);
126 }
127 
StartProcess()128 void MovingPhotoProcessor::StartProcess()
129 {
130     MEDIA_DEBUG_LOG("Start processing moving photo task");
131 
132     // 1. compat old moving photo
133     StartProcessMovingPhoto();
134 
135     // 2. compat local live photo
136     StartProcessLivePhoto();
137 
138     // 3. refresh cloud live photo if needed
139     if (!IsCloudLivePhotoRefreshed()) {
140         MEDIA_INFO_LOG("Strat reset cloud cursor for cloud live photo");
141         FileManagement::CloudSync::CloudSyncManager::GetInstance().ResetCursor();
142         MEDIA_INFO_LOG("End reset cloud cursor for cloud live photo");
143         bool ret = system::SetParameter(REFRESH_CLOUD_LIVE_PHOTO_FLAG, CLOUD_LIVE_PHOTO_REFRESHED);
144         MEDIA_INFO_LOG("Set parameter of isRefreshed to 1, ret: %{public}d", ret);
145     }
146 
147     isProcessing_ = false;
148     MEDIA_DEBUG_LOG("Finsh processing moving photo task");
149 }
150 
StopProcess()151 void MovingPhotoProcessor::StopProcess()
152 {
153     isProcessing_ = false;
154 }
155 
QueryMovingPhoto()156 shared_ptr<NativeRdb::ResultSet> MovingPhotoProcessor::QueryMovingPhoto()
157 {
158     const vector<string> columns = {
159         PhotoColumn::MEDIA_ID,
160         PhotoColumn::PHOTO_SUBTYPE,
161         PhotoColumn::MOVING_PHOTO_EFFECT_MODE,
162         PhotoColumn::MEDIA_SIZE,
163         PhotoColumn::MEDIA_FILE_PATH,
164     };
165     RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
166     predicates.EqualTo(PhotoColumn::PHOTO_DIRTY, DIRTY_NOT_UPLOADING)
167         ->And()
168         ->BeginWrap()
169         ->EqualTo(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::MOVING_PHOTO))
170         ->Or()
171         ->EqualTo(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY))
172         ->EndWrap()
173         ->And()
174         ->EqualTo(PhotoColumn::PHOTO_IS_TEMP, 0)
175         ->And()
176         ->EqualTo(PhotoColumn::MEDIA_TIME_PENDING, 0)
177         ->And()
178         ->BeginWrap()
179         ->EqualTo(PhotoColumn::PHOTO_QUALITY, static_cast<int32_t>(MultiStagesPhotoQuality::FULL))
180         ->Or()
181         ->IsNull(PhotoColumn::PHOTO_QUALITY)
182         ->EndWrap()
183         ->Limit(MOVING_PHOTO_PROCESS_NUM);
184     return MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
185 }
186 
ParseMovingPhotoData(shared_ptr<NativeRdb::ResultSet> & resultSet,MovingPhotoDataList & dataList)187 void MovingPhotoProcessor::ParseMovingPhotoData(shared_ptr<NativeRdb::ResultSet>& resultSet,
188     MovingPhotoDataList& dataList)
189 {
190     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
191         int32_t fileId = get<int32_t>(
192             ResultSetUtils::GetValFromColumn(PhotoColumn::MEDIA_ID, resultSet, TYPE_INT32));
193         int32_t subtype = get<int32_t>(
194             ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_SUBTYPE, resultSet, TYPE_INT32));
195         int32_t effectMode = get<int32_t>(
196             ResultSetUtils::GetValFromColumn(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, resultSet, TYPE_INT32));
197         int64_t size = get<int64_t>(
198             ResultSetUtils::GetValFromColumn(PhotoColumn::MEDIA_SIZE, resultSet, TYPE_INT64));
199         std::string path = get<std::string>(
200             ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_FILE_PATH, resultSet, TYPE_STRING));
201 
202         MovingPhotoData movingPhotoData;
203         movingPhotoData.fileId = fileId;
204         movingPhotoData.subtype = subtype;
205         movingPhotoData.effectMode = effectMode;
206         movingPhotoData.size = size;
207         movingPhotoData.path = path;
208         dataList.movingPhotos.push_back(movingPhotoData);
209     }
210 }
211 
UpdateMovingPhotoData(const MovingPhotoData & movingPhotoData)212 void MovingPhotoProcessor::UpdateMovingPhotoData(const MovingPhotoData& movingPhotoData)
213 {
214     ValuesBucket values;
215     string whereClause = PhotoColumn::MEDIA_ID + " = ? AND " + PhotoColumn::PHOTO_DIRTY + " = ?";
216     vector<string> whereArgs = { to_string(movingPhotoData.fileId), to_string(DIRTY_NOT_UPLOADING) };
217     values.PutInt(PhotoColumn::PHOTO_SUBTYPE, movingPhotoData.subtype);
218     values.PutLong(PhotoColumn::MEDIA_SIZE, movingPhotoData.size);
219     values.PutInt(PhotoColumn::PHOTO_DIRTY, static_cast<int32_t>(DirtyTypes::TYPE_NEW));
220     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
221     if (rdbStore == nullptr) {
222         MEDIA_ERR_LOG("rdbStore is null");
223         return;
224     }
225     if (!isProcessing_) {
226         MEDIA_INFO_LOG("stop updateing moving photo data");
227         return;
228     }
229     int32_t updateCount = 0;
230     int32_t result = rdbStore->Update(updateCount, PhotoColumn::PHOTOS_TABLE, values, whereClause, whereArgs);
231     if (result != NativeRdb::E_OK || updateCount <= 0) {
232         MEDIA_ERR_LOG("Update failed. result: %{public}d, updateCount: %{public}d", result, updateCount);
233         return;
234     }
235 }
236 
GetDefaultExtraData()237 static string GetDefaultExtraData()
238 {
239     static string defaultExtraData = "v3_f0               0:0                 LIVE_10000000       ";
240     return defaultExtraData;
241 }
242 
GetUpdatedMovingPhotoData(const MovingPhotoData & currentData,MovingPhotoData & newData)243 int32_t MovingPhotoProcessor::GetUpdatedMovingPhotoData(const MovingPhotoData& currentData,
244     MovingPhotoData& newData)
245 {
246     newData = currentData;
247     string imagePath = currentData.path;
248     string videoPath = MovingPhotoFileUtils::GetMovingPhotoVideoPath(imagePath);
249     string extraDataPath = MovingPhotoFileUtils::GetMovingPhotoExtraDataPath(imagePath);
250     size_t imageSize = 0;
251     size_t videoSize = 0;
252     size_t extraSize = 0;
253     if (!MediaFileUtils::GetFileSize(imagePath, imageSize) || imageSize == 0) {
254         MEDIA_WARN_LOG("Failed to get image of moving photo, id: %{public}d", currentData.fileId);
255         newData.size = -1; // set abnormal size to -1 if original size is 0
256         newData.subtype = static_cast<int32_t>(PhotoSubType::DEFAULT);
257         return E_OK;
258     }
259 
260     if (!MediaFileUtils::GetFileSize(videoPath, videoSize) || videoSize == 0) {
261         MEDIA_WARN_LOG("Failed to get video of moving photo, id: %{public}d", currentData.fileId);
262         newData.size = static_cast<int64_t>(imageSize);
263         newData.subtype = static_cast<int32_t>(PhotoSubType::DEFAULT);
264         return E_OK;
265     }
266 
267     if (MediaFileUtils::GetFileSize(extraDataPath, extraSize) && extraSize > 0) {
268         newData.size = static_cast<int64_t>(imageSize + videoSize + extraSize);
269         return E_OK;
270     }
271     string extraDataDir = MovingPhotoFileUtils::GetMovingPhotoExtraDataDir(imagePath);
272     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::CreateDirectory(extraDataDir), E_HAS_FS_ERROR,
273         "Cannot create dir %{private}s, errno:%{public}d", extraDataDir.c_str(), errno);
274     if (!MediaFileUtils::IsFileExists(extraDataPath) && MediaFileUtils::CreateAsset(extraDataPath) != E_OK) {
275         MEDIA_ERR_LOG("Failed to create extraData:%{private}s, errno:%{public}d", extraDataPath.c_str(), errno);
276         return E_HAS_FS_ERROR;
277     }
278     if (!MediaFileUtils::WriteStrToFile(extraDataPath, GetDefaultExtraData())) {
279         MEDIA_ERR_LOG("Failed to write extraData, errno:%{public}d", errno);
280         return E_HAS_FS_ERROR;
281     }
282     newData.size = static_cast<int64_t>(imageSize + videoSize + DEFAULT_EXTRA_DATA_SIZE);
283     return E_OK;
284 }
285 
CompatMovingPhoto(const MovingPhotoDataList & dataList)286 void MovingPhotoProcessor::CompatMovingPhoto(const MovingPhotoDataList& dataList)
287 {
288     MEDIA_INFO_LOG("Start processing %{public}zu moving photos", dataList.movingPhotos.size());
289     int32_t count = 0;
290     for (const auto& movingPhoto : dataList.movingPhotos) {
291         if (!isProcessing_) {
292             MEDIA_INFO_LOG("stop compating moving photo");
293             return;
294         }
295         MovingPhotoData newData;
296         if (GetUpdatedMovingPhotoData(movingPhoto, newData) != E_OK) {
297             MEDIA_INFO_LOG("Failed to get updated data of moving photo, id: %{public}d", movingPhoto.fileId);
298             continue;
299         }
300         UpdateMovingPhotoData(newData);
301         count += 1;
302     }
303     MEDIA_INFO_LOG("Finish processing %{public}d moving photos", count);
304 }
305 
QueryCandidateLivePhoto()306 shared_ptr<NativeRdb::ResultSet> MovingPhotoProcessor::QueryCandidateLivePhoto()
307 {
308     const vector<string> columns = {
309         PhotoColumn::MEDIA_ID,
310         PhotoColumn::MEDIA_TYPE,
311         PhotoColumn::PHOTO_SUBTYPE,
312         PhotoColumn::PHOTO_POSITION,
313         PhotoColumn::PHOTO_EDIT_TIME,
314         PhotoColumn::MEDIA_FILE_PATH,
315     };
316     RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
317     string currentFileIdStr = GetLivePhotoCompatId();
318     int32_t currentFileId = std::atoi(currentFileIdStr.c_str());
319     MEDIA_INFO_LOG("Start query candidate live photo from file_id: %{public}d", currentFileId);
320     predicates.GreaterThanOrEqualTo(PhotoColumn::MEDIA_ID, currentFileId)
321         ->And()
322         ->EqualTo(PhotoColumn::MEDIA_TYPE, static_cast<int32_t>(MEDIA_TYPE_IMAGE))
323         ->And()
324         ->EqualTo(PhotoColumn::PHOTO_IS_TEMP, 0)
325         ->And()
326         ->EqualTo(PhotoColumn::MEDIA_TIME_PENDING, 0)
327         ->And()
328         ->BeginWrap()
329         ->EqualTo(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::DEFAULT))
330         ->Or()
331         ->EqualTo(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::CAMERA))
332         ->EndWrap()
333         ->And()
334         ->BeginWrap()
335         ->EqualTo(PhotoColumn::PHOTO_POSITION, static_cast<int32_t>(PhotoPositionType::LOCAL))
336         ->Or()
337         ->EqualTo(PhotoColumn::PHOTO_POSITION, static_cast<int32_t>(PhotoPositionType::LOCAL_AND_CLOUD))
338         ->EndWrap()
339         ->OrderByAsc(PhotoColumn::MEDIA_ID)
340         ->Limit(LIVE_PHOTO_QUERY_NUM);
341     return MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
342 }
343 
ParseLivePhotoData(shared_ptr<NativeRdb::ResultSet> & resultSet,LivePhotoDataList & dataList)344 void MovingPhotoProcessor::ParseLivePhotoData(shared_ptr<NativeRdb::ResultSet>& resultSet,
345     LivePhotoDataList& dataList)
346 {
347     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
348         int32_t fileId = get<int32_t>(
349             ResultSetUtils::GetValFromColumn(PhotoColumn::MEDIA_ID, resultSet, TYPE_INT32));
350         int32_t mediaType = get<int32_t>(
351             ResultSetUtils::GetValFromColumn(PhotoColumn::MEDIA_TYPE, resultSet, TYPE_INT32));
352         int32_t subtype = get<int32_t>(
353             ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_SUBTYPE, resultSet, TYPE_INT32));
354         int32_t position = get<int32_t>(
355             ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_POSITION, resultSet, TYPE_INT32));
356         int64_t editTime = get<int64_t>(
357             ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_EDIT_TIME, resultSet, TYPE_INT64));
358         std::string path = get<std::string>(
359             ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_FILE_PATH, resultSet, TYPE_STRING));
360 
361         LivePhotoData livePhotoData;
362         livePhotoData.isLivePhoto = false;
363         livePhotoData.fileId = fileId;
364         livePhotoData.mediaType = mediaType;
365         livePhotoData.subtype = subtype;
366         livePhotoData.position = position;
367         livePhotoData.editTime = editTime;
368         livePhotoData.path = path;
369         dataList.livePhotos.push_back(livePhotoData);
370     }
371 }
372 
CompatLivePhoto(const LivePhotoDataList & dataList)373 void MovingPhotoProcessor::CompatLivePhoto(const LivePhotoDataList& dataList)
374 {
375     MEDIA_INFO_LOG("Start processing %{public}zu candidate live photos", dataList.livePhotos.size());
376     int32_t count = 0;
377     int32_t livePhotoCount = 0;
378     int32_t processedFileId = 0;
379     for (const auto& livePhoto : dataList.livePhotos) {
380         if (!isProcessing_) {
381             SetLivePhotoCompatId(std::to_string(livePhoto.fileId));
382             MEDIA_INFO_LOG("Stop compating live photo, file_id: %{public}d", livePhoto.fileId);
383             return;
384         }
385         processedFileId = livePhoto.fileId;
386         LivePhotoData newData;
387         if (GetUpdatedLivePhotoData(livePhoto, newData) != E_OK) {
388             MEDIA_INFO_LOG("Failed to get updated data of candidate live photo, id: %{public}d", livePhoto.fileId);
389             continue;
390         }
391         if (newData.isLivePhoto) {
392             UpdateLivePhotoData(newData);
393             livePhotoCount += 1;
394         }
395         count += 1;
396 
397         if (livePhotoCount >= LIVE_PHOTO_PROCESS_NUM) {
398             SetLivePhotoCompatId(std::to_string(livePhoto.fileId + 1));
399             MEDIA_INFO_LOG("Stop compating live photo, %{public}d processed", livePhotoCount);
400             return;
401         }
402     }
403     SetLivePhotoCompatId(std::to_string(processedFileId + 1));
404     MEDIA_INFO_LOG("Finish processing %{public}d candidates, contains %{public}d live photos, file_id: %{public}d",
405         count, livePhotoCount, processedFileId);
406 }
407 
addCompatPathSuffix(const string & oldPath,const string & suffix,string & newPath)408 static void addCompatPathSuffix(const string &oldPath, const string &suffix, string &newPath)
409 {
410     if (oldPath.empty() || suffix.empty()) {
411         MEDIA_WARN_LOG("oldPath or suffix is empty");
412         return;
413     }
414 
415     newPath = oldPath + ".compat" + suffix;
416     while (MediaFileUtils::IsFileExists(newPath)) {
417         newPath += ".dup" + suffix;
418     }
419 }
420 
MoveMovingPhoto(const string & path,const string & compatImagePath,const string & compatVideoPath,const string & compatExtraDataPath)421 static int32_t MoveMovingPhoto(const string &path,
422     const string &compatImagePath, const string &compatVideoPath, const string &compatExtraDataPath)
423 {
424     string movingPhotoVideoPath = MovingPhotoFileUtils::GetMovingPhotoVideoPath(path);
425     string movingPhotoExtraDataPath = MovingPhotoFileUtils::GetMovingPhotoExtraDataPath(path);
426     CHECK_AND_RETURN_RET_LOG(!movingPhotoVideoPath.empty(), E_INVALID_VALUES, "Failed to get video path");
427     CHECK_AND_RETURN_RET_LOG(!movingPhotoExtraDataPath.empty(), E_INVALID_VALUES, "Failed to get extraData path");
428     CHECK_AND_RETURN_RET_LOG(
429         !MediaFileUtils::IsFileExists(movingPhotoVideoPath), E_INVALID_VALUES, "Video path exists!");
430     CHECK_AND_RETURN_RET_LOG(
431         !MediaFileUtils::IsFileExists(movingPhotoExtraDataPath), E_INVALID_VALUES, "extraData path exists");
432     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::CreateDirectory(MovingPhotoFileUtils::GetMovingPhotoExtraDataDir(path)),
433         E_HAS_FS_ERROR, "Failed to create extraData dir of %{private}s", path.c_str());
434 
435     int32_t ret = rename(compatExtraDataPath.c_str(), movingPhotoExtraDataPath.c_str());
436     if (ret < 0) {
437         MEDIA_ERR_LOG("Failed to rename extraData, src: %{public}s, dest: %{public}s, errno: %{public}d",
438             compatExtraDataPath.c_str(), movingPhotoExtraDataPath.c_str(), errno);
439         return ret;
440     }
441     ret = rename(compatVideoPath.c_str(), movingPhotoVideoPath.c_str());
442     if (ret < 0) {
443         MEDIA_ERR_LOG("Failed to rename video, src: %{public}s, dest: %{public}s, errno: %{public}d",
444             compatVideoPath.c_str(), movingPhotoVideoPath.c_str(), errno);
445         return ret;
446     }
447     ret = rename(compatImagePath.c_str(), path.c_str());
448     if (ret < 0) {
449         MEDIA_ERR_LOG("Failed to rename image, src: %{public}s, dest: %{public}s, errno: %{public}d",
450             compatImagePath.c_str(), path.c_str(), errno);
451         return ret;
452     }
453     return ret;
454 }
455 
ProcessLocalLivePhoto(LivePhotoData & data)456 int32_t MovingPhotoProcessor::ProcessLocalLivePhoto(LivePhotoData& data)
457 {
458     data.isLivePhoto = false;
459     bool isLivePhoto = MovingPhotoFileUtils::IsLivePhoto(data.path);
460     if (!isLivePhoto) {
461         return E_OK;
462     }
463 
464     string livePhotoPath = data.path;
465     string compatImagePath;
466     string compatVideoPath;
467     string compatExtraDataPath;
468     addCompatPathSuffix(livePhotoPath, ".jpg", compatImagePath);
469     addCompatPathSuffix(livePhotoPath, ".mp4", compatVideoPath);
470     addCompatPathSuffix(livePhotoPath, ".extra", compatExtraDataPath);
471     int32_t ret = MovingPhotoFileUtils::ConvertToMovingPhoto(
472         livePhotoPath, compatImagePath, compatVideoPath, compatExtraDataPath);
473     if (ret != E_OK) {
474         MEDIA_ERR_LOG("Failed to convert live photo, ret:%{public}d, file_id:%{public}d", ret, data.fileId);
475         (void)MediaFileUtils::DeleteFile(compatImagePath);
476         (void)MediaFileUtils::DeleteFile(compatVideoPath);
477         (void)MediaFileUtils::DeleteFile(compatExtraDataPath);
478         return ret;
479     }
480 
481     uint64_t coverPosition = 0;
482     uint32_t version = 0;
483     uint32_t frameIndex = 0;
484     bool hasCinemagraphInfo = false;
485     string absExtraDataPath;
486     if (!PathToRealPath(compatExtraDataPath, absExtraDataPath)) {
487         MEDIA_ERR_LOG("extraData is not real path: %{private}s, errno: %{public}d", compatExtraDataPath.c_str(), errno);
488         return E_HAS_FS_ERROR;
489     }
490     UniqueFd extraDataFd(open(absExtraDataPath.c_str(), O_RDONLY));
491     (void)MovingPhotoFileUtils::GetVersionAndFrameNum(extraDataFd.Get(), version, frameIndex, hasCinemagraphInfo);
492     (void)MovingPhotoFileUtils::GetCoverPosition(compatVideoPath, frameIndex, coverPosition);
493     data.coverPosition = static_cast<int64_t>(coverPosition);
494 
495     ret = MoveMovingPhoto(livePhotoPath, compatImagePath, compatVideoPath, compatExtraDataPath);
496     CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Failed to move moving photo, file_id:%{public}d", data.fileId);
497     data.subtype = static_cast<int32_t>(PhotoSubType::MOVING_PHOTO);
498     data.isLivePhoto = true;
499     return E_OK;
500 }
501 
ProcessLocalCloudLivePhoto(LivePhotoData & data)502 int32_t MovingPhotoProcessor::ProcessLocalCloudLivePhoto(LivePhotoData& data)
503 {
504     if (data.editTime == 0) {
505         return ProcessLocalLivePhoto(data);
506     }
507 
508     data.isLivePhoto = false;
509     string sourcePath = PhotoFileUtils::GetEditDataSourcePath(data.path);
510     bool isLivePhotoEdited = MovingPhotoFileUtils::IsLivePhoto(sourcePath);
511     if (!isLivePhotoEdited) {
512         return E_OK;
513     }
514     data.metaDateModified = MediaFileUtils::UTCTimeMilliSeconds();
515     data.isLivePhoto = true;
516     return E_OK;
517 }
518 
GetUpdatedLivePhotoData(const LivePhotoData & currentData,LivePhotoData & newData)519 int32_t MovingPhotoProcessor::GetUpdatedLivePhotoData(const LivePhotoData& currentData, LivePhotoData& newData)
520 {
521     newData = currentData;
522     string path = currentData.path;
523     string extension = ScannerUtils::GetFileExtension(path);
524     string mimeType = MimeTypeUtils::GetMimeTypeFromExtension(extension);
525     if (mimeType.compare("image/jpeg") != 0) {
526         newData.isLivePhoto = false;
527         return E_OK;
528     }
529 
530     if (currentData.position == static_cast<int32_t>(PhotoPositionType::LOCAL)) {
531         return ProcessLocalLivePhoto(newData);
532     } else if (currentData.position == static_cast<int32_t>(PhotoPositionType::LOCAL_AND_CLOUD)) {
533         return ProcessLocalCloudLivePhoto(newData);
534     } else {
535         MEDIA_ERR_LOG("Invalid position to process: %{public}d", currentData.position);
536         return E_INVALID_VALUES;
537     }
538 }
539 
UpdateLivePhotoData(const LivePhotoData & livePhotoData)540 void MovingPhotoProcessor::UpdateLivePhotoData(const LivePhotoData& livePhotoData)
541 {
542     if (!livePhotoData.isLivePhoto) {
543         return;
544     }
545 
546     if (livePhotoData.position != static_cast<int32_t>(PhotoPositionType::LOCAL) &&
547         livePhotoData.position != static_cast<int32_t>(PhotoPositionType::LOCAL_AND_CLOUD)) {
548         MEDIA_ERR_LOG("Invalid position: %{public}d", livePhotoData.position);
549         return;
550     }
551 
552     ValuesBucket values;
553     string whereClause = PhotoColumn::MEDIA_ID + " = ?";
554     vector<string> whereArgs = { to_string(livePhotoData.fileId) };
555     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
556     if (rdbStore == nullptr) {
557         MEDIA_ERR_LOG("rdbStore is null");
558         return;
559     }
560 
561     if (livePhotoData.editTime == 0) {
562         values.PutInt(PhotoColumn::PHOTO_SUBTYPE, livePhotoData.subtype);
563         values.PutLong(PhotoColumn::PHOTO_COVER_POSITION, livePhotoData.coverPosition);
564     } else {
565         values.PutLong(PhotoColumn::PHOTO_META_DATE_MODIFIED, livePhotoData.metaDateModified);
566     }
567 
568     int32_t updateCount = 0;
569     int32_t result = rdbStore->Update(updateCount, PhotoColumn::PHOTOS_TABLE, values, whereClause, whereArgs);
570     if (result != NativeRdb::E_OK || updateCount <= 0) {
571         MEDIA_ERR_LOG("Update failed. result: %{public}d, updateCount: %{public}d", result, updateCount);
572         return;
573     }
574 }
575 } // namespace Media
576 } // namespace OHOS
577