• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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 <libxml/parser.h>
17 #include <libxml/tree.h>
18 
19 #include "highlight_restore.h"
20 
21 #include "backup_database_utils.h"
22 #include "medialibrary_data_manager_utils.h"
23 #include "media_file_utils.h"
24 #include "media_log.h"
25 #include "upgrade_restore_task_report.h"
26 
27 namespace OHOS::Media {
28 const int32_t PAGE_SIZE = 200;
29 const int32_t HIGHLIGHT_STATUS_SUCCESS = 1;
30 const int32_t HIGHLIGHT_STATUS_FAIL = -2;
31 const int32_t HIGHLIGHT_STATUS_DUPLICATE = -1;
32 const std::vector<std::string> HIGHLIGHT_RATIO = {
33     "1_1", "3_2", "3_4", "microcard", "medium_card", "big_card", "screen_0_ver", "screen_0_hor"
34 };
35 const std::unordered_map<std::string, std::string> CLUSTER_SUB_TYPE_MAP = {
36     { "AttractionsAlbum", "Old_Attraction" },
37     { "LocationRenamed", "Old_AOI" },
38     { "FrequencyLocationGrowingCluster", "Old_FrequencyLocation" },
39     { "RegionalFoodGrowingCluster", "Old_RegionalFood" },
40     { "CatGrowingCluster", "Old_Cat" },
41     { "DogGrowingCluster", "Old_Dog" },
42     { "PeopleGrowingCluster", "Old_People" },
43     { "LifeStageCluster", "Old_LifeStage" },
44     { "夜色", "Old_Heaven" },
45     { "恰同学少年", "Old_Graduate" },
46     { "我们毕业了", "Old_Graduate" },
47     { "DEFAULT_DBSCAN", "Old_DBSCAN" },
48     { "", "Old_Null" }
49 };
50 const std::unordered_map<std::string, std::string> CLUSTER_TYPE_MAP = { { "DBSCANTIME", "TYPE_DBSCAN" } };
51 
Init(int32_t sceneCode,std::string taskId,std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb,std::shared_ptr<NativeRdb::RdbStore> galleryRdb)52 void HighlightRestore::Init(int32_t sceneCode, std::string taskId,
53     std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb, std::shared_ptr<NativeRdb::RdbStore> galleryRdb)
54 {
55     sceneCode_ = sceneCode;
56     taskId_ = taskId;
57     mediaLibraryRdb_ = mediaLibraryRdb;
58     galleryRdb_ = galleryRdb;
59     albumPhotoCounter_.clear();
60     successCnt_ = 0;
61     duplicateCnt_ = 0;
62     failCnt_ = 0;
63     tracksParseFailCnt_ = 0;
64 }
65 
RestoreAlbums(const std::string & albumOdid)66 void HighlightRestore::RestoreAlbums(const std::string &albumOdid)
67 {
68     bool cond = (galleryRdb_ == nullptr || mediaLibraryRdb_ == nullptr);
69     CHECK_AND_RETURN_LOG(!cond, "rdbStore is nullptr");
70 
71     GetAlbumInfos(albumOdid);
72     InsertIntoAnalysisAlbum();
73     UpdateAlbumIds();
74     InsertIntoHighlightTables();
75 }
76 
GetAlbumInfos(const std::string & albumOdid)77 void HighlightRestore::GetAlbumInfos(const std::string &albumOdid)
78 {
79     const std::string QUERY_SQL = "SELECT story_id, date, name, min_datetaken, max_datetaken, "
80         "cover_id, album_type, generatedtime, cluster_type, cluster_sub_type, cluster_condition, "
81         "(SELECT COUNT(1) FROM t_story_album_suggestion "
82         "WHERE t_story_album_suggestion.story_id = t_story_album.story_id) AS suggestion "
83         "FROM t_story_album WHERE COALESCE(name, '') <> '' AND displayable = 1 LIMIT ?, ?";
84     int rowCount = 0;
85     int offset = 0;
86     do {
87         std::vector<NativeRdb::ValueObject> params = {offset, PAGE_SIZE};
88         auto resultSet = BackupDatabaseUtils::QuerySql(galleryRdb_, QUERY_SQL, params);
89         if (resultSet == nullptr) {
90             MEDIA_ERR_LOG("resultSet is nullptr");
91             break;
92         }
93         while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
94             HighlightAlbumInfo info;
95             info.albumOdid = albumOdid;
96             info.albumIdOld = GetInt32Val("story_id", resultSet);
97             info.subTitle = GetStringVal("date", resultSet);
98             info.albumName = GetStringVal("name", resultSet);
99             info.minDateAdded = GetInt64Val("min_datetaken", resultSet);
100             info.maxDateAdded = GetInt64Val("max_datetaken", resultSet);
101             info.coverId = GetInt32Val("cover_id", resultSet);
102             info.generateTime = GetInt64Val("generatedtime", resultSet);
103             info.generateTime = info.generateTime != 0 ? info.generateTime : info.maxDateAdded;
104 
105             info.clusterType = GetStringVal("cluster_type", resultSet);
106             info.clusterSubType = GetStringVal("cluster_sub_type", resultSet);
107             info.clusterCondition = GetStringVal("cluster_condition", resultSet);
108             TransferClusterInfo(info);
109             int32_t suggestion = GetInt32Val("suggestion", resultSet);
110             if (suggestion) {
111                 info.clusterType = info.clusterType + "_suggestion";
112             }
113             int32_t albumType = GetInt32Val("album_type", resultSet);
114             info.clusterSubType = info.clusterSubType + "_" + std::to_string(albumType);
115             info.highlightStatus = !HasSameHighlightAlbum(info) ? HIGHLIGHT_STATUS_SUCCESS : HIGHLIGHT_STATUS_DUPLICATE;
116             albumInfos_.emplace_back(info);
117         }
118         resultSet->GetRowCount(rowCount);
119         resultSet->Close();
120         offset += PAGE_SIZE;
121     } while (rowCount == PAGE_SIZE);
122 }
123 
HasSameHighlightAlbum(HighlightAlbumInfo & info)124 bool HighlightRestore::HasSameHighlightAlbum(HighlightAlbumInfo &info)
125 {
126     const std::string QUERY_SQL = "SELECT highlight.id, highlight.album_id, highlight.ai_album_id FROM "
127         "tab_highlight_album highlight "
128         "LEFT JOIN AnalysisAlbum album ON highlight.album_id = album.album_id "
129         "WHERE highlight.cluster_type = ? AND highlight.cluster_sub_type = ? AND highlight.cluster_condition = ? "
130         "AND album.album_name = ? AND highlight.highlight_status = 1";
131     std::vector<NativeRdb::ValueObject> params = {
132         info.clusterType, info.clusterSubType, info.clusterCondition, info.albumName
133     };
134     std::shared_ptr<NativeRdb::ResultSet> resultSet =
135         BackupDatabaseUtils::QuerySql(mediaLibraryRdb_, QUERY_SQL, params);
136     bool cond = (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK);
137     CHECK_AND_RETURN_RET_LOG(!cond, false, "query highlight album failed.");
138 
139     info.id = GetInt32Val("id", resultSet);
140     info.albumIdNew = GetInt32Val("album_id", resultSet);
141     info.aiAlbumIdNew = GetInt32Val("ai_album_id", resultSet);
142     resultSet->Close();
143     return true;
144 }
145 
TransferClusterInfo(HighlightAlbumInfo & info)146 void HighlightRestore::TransferClusterInfo(HighlightAlbumInfo &info)
147 {
148     if (info.clusterType.empty()) {
149         info.clusterType = "TYPE_NULL";
150         info.clusterSubType = "Old_Null";
151         nlohmann::json jsonObjects;
152         jsonObjects.push_back({
153             { "start", std::to_string(info.minDateAdded) }, { "end", std::to_string(info.maxDateAdded) }
154         });
155         info.clusterCondition = jsonObjects.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
156         return;
157     }
158 
159     info.clusterType = CLUSTER_TYPE_MAP.count(info.clusterType) > 0 ?
160         CLUSTER_TYPE_MAP.at(info.clusterType) : info.clusterType;
161     nlohmann::json jsonObjects;
162     if (info.clusterType == "TYPE_DBSCAN") {
163         if (info.clusterSubType.empty()) {
164             info.clusterSubType = info.albumName;
165         }
166         info.clusterSubType = CLUSTER_SUB_TYPE_MAP.count(info.clusterSubType) > 0 ?
167             CLUSTER_SUB_TYPE_MAP.at(info.clusterSubType) : CLUSTER_SUB_TYPE_MAP.at("DEFAULT_DBSCAN");
168         jsonObjects.push_back({
169             { "start", std::to_string(info.minDateAdded) }, { "end", std::to_string(info.maxDateAdded) }
170         });
171     } else {
172         info.clusterSubType = CLUSTER_SUB_TYPE_MAP.count(info.clusterSubType) > 0 ?
173             CLUSTER_SUB_TYPE_MAP.at(info.clusterSubType) : "Old_" + info.clusterSubType;
174         nlohmann::json jsonObject = nlohmann::json::parse(info.clusterCondition, nullptr, false);
175         if (jsonObject.is_discarded() || info.clusterCondition == "null") {
176             MEDIA_ERR_LOG("Parse clusterCondition failed, %{public}s", info.ToString().c_str());
177             jsonObjects.push_back({
178                 { "start", std::to_string(info.minDateAdded) }, { "end", std::to_string(info.maxDateAdded) }
179             });
180             info.clusterCondition = jsonObjects.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
181             return;
182         }
183         if (jsonObject.contains("startDate")) {
184             jsonObject["start"] = jsonObject["startDate"];
185             jsonObject.erase("startDate");
186         }
187         if (jsonObject.contains("endDate")) {
188             jsonObject["end"] = jsonObject["endDate"];
189             jsonObject.erase("endDate");
190         }
191         jsonObjects.push_back(jsonObject);
192     }
193     info.clusterCondition = jsonObjects.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
194 }
195 
InsertIntoAnalysisAlbum()196 void HighlightRestore::InsertIntoAnalysisAlbum()
197 {
198     for (HighlightAlbumInfo &info : albumInfos_) {
199         if (info.highlightStatus != HIGHLIGHT_STATUS_SUCCESS) {
200             continue;
201         }
202         vector<NativeRdb::ValuesBucket> values;
203         const int64_t ROW_NUM = 2;
204         values.emplace_back(GetAnalysisAlbumValuesBucket(info, PhotoAlbumSubType::HIGHLIGHT));
205         values.emplace_back(GetAnalysisAlbumValuesBucket(info, PhotoAlbumSubType::HIGHLIGHT_SUGGESTIONS));
206         int64_t rowNum = 0;
207         int32_t errCode = BatchInsertWithRetry("AnalysisAlbum", values, rowNum);
208         if (errCode != E_OK || rowNum != ROW_NUM) {
209             info.highlightStatus = HIGHLIGHT_STATUS_FAIL;
210             MEDIA_ERR_LOG("InsertIntoAnalysisAlbum fail, %{public}s", info.ToString().c_str());
211             ErrorInfo errorInfo(RestoreError::INSERT_FAILED, 0, std::to_string(errCode),
212                 info.albumName + " insert into AnalysisAlbum fail.");
213             UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).ReportError(errorInfo);
214         }
215     }
216 }
217 
GetAnalysisAlbumValuesBucket(const HighlightAlbumInfo & info,int32_t subType)218 NativeRdb::ValuesBucket HighlightRestore::GetAnalysisAlbumValuesBucket(const HighlightAlbumInfo &info, int32_t subType)
219 {
220     NativeRdb::ValuesBucket value;
221     value.PutInt("album_type", PhotoAlbumType::SMART);
222     value.PutInt("count", 0);
223     value.PutInt("album_subtype", subType);
224     value.PutString("album_name", info.albumName);
225     value.PutLong("date_modified", info.generateTime);
226     return value;
227 }
228 
UpdateAlbumIds()229 void HighlightRestore::UpdateAlbumIds()
230 {
231     CHECK_AND_RETURN(!albumInfos_.empty());
232     const std::string QUERY_SQL =
233         "SELECT album_id, album_subtype, album_name, date_modified FROM AnalysisAlbum "
234         "WHERE album_subtype IN (4104, 4105) LIMIT ?, ?";
235     int rowCount = 0;
236     int offset = 0;
237     do {
238         std::vector<NativeRdb::ValueObject> params = {offset, PAGE_SIZE};
239         auto resultSet = BackupDatabaseUtils::QuerySql(mediaLibraryRdb_, QUERY_SQL, params);
240         if (resultSet == nullptr) {
241             MEDIA_ERR_LOG("resultSet is nullptr");
242             break;
243         }
244         while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
245             int32_t albumId = GetInt32Val("album_id", resultSet);
246             int32_t albumSubType = GetInt32Val("album_subtype", resultSet);
247             std::string albumName = GetStringVal("album_name", resultSet);
248             int64_t dateModified = GetInt64Val("date_modified", resultSet);
249             auto it = std::find_if(albumInfos_.begin(), albumInfos_.end(),
250                 [albumName, dateModified](const HighlightAlbumInfo &info) {
251                     return info.albumName == albumName && info.generateTime == dateModified;
252                 });
253             if (it == albumInfos_.end()) {
254                 continue;
255             }
256             if (albumSubType == PhotoAlbumSubType::HIGHLIGHT) {
257                 it->albumIdNew = albumId;
258             } else {
259                 it->aiAlbumIdNew = albumId;
260             }
261         }
262         resultSet->GetRowCount(rowCount);
263         resultSet->Close();
264         offset += PAGE_SIZE;
265     } while (rowCount == PAGE_SIZE);
266 }
267 
InsertIntoHighlightTables()268 void HighlightRestore::InsertIntoHighlightTables()
269 {
270     InsertIntoHighlightAlbum();
271     UpdateHighlightIds();
272     InsertIntoHighlightCoverAndPlayInfo();
273 }
274 
InsertIntoHighlightAlbum()275 void HighlightRestore::InsertIntoHighlightAlbum()
276 {
277     const std::string INSERT_ALBUM_SQL =
278         "INSERT INTO tab_highlight_album (album_id, ai_album_id, sub_title, min_date_added, max_date_added, "
279         "generate_time, cluster_type, cluster_sub_type, cluster_condition, highlight_status, highlight_version) "
280         "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 2)";
281     for (HighlightAlbumInfo &info : albumInfos_) {
282         if (info.highlightStatus != HIGHLIGHT_STATUS_SUCCESS) {
283             continue;
284         }
285         std::vector<NativeRdb::ValueObject> albumBindArgs = {
286             info.albumIdNew, info.aiAlbumIdNew, info.subTitle, info.minDateAdded, info.maxDateAdded,
287             info.generateTime, info.clusterType, info.clusterSubType, info.clusterCondition, info.highlightStatus
288         };
289         int errCode = BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb_, INSERT_ALBUM_SQL, albumBindArgs);
290         if (errCode != E_OK) {
291             info.highlightStatus = HIGHLIGHT_STATUS_FAIL;
292             MEDIA_ERR_LOG("InsertIntoHighlightAlbum fail, %{public}s", info.ToString().c_str());
293             ErrorInfo errorInfo(RestoreError::INSERT_FAILED, 0, std::to_string(errCode),
294                 info.albumName + " insert into HighlightAlbum fail.");
295             UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).ReportError(errorInfo);
296         }
297     }
298 }
299 
InsertIntoHighlightCoverAndPlayInfo()300 void HighlightRestore::InsertIntoHighlightCoverAndPlayInfo()
301 {
302     const std::string INSERT_PLAY_INFO_SQL =
303         "INSERT INTO tab_highlight_play_info (album_id, play_service_version, status) "
304         "VALUES (?, 0, 1)";
305     for (HighlightAlbumInfo &info : albumInfos_) {
306         if (info.highlightStatus != HIGHLIGHT_STATUS_SUCCESS) {
307             continue;
308         }
309 
310         vector<NativeRdb::ValuesBucket> coverValues;
311         for (const std::string &ratio : HIGHLIGHT_RATIO) {
312             NativeRdb::ValuesBucket value;
313             value.PutInt("album_id", info.id);
314             value.PutString("ratio", ratio);
315             value.PutInt("status", 1);
316             coverValues.emplace_back(value);
317         }
318         int64_t rowNum = 0;
319         int32_t errCode = BatchInsertWithRetry("tab_highlight_cover_info", coverValues, rowNum);
320         if (errCode != E_OK || rowNum != (int64_t) HIGHLIGHT_RATIO.size()) {
321             info.highlightStatus = HIGHLIGHT_STATUS_FAIL;
322             MEDIA_ERR_LOG("InsertIntoHighlightCover fail, %{public}s", info.ToString().c_str());
323             ErrorInfo errorInfo(RestoreError::INSERT_FAILED, 0, std::to_string(errCode),
324                 info.albumName + " insert into HighlightCover fail.");
325             UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).ReportError(errorInfo);
326         }
327 
328         if (info.highlightStatus != HIGHLIGHT_STATUS_SUCCESS) {
329             continue;
330         }
331 
332         std::vector<NativeRdb::ValueObject> playInfoBindArgs = { info.id };
333         errCode = BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb_, INSERT_PLAY_INFO_SQL, playInfoBindArgs);
334         if (errCode != E_OK) {
335             info.highlightStatus = HIGHLIGHT_STATUS_FAIL;
336             MEDIA_ERR_LOG("InsertIntoHighlightPlayInfo fail, %{public}s", info.ToString().c_str());
337             ErrorInfo errorInfo(RestoreError::INSERT_FAILED, 0, std::to_string(errCode),
338                 info.albumName + " insert into HighlightPlayInfo fail.");
339             UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).ReportError(errorInfo);
340         }
341     }
342 }
343 
UpdateHighlightIds()344 void HighlightRestore::UpdateHighlightIds()
345 {
346     CHECK_AND_RETURN(!albumInfos_.empty());
347     const std::string QUERY_SQL = "SELECT album_id, id FROM tab_highlight_album LIMIT ?, ?";
348     int rowCount = 0;
349     int offset = 0;
350     do {
351         std::vector<NativeRdb::ValueObject> params = {offset, PAGE_SIZE};
352         auto resultSet = BackupDatabaseUtils::QuerySql(mediaLibraryRdb_, QUERY_SQL, params);
353         if (resultSet == nullptr) {
354             MEDIA_ERR_LOG("resultSet is nullptr");
355             break;
356         }
357         while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
358             int32_t albumId = GetInt32Val("album_id", resultSet);
359             int32_t id = GetInt32Val("id", resultSet);
360             auto it = std::find_if(albumInfos_.begin(), albumInfos_.end(),
361                 [albumId](const HighlightAlbumInfo &info) {
362                     return info.albumIdNew == albumId;
363                 });
364             if (it == albumInfos_.end()) {
365                 continue;
366             }
367             it->id = id;
368         }
369         resultSet->GetRowCount(rowCount);
370         resultSet->Close();
371         offset += PAGE_SIZE;
372     } while (rowCount == PAGE_SIZE);
373 }
374 
RestoreMaps(std::vector<FileInfo> & fileInfos)375 void HighlightRestore::RestoreMaps(std::vector<FileInfo> &fileInfos)
376 {
377     if (albumInfos_.empty()) {
378         MEDIA_INFO_LOG("albumInfos_ is empty, no need to restore maps.");
379         return;
380     }
381     std::vector<NativeRdb::ValuesBucket> values;
382     BatchQueryPhoto(fileInfos);
383     for (const auto &fileInfo : fileInfos) {
384         UpdateMapInsertValues(values, fileInfo);
385     }
386     int64_t rowNum = 0;
387     int errCode = BatchInsertWithRetry("AnalysisPhotoMap", values, rowNum);
388     if (errCode != E_OK) {
389         MEDIA_ERR_LOG("RestoreMaps fail.");
390         ErrorInfo errorInfo(RestoreError::INSERT_FAILED, 0, std::to_string(errCode), "RestoreMaps fail.");
391         UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).ReportError(errorInfo);
392     }
393 }
394 
BatchQueryPhoto(std::vector<FileInfo> & fileInfos)395 void HighlightRestore::BatchQueryPhoto(std::vector<FileInfo> &fileInfos)
396 {
397     std::stringstream querySql;
398     querySql << "SELECT file_id, data FROM Photos WHERE data IN (";
399     std::vector<NativeRdb::ValueObject> params;
400     int32_t count = 0;
401     for (const auto &fileInfo : fileInfos) {
402         if (fileInfo.fileIdNew > 0) {
403             continue;
404         }
405         querySql << (count++ > 0 ? "," : "");
406         querySql << "?";
407         params.push_back(fileInfo.cloudPath);
408     }
409     querySql << ")";
410     if (params.empty()) {
411         return;
412     }
413 
414     auto resultSet = BackupDatabaseUtils::QuerySql(mediaLibraryRdb_, querySql.str(), params);
415     CHECK_AND_RETURN_LOG(resultSet != nullptr, "resultSet is nullptr");
416     std::vector<NativeRdb::ValuesBucket> values;
417     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
418         int32_t fileId = GetInt32Val("file_id", resultSet);
419         std::string data = GetStringVal("data", resultSet);
420         auto it = std::find_if(fileInfos.begin(), fileInfos.end(),
421             [data](const FileInfo &info) {
422                 return info.cloudPath == data;
423             });
424         if (it == fileInfos.end()) {
425             continue;
426         }
427         it->fileIdNew = fileId;
428     }
429     resultSet->Close();
430 }
431 
UpdateMapInsertValues(std::vector<NativeRdb::ValuesBucket> & values,const FileInfo & fileInfo)432 void HighlightRestore::UpdateMapInsertValues(std::vector<NativeRdb::ValuesBucket> &values, const FileInfo &fileInfo)
433 {
434     CHECK_AND_RETURN(fileInfo.fileIdNew > 0);
435     int32_t fileIdOld = fileInfo.fileIdOld;
436     auto it = std::find_if(albumInfos_.begin(), albumInfos_.end(),
437         [fileIdOld](const HighlightAlbumInfo &info) { return info.coverId == fileIdOld; });
438     if (it != albumInfos_.end()) {
439         it->coverUri = MediaFileUtils::GetUriByExtrConditions(PhotoColumn::PHOTO_URI_PREFIX,
440             std::to_string(fileInfo.fileIdNew), MediaFileUtils::GetExtraUri(fileInfo.displayName, fileInfo.cloudPath));
441         MEDIA_INFO_LOG("album %{public}s get coverUri %{public}s.", it->albumName.c_str(), it->coverUri.c_str());
442     }
443 
444     bool cond = ((fileInfo.storyIds.empty() && fileInfo.portraitIds.empty()) || fileInfo.storyChosen == 0);
445     CHECK_AND_RETURN(!cond);
446     std::stringstream storyIdss(fileInfo.storyIds);
447     std::string storyId;
448     while (std::getline(storyIdss, storyId, ',')) {
449         UpdateMapInsertValuesByStoryId(values, fileInfo, storyId);
450     }
451 
452     std::stringstream portraitIdss(fileInfo.portraitIds);
453     std::string portraitId;
454     while (std::getline(portraitIdss, portraitId, ',')) {
455         UpdateMapInsertValuesByStoryId(values, fileInfo, portraitId);
456     }
457 }
458 
UpdateMapInsertValuesByStoryId(std::vector<NativeRdb::ValuesBucket> & values,const FileInfo & fileInfo,const std::string & storyId)459 void HighlightRestore::UpdateMapInsertValuesByStoryId(std::vector<NativeRdb::ValuesBucket> &values,
460     const FileInfo &fileInfo, const std::string &storyId)
461 {
462     if (!MediaLibraryDataManagerUtils::IsNumber(storyId)) {
463         return;
464     }
465     int32_t albumIdOld = std::atoi(storyId.c_str());
466     auto it = std::find_if(albumInfos_.begin(), albumInfos_.end(),
467         [albumIdOld](const HighlightAlbumInfo &info) { return info.albumIdOld == albumIdOld; });
468     bool cond = (it == albumInfos_.end() || it->albumIdNew <= 0 || it->aiAlbumIdNew <= 0);
469     CHECK_AND_RETURN_LOG(!cond, "no such album of albumIdOld: %{public}d", albumIdOld);
470 
471     if (fileInfo.fileType == MediaType::MEDIA_TYPE_VIDEO) {
472         it->effectline.push_back(GetEffectline(fileInfo));
473     }
474     std::lock_guard<mutex> lock(counterMutex_);
475     std::string albumName = std::to_string(it->id) + it->albumName;
476     if (albumPhotoCounter_.count(albumName) == 0) {
477         albumPhotoCounter_[albumName] = 0;
478     }
479     albumPhotoCounter_[albumName]++;
480     values.push_back(GetMapInsertValue(it->albumIdNew, fileInfo.fileIdNew));
481     values.push_back(GetMapInsertValue(it->aiAlbumIdNew, fileInfo.fileIdNew));
482 }
483 
GetEffectline(const FileInfo & fileInfo)484 nlohmann::json HighlightRestore::GetEffectline(const FileInfo &fileInfo)
485 {
486     nlohmann::json effectline;
487     nlohmann::json fileId;
488     fileId.emplace_back(fileInfo.fileIdNew);
489     effectline["fileId"] = fileId;
490     nlohmann::json fileUri;
491     std::string effectVideoUri = MediaFileUtils::GetUriByExtrConditions(PhotoColumn::PHOTO_URI_PREFIX,
492         std::to_string(fileInfo.fileIdNew), MediaFileUtils::GetExtraUri(fileInfo.displayName, fileInfo.cloudPath));
493     fileUri.emplace_back(effectVideoUri);
494     effectline["fileUri"] = fileUri;
495     nlohmann::json filedatemodified;
496     filedatemodified.emplace_back(fileInfo.dateModified);
497     effectline["filedatemodified"] = filedatemodified;
498     effectline["effectVideoTrack"] = GetEffectVideoTrack(fileInfo.hashCode);
499 
500     effectline["effect"] = "TYPE_HILIGHT_CLIP";
501     effectline["effectVideoUri"] = effectVideoUri;
502     effectline["transitionId"] = "";
503     effectline["transitionVideoUri"] = "";
504     effectline["prefileId"] = nlohmann::json::array();
505     effectline["prefileUri"] = nlohmann::json::array();
506     effectline["prefiledatemodified"] = nlohmann::json::array();
507     return effectline;
508 }
509 
GetEffectVideoTrack(const std::string & hashCode)510 nlohmann::json HighlightRestore::GetEffectVideoTrack(const std::string &hashCode)
511 {
512     const std::string QUERY_SQL = "SELECT tracks FROM t_video_semantic_analysis "
513         "WHERE hash = ? ORDER BY confidence_probability DESC LIMIT 1";
514     std::vector<NativeRdb::ValueObject> params = { hashCode };
515     auto resultSet = BackupDatabaseUtils::QuerySql(galleryRdb_, QUERY_SQL, params);
516     bool cond = (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK);
517     CHECK_AND_RETURN_RET(!cond, nlohmann::json::array());
518 
519     std::string tracks = GetStringVal("tracks", resultSet);
520     resultSet->Close();
521     nlohmann::json effectVideoTrack = nlohmann::json::parse(tracks, nullptr, false);
522     if (effectVideoTrack.is_discarded()) {
523         MEDIA_ERR_LOG("EffectVideoTrack parse fail. tracks: %{public}s", tracks.c_str());
524         tracksParseFailCnt_++;
525         return nlohmann::json::array();
526     }
527     return effectVideoTrack;
528 }
529 
GetMapInsertValue(int32_t albumId,int32_t fileId)530 NativeRdb::ValuesBucket HighlightRestore::GetMapInsertValue(int32_t albumId, int32_t fileId)
531 {
532     NativeRdb::ValuesBucket value;
533     value.PutInt("map_album", albumId);
534     value.PutInt("map_asset", fileId);
535     return value;
536 }
537 
BatchInsertWithRetry(const std::string & tableName,std::vector<NativeRdb::ValuesBucket> & values,int64_t & rowNum)538 int32_t HighlightRestore::BatchInsertWithRetry(const std::string &tableName,
539     std::vector<NativeRdb::ValuesBucket> &values, int64_t &rowNum)
540 {
541     CHECK_AND_RETURN_RET(!values.empty(), 0);
542     int32_t errCode = E_ERR;
543     TransactionOperations trans{ __func__ };
544     trans.SetBackupRdbStore(mediaLibraryRdb_);
545     std::function<int(void)> func = [&]() -> int {
546         errCode = trans.BatchInsert(rowNum, tableName, values);
547         CHECK_AND_PRINT_LOG(errCode == E_OK,
548             "InsertSql failed, errCode: %{public}d, rowNum: %{public}ld.", errCode, (long)rowNum);
549         return errCode;
550     };
551     errCode = trans.RetryTrans(func, true);
552     CHECK_AND_PRINT_LOG(errCode == E_OK, "BatchInsertWithRetry: trans finish fail!, ret:%{public}d", errCode);
553     return errCode;
554 }
555 
UpdateAlbums()556 void HighlightRestore::UpdateAlbums()
557 {
558     const std::string UPDATE_ALBUM_SQL = "UPDATE AnalysisAlbum SET "
559         "cover_uri = ?, "
560         "count = (SELECT count(1) FROM AnalysisPhotoMap AS apm WHERE apm.map_album = AnalysisAlbum.album_id) "
561         "WHERE album_id IN (?, ?)";
562     const std::string UPDATE_COVER_KEY_SQL = "UPDATE tab_highlight_cover_info SET "
563         "cover_key = ?||'_'||ratio||'_'||? "
564         "WHERE album_id = ?";
565     const std::string UPDATE_PLAY_INFO_SQL = "UPDATE tab_highlight_play_info SET "
566         "play_info = ? "
567         "WHERE album_id = ?";
568     const std::string DELETE_ALBUM_SQL = "DELETE FROM AnalysisAlbum WHERE album_id IN (?, ?)";
569     const std::string DELETE_HIGHLIGHT_ALBUM_SQL = "DELETE FROM tab_highlight_album WHERE id = ?";
570     const std::string DELETE_HIGHLIGHT_COVER_SQL = "DELETE FROM tab_highlight_cover_info WHERE album_id = ?";
571     const std::string DELETE_HIGHLIGHT_PLAY_INFO_SQL = "DELETE FROM tab_highlight_play_info WHERE album_id = ?";
572 
573     for (const auto &info : albumInfos_) {
574         MEDIA_INFO_LOG("UpdateAlbums %{public}s", info.ToString().c_str());
575         if (info.highlightStatus != HIGHLIGHT_STATUS_FAIL) {
576             info.highlightStatus == HIGHLIGHT_STATUS_SUCCESS ? successCnt_++ : duplicateCnt_++;
577             BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb_, UPDATE_ALBUM_SQL,
578                 { info.coverUri, info.albumIdNew, info.aiAlbumIdNew });
579             BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb_, UPDATE_COVER_KEY_SQL,
580                 { info.albumName, info.coverUri, info.id });
581             nlohmann::json playInfo;
582             nlohmann::json effectline;
583             nlohmann::json effectlineArray = nlohmann::json::array();
584             for (const auto &e : info.effectline) {
585                 effectlineArray.emplace_back(e);
586             }
587             effectline["effectline"] = effectlineArray;
588             playInfo["effectline"] = effectline;
589             playInfo["beatsInfo"] = nlohmann::json::array();
590             playInfo["timeline"] = nlohmann::json::array();
591             BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb_, UPDATE_PLAY_INFO_SQL,
592                 { playInfo.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace), info.id });
593         } else {
594             failCnt_++;
595             BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb_, DELETE_ALBUM_SQL, { info.albumIdNew, info.aiAlbumIdNew });
596             BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb_, DELETE_HIGHLIGHT_ALBUM_SQL, { info.id });
597             BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb_, DELETE_HIGHLIGHT_COVER_SQL, { info.id });
598             BackupDatabaseUtils::ExecuteSQL(mediaLibraryRdb_, DELETE_HIGHLIGHT_PLAY_INFO_SQL, { info.id });
599         }
600     }
601 
602     ReportHighlightRestoreTask();
603 }
604 
ReportHighlightRestoreTask()605 void HighlightRestore::ReportHighlightRestoreTask()
606 {
607     int maxCnt = 0;
608     int totalCnt = 0;
609     for (auto &counter : albumPhotoCounter_) {
610         maxCnt = maxCnt > counter.second ? maxCnt : counter.second;
611         totalCnt += counter.second;
612         MEDIA_INFO_LOG("UpdateMapInsertValues albumName: %{public}s, photo count: %{public}d",
613             counter.first.c_str(), counter.second);
614         UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_)
615             .Report("Highlight Photo Map", std::to_string(HIGHLIGHT_STATUS_SUCCESS),
616             "albumName: " + counter.first + ", photo count: " + std::to_string(counter.second));
617     }
618     double meanCnt = albumPhotoCounter_.size() == 0 ? 0 : (double) totalCnt / albumPhotoCounter_.size();
619     MEDIA_INFO_LOG("Highlight photos max: %{public}d, mean: %{public}f", maxCnt, meanCnt);
620     UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_)
621         .Report("Highlight Photos", std::to_string(HIGHLIGHT_STATUS_SUCCESS),
622         "max: " + std::to_string(maxCnt) + ", mean: " + std::to_string(meanCnt));
623 
624     MEDIA_INFO_LOG("Highlight Restore successCnt_: %{public}d, duplicateCnt_: %{public}d, failCnt_: %{public}d",
625         successCnt_.load(), duplicateCnt_.load(), failCnt_.load());
626     UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_)
627         .Report("Highlight Restore", std::to_string(HIGHLIGHT_STATUS_SUCCESS),
628         "successCnt_: " + std::to_string(successCnt_) + ", duplicateCnt_: " + std::to_string(duplicateCnt_) +
629         ", failCnt_: " + std::to_string(failCnt_));
630     MEDIA_ERR_LOG("EffectVideoTrack parse fail. totalCnt: %{public}d", tracksParseFailCnt_.load());
631     ErrorInfo errorInfo(RestoreError::PARSE_TRACK_FAILED, 0, "",
632         "EffectVideoTrack parse fail. totalCnt: " + std::to_string(tracksParseFailCnt_));
633     UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_).ReportError(errorInfo);
634 }
635 } // namespace OHOS::Media