Guest User

Untitled

a guest
Mar 11th, 2026
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.73 KB | None | 0 0
  1. private async Task<Result<string>> MigrateFileAndGetNewUrl(OldMediaData mediaData, Maybe<string> title, string codeNamePrefix = "")
  2. {
  3. var fileType = _fileHelper.GetFileType(mediaData.Extension);
  4.  
  5. // Get Binary and Generate the assetMetadataWithSource
  6. var assetBinary = Maybe<byte[]>.None;
  7.  
  8. if (mediaData.IsAttachment) {
  9. var attachmentBinary = await GetAttachmentBinary(mediaData.FileGuid);
  10. if (attachmentBinary.Length == 0) {
  11. // try to get directly
  12. attachmentBinary = await httpClient.GetByteArrayAsync($"https://direct.physicsclassroom.com/" + mediaData.PathLookup.Trim('/'));
  13. }
  14. if (attachmentBinary.Length > 0) {
  15. assetBinary = attachmentBinary;
  16. }
  17. } else {
  18. // get file data stream, first check local
  19. var localPath = $"E:\\web\\dev.physicsclassroom.com-MediaFiles\\{mediaData.PathLookup.Replace("/", "\\")}";
  20. var fileInfo = new System.IO.FileInfo(localPath);
  21. if (fileInfo.Exists) {
  22. var fileStream = File.Open(localPath, FileMode.Open, FileAccess.ReadWrite);
  23. using (MemoryStream ms = new MemoryStream()) {
  24. fileStream.CopyTo(ms);
  25. assetBinary = ms.ToArray();
  26. }
  27. fileStream.Close();
  28. } else {
  29. // try to get from url
  30. var url = $"https://direct.physicsclassroom.com/" + mediaData.PathLookup.Trim('/');
  31. HttpResponseMessage response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));
  32. if (!(response.Content.Headers.ContentType?.MediaType ?? "text/html").Equals("text/html", StringComparison.OrdinalIgnoreCase)) {
  33. assetBinary = await httpClient.GetByteArrayAsync(url);
  34. }
  35. }
  36. }
  37.  
  38. if (!assetBinary.TryGetValue(out var assetBinaryVal)) {
  39. return Result.Failure<string>("No binary data found");
  40. }
  41.  
  42. // Creates a metadata object that describes the uploaded asset
  43. var assetMetadata = new ContentItemAssetMetadata() {
  44. Extension = Path.GetExtension(mediaData.Extension),
  45. Identifier = Guid.NewGuid(), // the old Media Guid will go to the Content Item, not the Content Asset Item
  46. LastModified = mediaData.LastModified,
  47. Name = $"{Path.GetFileName(mediaData.FileName)}",
  48. Size = assetBinaryVal.Length
  49. };
  50. if (mediaData.ImageWidth.TryGetValue(out var width) && mediaData.ImageHeight.TryGetValue(out var height)) {
  51. assetMetadata.Width = width;
  52. assetMetadata.Height = height;
  53. }
  54.  
  55. // Try writing to temp location file
  56. var tempPath = "E:\\web\\physicsclassroom.com-new\\Untracked-Other\\tempfiles\\" + mediaData.FileName;
  57. await File.WriteAllBytesAsync(tempPath, assetBinaryVal);
  58. var file = CMS.IO.FileInfo.New(tempPath);
  59. var fileSource = new ContentItemAssetFileSource(file.FullName, true);
  60.  
  61. var assetMetadataWithSource = new ContentItemAssetMetadataWithSource(fileSource, assetMetadata);
  62. var workSpaceData = GetWorkspaceAndRootName(mediaData);
  63. var displayName = mediaData.FileName.Replace(mediaData.Extension, "").SplitAndRemoveEntries("-").Select(x => $"{x.Substring(0, 1).ToUpper()}{x.Substring(1)}").StringJoin(" ") + " " + mediaData.FileName;
  64. displayName = displayName.Length > 100 ? displayName.Substring(0, 100) : displayName;
  65. var contentItemName = ValidationHelper.GetCodeName($"{codeNamePrefix}{mediaData.FileName.Replace(mediaData.Extension, "")}-{mediaData.FileGuid.ToString().Split('-')[0]}");
  66. contentItemName = contentItemName.Length > 100 ? contentItemName.Substring(0, 100) : contentItemName;
  67. int contentItemID = 0;
  68. switch (fileType) {
  69. default:
  70. case FileTypes.Image:
  71. var parametersImage = new CreateContentItemParameters(
  72. contentTypeName: Generic.Image.CONTENT_TYPE_NAME,
  73. name: contentItemName,
  74. displayName: displayName,
  75. languageName: "en",
  76. workspaceName: workSpaceData.WorkspaceName) {
  77. IsSecured = mediaData.IsAttachment && SecureNodeAliasPaths.Any(x => mediaData.PathLookup.IndexOf(x) > 0)
  78. };
  79.  
  80. var descriptionFromAira = false;
  81. var imageLongDescription = Maybe<string>.None;
  82. var titleValue = mediaData.FileTitle.GetValueOrDefault(string.Empty);
  83.  
  84. if (!mediaData.FileDescription.TryGetValue(out var fileDescription)) {
  85. // No description - need to generate and use
  86. if ((await _openAIService.GenerateImageDescriptionAsync(assetBinaryVal, mediaData.MimeType)).TryGetValue(out var description)) {
  87. fileDescription = description.ShortDescription;
  88. imageLongDescription = description.LongDescription;
  89. descriptionFromAira = true;
  90. }
  91. } else if (mediaData.FolderPath.Contains("chemtutorial", StringComparison.OrdinalIgnoreCase)) {
  92. if ((await _openAIService.GenerateImageDescriptionAsync(assetBinaryVal, mediaData.MimeType)).TryGetValue(out var description)) {
  93. // Use AI for long description for tom's tutorial images.
  94. imageLongDescription = description.LongDescription;
  95. descriptionFromAira = true;
  96. }
  97. } else if (mediaData.FolderPath.Contains("physics-tutorial", StringComparison.OrdinalIgnoreCase)) {
  98. if (titleValue.Equals(fileDescription, StringComparison.OrdinalIgnoreCase)) {
  99. // They match, so the title and File description are the same and there is no long description needed
  100. imageLongDescription = Maybe.None;
  101. fileDescription = titleValue;
  102. } else {
  103. // They don't match, so the "Title" is the description, and the description is the Long Description
  104. imageLongDescription = fileDescription;
  105. fileDescription = titleValue;
  106. }
  107. }
  108.  
  109. titleValue = titleValue.AsNullOrWhitespaceMaybe().GetValueOrDefault(parametersImage.DisplayName);
  110. titleValue = titleValue.Length > 100 ? titleValue.Substring(0, 97)+"..." : titleValue;
  111. var isAnimation = parametersImage.DisplayName.Contains("animation", StringComparison.OrdinalIgnoreCase) || titleValue.Contains("animation", StringComparison.OrdinalIgnoreCase) || (fileDescription ?? string.Empty).Contains("animation", StringComparison.OrdinalIgnoreCase);
  112. ContentItemData itemDataImage = new ContentItemData(new Dictionary<string, object>{
  113. { nameof(Image.ImageTitle), titleValue },
  114. { nameof(Image.ImageDescription), fileDescription.GetValueOrDefault(string.Empty) },
  115. { nameof(Image.ImageDescriptionLong), imageLongDescription.GetValueOrDefault(string.Empty) },
  116. { nameof(Image.ImageDescriptionFromAira), descriptionFromAira },
  117. { nameof(Image.ImageFile), assetMetadataWithSource },
  118. { nameof(Image.ImageIsAnimation), isAnimation }
  119. });
  120.  
  121. // Creates the content item in the database
  122. try {
  123. contentItemID = await _contentItemManager.Create(parametersImage, itemDataImage);
  124. } catch (CodeNameNotUniqueException) {
  125. // get existing
  126. contentItemID = (await new DataQuery() { CustomQueryText = $"Select ContentItemID from CMS_ContentItem where ContentItemName = '{parametersImage.Name}'" }.GetDataContainerResultAsync()).First().GetValue("ContentItemID", 0); ;
  127. }
  128. break;
  129. case FileTypes.Audio:
  130. var parametersAudio = new CreateContentItemParameters(
  131. contentTypeName: Generic.Audio.CONTENT_TYPE_NAME,
  132. name: contentItemName,
  133. displayName: displayName,
  134. languageName: "en",
  135. workspaceName: workSpaceData.WorkspaceName) {
  136. IsSecured = mediaData.IsAttachment && SecureNodeAliasPaths.Any(x => mediaData.PathLookup.IndexOf(x) > 0)
  137. };
  138.  
  139. // Transcribe
  140. var transcription = (await _openAIService.TranscribeAudio(assetBinaryVal, mediaData.FileName)).TryGetValue(out var transcriptionVal) ? transcriptionVal : Maybe<string>.None;
  141.  
  142. ContentItemData itemDataAudio = new ContentItemData(new Dictionary<string, object>{
  143. { nameof(Audio.AudioTitle), parametersAudio.DisplayName },
  144. { nameof(Audio.AudioDescription), mediaData.FileDescription.GetValueOrDefault(string.Empty) },
  145. { nameof(Audio.AudioTranscript), transcription.GetValueOrDefault(string.Empty) },
  146. { nameof(Audio.AudioFile), assetMetadataWithSource },
  147. });
  148.  
  149. // Creates the content item in the database
  150. try {
  151. contentItemID = await _contentItemManager.Create(parametersAudio, itemDataAudio);
  152. } catch (CodeNameNotUniqueException) {
  153. // get existing
  154. contentItemID = (await new DataQuery() { CustomQueryText = $"Select ContentItemID from CMS_ContentItem where ContentItemName = '{parametersAudio.Name}'" }.GetDataContainerResultAsync()).First().GetValue("ContentItemID", 0); ;
  155. }
  156. break;
  157. case FileTypes.Video:
  158. var parametersVideo = new CreateContentItemParameters(
  159. contentTypeName: Generic.Video.CONTENT_TYPE_NAME,
  160. name: contentItemName,
  161. displayName: displayName,
  162. languageName: "en",
  163. workspaceName: workSpaceData.WorkspaceName) {
  164. IsSecured = mediaData.IsAttachment && SecureNodeAliasPaths.Any(x => mediaData.PathLookup.IndexOf(x) > 0)
  165. };
  166.  
  167. ContentItemData itemDataVideo = new ContentItemData(new Dictionary<string, object>{
  168. { nameof(Video.VideoTitle), parametersVideo.DisplayName },
  169. { nameof(Video.VideoDescription), mediaData.FileDescription.GetValueOrDefault(string.Empty) },
  170. { nameof(Video.VideoFile), assetMetadataWithSource },
  171. });
  172.  
  173. // Creates the content item in the database
  174. try {
  175. contentItemID = await _contentItemManager.Create(parametersVideo, itemDataVideo);
  176. } catch (CodeNameNotUniqueException) {
  177. // get existing
  178. contentItemID = (await new DataQuery() { CustomQueryText = $"Select ContentItemID from CMS_ContentItem where ContentItemName = '{parametersVideo.Name}'" }.GetDataContainerResultAsync()).First().GetValue("ContentItemID", 0); ;
  179. }
  180. break;
  181. case FileTypes.File:
  182. var parametersFile = new CreateContentItemParameters(
  183. contentTypeName: Generic.File.CONTENT_TYPE_NAME,
  184. name: ValidationHelper.GetCodeName($"{codeNamePrefix}{mediaData.FileName.Replace(mediaData.Extension, "")}-{mediaData.FileGuid.ToString().Split('-')[0]}"),
  185. displayName: mediaData.FileTitle.GetValueOrDefault(title.GetValueOrDefault(mediaData.FileName)),
  186. languageName: "en",
  187. workspaceName: workSpaceData.WorkspaceName) {
  188. IsSecured = mediaData.IsAttachment && SecureNodeAliasPaths.Any(x => mediaData.PathLookup.IndexOf(x) > 0),
  189. };
  190.  
  191. ContentItemData itemDataFile = new ContentItemData(new Dictionary<string, object>{
  192. { nameof(Generic.File.FileTitle), parametersFile.DisplayName },
  193. { nameof(Generic.File.FileDescription), mediaData.FileDescription.GetValueOrDefault(string.Empty) },
  194. { nameof(Generic.File.FileFile), assetMetadataWithSource },
  195. });
  196.  
  197. // Creates the content item in the database
  198. try {
  199. contentItemID = await _contentItemManager.Create(parametersFile, itemDataFile);
  200. } catch (CodeNameNotUniqueException ex) {
  201. // get existing
  202. contentItemID = (await new DataQuery() { CustomQueryText = $"Select ContentItemID from CMS_ContentItem where ContentItemName = '{parametersFile.Name}'" }.GetDataContainerResultAsync()).First().GetValue("ContentItemID", 0); ;
  203. }
  204. break;
  205. }
  206.  
  207. // delete temp file
  208. File.Delete(tempPath);
  209.  
  210. if (contentItemID > 0) {
  211. // set guid, can't seem to figure out how to adjust aside from SQL, and then manually moving files *bleh*
  212.  
  213. // get current GUID so we can pinpoint the file
  214. var currentQuery = $"Select ContentItemGUID from CMS_ContentItem where ContentItemID = {contentItemID}";
  215. var oldGuid = (await new DataQuery() { CustomQueryText = currentQuery }.GetDataContainerResultAsync()).First().GetValue<Guid>("ContentItemGUID", Guid.Empty);
  216.  
  217. var fileGuidQuery = $@"select JSON_VALUE(FileData, '$.Identifier') as ID from (
  218. select FileFile as FileData from Generic_File GI inner join CMS_ContentItemCommonData CD on CD.ContentItemCommonDataID = GI.ContentItemDataCommonDataID where ContentItemCommonDataContentItemID = {contentItemID}
  219. union all
  220. select AudioFile as FileData from Generic_Audio GI inner join CMS_ContentItemCommonData CD on CD.ContentItemCommonDataID = GI.ContentItemDataCommonDataID where ContentItemCommonDataContentItemID = {contentItemID}
  221. UNION ALL
  222. select ImageFile as FileData from Generic_Image GI inner join CMS_ContentItemCommonData CD on CD.ContentItemCommonDataID = GI.ContentItemDataCommonDataID where ContentItemCommonDataContentItemID = {contentItemID}
  223. UNION ALL
  224. select VideoFile as FileData from Generic_Video GI inner join CMS_ContentItemCommonData CD on CD.ContentItemCommonDataID = GI.ContentItemDataCommonDataID where ContentItemCommonDataContentItemID = {contentItemID}
  225. ) item";
  226. // TODO: Add in the HTML Module Files to this as well so it will work
  227. var fileGuid = (await new DataQuery() { CustomQueryText = fileGuidQuery }.GetDataContainerResultAsync()).First().GetValue("ID", string.Empty).ToString().ToLower();
  228.  
  229. var oldGuidPath = oldGuid.ToString().Substring(0, 2).ToLower() + "/" + oldGuid.ToString().ToLower();
  230. var sourceFolder = "assets/contentitems/" + oldGuidPath;
  231.  
  232. var newGuidPath = mediaData.FileGuid.ToString().Substring(0, 2).ToLower() + "/" + mediaData.FileGuid.ToString().ToLower();
  233. var targetFolder = sourceFolder.Replace(oldGuidPath, newGuidPath);
  234.  
  235.  
  236. var blobServiceClient = new BlobServiceClient("DefaultEndpointsProtocol=https;AccountName=XXXREPLACEMEXXX;AccountKey=XXXREPLACEMEXXX;EndpointSuffix=core.windows.net");
  237. var containerClient = blobServiceClient.GetBlobContainerClient("production");
  238. var blobs = containerClient.GetBlobs(BlobTraits.None, BlobStates.None, sourceFolder);
  239. foreach (var blob in blobs) {
  240. var targetBlobClient = containerClient.GetBlobClient($"{blob.Name.Replace(oldGuidPath, newGuidPath)}");
  241. var blobUri = new Uri($"{containerClient.Uri}/{blob.Name}");
  242. var copyOp = await targetBlobClient.StartCopyFromUriAsync(blobUri);
  243. await copyOp.WaitForCompletionAsync();
  244. containerClient.DeleteBlob(blob.Name);
  245. }
  246.  
  247. var updateQuery = $"update CMS_ContentItem set ContentItemGUID = '{mediaData.FileGuid}' where ContentItemID = {contentItemID}";
  248. await new DataQuery() { CustomQueryText = updateQuery }.ExecuteNonQueryAsync();
  249.  
  250. // Move to proper Folder
  251. var fileFolderId = await GetOrCreateFolder(mediaData);
  252. await _contentFolderManager.MoveItems(fileFolderId, [contentItemID]);
  253.  
  254. // Publish it
  255. await _contentItemManager.TryPublish(contentItemID, "en");
  256.  
  257. // Return new URL
  258. return $"/getcontentasset/{mediaData.FileGuid}/{_fileHelper.GetFileContentAssetItemFieldGuid(mediaData.Extension)}/{HttpUtility.UrlEncode(mediaData.FileName.ToLower())}";
  259. }
  260.  
  261. return Result.Failure<string>("No Content Item Created");
  262. }
Advertisement
Add Comment
Please, Sign In to add comment