1 /* 2 * Copyright (C) 2024-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 #ifndef OHOS_MEDIA_PHOTO_DISPLAYNAME_OPERATIOIN_H 17 #define OHOS_MEDIA_PHOTO_DISPLAYNAME_OPERATIOIN_H 18 19 #include <string> 20 #include <vector> 21 #include <regex> 22 #include <sstream> 23 24 #include "rdb_store.h" 25 #include "result_set_utils.h" 26 #include "userfile_manager_types.h" 27 #include "media_column.h" 28 #include "media_log.h" 29 30 namespace OHOS::Media { 31 class PhotoDisplayNameOperation { 32 private: 33 struct PhotoAssetInfo { 34 std::string displayName; 35 int32_t subtype; 36 int32_t ownerAlbumId; 37 }; 38 class DisplayNameInfo { 39 public: DisplayNameInfo(const PhotoAssetInfo & photoAssetInfo)40 explicit DisplayNameInfo(const PhotoAssetInfo &photoAssetInfo) 41 { 42 ParseDisplayName(photoAssetInfo); 43 } 44 ToString()45 std::string ToString() 46 { 47 std::string yearMonthDayStr; 48 std::string hourMinuteSecondStr; 49 if (this->yearMonthDay != 0 && this->hourMinuteSecond != 0) { 50 std::ostringstream yearMonthDayStream; 51 yearMonthDayStream << std::setw(YEAR_MONTH_DAY_LENGTH) << std::setfill('0') << this->yearMonthDay; 52 std::ostringstream hourMinuteSecondStream; 53 hourMinuteSecondStream << std::setw(HOUR_MINUTE_SECOND_LENGTH) << std::setfill('0') 54 << this->hourMinuteSecond; 55 yearMonthDayStr = "_" + yearMonthDayStream.str(); 56 hourMinuteSecondStr = "_" + hourMinuteSecondStream.str(); 57 } else { 58 yearMonthDayStr = this->yearMonthDay == 0 ? "" : "_" + std::to_string(this->yearMonthDay); 59 hourMinuteSecondStr = this->hourMinuteSecond == 0 ? "" : "_" + std::to_string(this->hourMinuteSecond); 60 } 61 return this->prefix + yearMonthDayStr + hourMinuteSecondStr + this->suffix; 62 } 63 Next()64 std::string Next() 65 { 66 this->hourMinuteSecond++; 67 return this->ToString(); 68 } 69 70 private: ParseDisplayName(const PhotoAssetInfo & photoAssetInfo)71 void ParseDisplayName(const PhotoAssetInfo &photoAssetInfo) 72 { 73 if (photoAssetInfo.subtype == static_cast<int32_t>(PhotoSubType::BURST)) { 74 ParseBurstDisplayName(photoAssetInfo); 75 return; 76 } 77 ParseNormalDisplayName(photoAssetInfo); 78 return; 79 } 80 ParseBurstDisplayName(const PhotoAssetInfo & photoAssetInfo)81 void ParseBurstDisplayName(const PhotoAssetInfo &photoAssetInfo) 82 { 83 bool isValid = photoAssetInfo.subtype == static_cast<int32_t>(PhotoSubType::BURST); 84 isValid = isValid && photoAssetInfo.displayName.size() > BURST_DISPLAY_NAME_MIN_LENGTH; 85 if (!isValid) { 86 return ParseNormalDisplayName(photoAssetInfo); 87 } 88 std::string displayName = photoAssetInfo.displayName; 89 std::regex pattern(R"(IMG_\d{8}_\d{6}_)", std::regex_constants::icase); 90 std::smatch match; 91 if (!std::regex_search(displayName, match, pattern)) { 92 return ParseNormalDisplayName(photoAssetInfo); 93 } 94 std::vector<std::string> parts; 95 std::istringstream iss(displayName); 96 std::string part; 97 while (std::getline(iss, part, '_')) { 98 parts.push_back(part); 99 } 100 if (parts.size() >= BURST_DISPLAY_NAME_MIN_SUBLINE_COUNT) { 101 this->prefix = parts[0]; 102 this->yearMonthDay = this->ToNumber(parts[BURST_DISPLAY_NAME_YEAR_INDEX]); 103 this->hourMinuteSecond = this->ToNumber(parts[BURST_DISPLAY_NAME_HOUR_INDEX]); 104 this->suffix = displayName.substr(BURST_DISPLAY_NAME_MIN_LENGTH - 1); 105 } 106 MEDIA_INFO_LOG("ParseBurstDisplayName Original display name: %{public}s, BurstDisplayNameInfo: %{public}s", 107 displayName.c_str(), 108 this->ToString().c_str()); 109 } 110 ToNumber(const std::string & str)111 int32_t ToNumber(const std::string &str) 112 { 113 char *end; 114 long number = std::strtol(str.c_str(), &end, 10); 115 116 if (*end != '\0') { 117 MEDIA_ERR_LOG("ToNumber failed, has invalid char. str: %{public}s", str.c_str()); 118 return 0; 119 } else if (number < INT_MIN || number > INT_MAX) { 120 MEDIA_ERR_LOG("ToNumber failed, number overflow. str: %{public}s", str.c_str()); 121 return 0; 122 } 123 return static_cast<int32_t>(number); 124 } 125 ParseNormalDisplayName(const PhotoAssetInfo & photoAssetInfo)126 void ParseNormalDisplayName(const PhotoAssetInfo &photoAssetInfo) 127 { 128 std::string displayName = photoAssetInfo.displayName; 129 size_t dotPos = displayName.rfind('.'); 130 if (dotPos != std::string::npos) { 131 this->prefix = displayName.substr(0, dotPos); 132 this->suffix = displayName.substr(dotPos); // include dot, e.g. ".jpg" 133 } 134 MEDIA_INFO_LOG("ParseNormalDisplayName Original display name: %{public}s, BurstDisplayNameInfo: %{public}s", 135 displayName.c_str(), 136 this->ToString().c_str()); 137 } 138 139 private: 140 enum { 141 YEAR_MONTH_DAY_LENGTH = 8, 142 HOUR_MINUTE_SECOND_LENGTH = 6, 143 BURST_DISPLAY_NAME_MIN_LENGTH = 20, 144 BURST_DISPLAY_NAME_YEAR_INDEX = 1, 145 BURST_DISPLAY_NAME_HOUR_INDEX = 2, 146 BURST_DISPLAY_NAME_MIN_SUBLINE_COUNT = 3 147 }; 148 std::string prefix; 149 int32_t yearMonthDay; 150 int32_t hourMinuteSecond; 151 std::string suffix; 152 }; 153 154 public: FindDisplayName(const std::shared_ptr<MediaLibraryRdbStore> rdbStore,const std::shared_ptr<NativeRdb::ResultSet> & resultSet,const int32_t targetAlbumId)155 std::string FindDisplayName(const std::shared_ptr<MediaLibraryRdbStore> rdbStore, 156 const std::shared_ptr<NativeRdb::ResultSet> &resultSet, const int32_t targetAlbumId) 157 { 158 if (resultSet == nullptr || targetAlbumId <= 0) { 159 MEDIA_ERR_LOG("Media_Operation: FindBurstKey: resultSet is null or targetAlbumId is invalid"); 160 return ""; 161 } 162 // Build the photo asset info. 163 PhotoDisplayNameOperation::PhotoAssetInfo photoAssetInfo; 164 photoAssetInfo.displayName = GetStringVal(MediaColumn::MEDIA_NAME, resultSet); 165 photoAssetInfo.subtype = GetInt32Val(PhotoColumn::PHOTO_SUBTYPE, resultSet); 166 photoAssetInfo.ownerAlbumId = targetAlbumId; 167 return this->FindDislayName(rdbStore, photoAssetInfo); 168 } 169 170 private: FindDislayName(const std::shared_ptr<MediaLibraryRdbStore> rdbStore,const PhotoAssetInfo & photoAssetInfo)171 std::string FindDislayName(const std::shared_ptr<MediaLibraryRdbStore> rdbStore, 172 const PhotoAssetInfo &photoAssetInfo) 173 { 174 DisplayNameInfo displayNameInfo(photoAssetInfo); 175 std::string displayName = displayNameInfo.ToString(); 176 int32_t retryCount = 0; 177 const int32_t MAX_RETRY_COUNT = 100; 178 while (IsDisplayNameExists(rdbStore, photoAssetInfo.ownerAlbumId, displayName)) { 179 displayName = displayNameInfo.Next(); 180 if (retryCount++ > MAX_RETRY_COUNT) { 181 MEDIA_ERR_LOG("Media_Operation: can not find unique display after retry %{public}d", MAX_RETRY_COUNT); 182 break; 183 } 184 } 185 if (photoAssetInfo.displayName != displayName) { 186 MEDIA_INFO_LOG("Media_Operation: displayName changed from %{public}s to %{public}s", 187 photoAssetInfo.displayName.c_str(), 188 displayName.c_str()); 189 } 190 return displayName; 191 } 192 IsDisplayNameExists(const std::shared_ptr<MediaLibraryRdbStore> rdbStore,const int32_t ownerAlbumId,const std::string & displayName)193 bool IsDisplayNameExists(const std::shared_ptr<MediaLibraryRdbStore> rdbStore, const int32_t ownerAlbumId, 194 const std::string &displayName) 195 { 196 if (ownerAlbumId <= 0 || displayName.empty()) { 197 return false; 198 } 199 std::string querySql = this->SQL_PHOTOS_TABLE_QUERY_DISPLAY_NAME; 200 const std::vector<NativeRdb::ValueObject> bindArgs = {ownerAlbumId, displayName}; 201 auto resultSet = rdbStore->QuerySql(querySql, bindArgs); 202 if (resultSet == nullptr || resultSet->GoToFirstRow() != NativeRdb::E_OK) { 203 return false; 204 } 205 std::string displayNameInDb = GetStringVal(MediaColumn::MEDIA_NAME, resultSet); 206 return displayNameInDb.size() > 0; 207 } 208 209 private: 210 const std::string SQL_PHOTOS_TABLE_QUERY_DISPLAY_NAME = "\ 211 SELECT \ 212 display_name \ 213 FROM Photos \ 214 WHERE owner_album_id = ? \ 215 AND LOWER(display_name) = LOWER(?) \ 216 LIMIT 1;"; 217 }; 218 } // namespace OHOS::Media 219 #endif // OHOS_MEDIA_PHOTO_DISPLAYNAME_OPERATIOIN_H