xmp and json sidecar support
This commit is contained in:
@ -1,5 +1,9 @@
|
|||||||
using CliWrap;
|
using CliWrap;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Phares.Metadata.Models;
|
||||||
|
using Phares.Metadata.Models.Stateless;
|
||||||
|
using Phares.Shared.Models;
|
||||||
|
using Phares.Shared.Models.Stateless;
|
||||||
using ShellProgressBar;
|
using ShellProgressBar;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
@ -7,11 +11,7 @@ using System.Drawing;
|
|||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Phares.Metadata.Models;
|
|
||||||
using Phares.Metadata.Models.Stateless;
|
|
||||||
using View_by_Distance.Rename.Models;
|
using View_by_Distance.Rename.Models;
|
||||||
using Phares.Shared.Models;
|
|
||||||
using Phares.Shared.Models.Stateless;
|
|
||||||
|
|
||||||
namespace View_by_Distance.Rename;
|
namespace View_by_Distance.Rename;
|
||||||
|
|
||||||
@ -403,7 +403,7 @@ public partial class Rename : IRename, IDisposable
|
|||||||
bool hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(keywords.Contains);
|
bool hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(keywords.Contains);
|
||||||
string checkFileExtension = exifDirectory.FilePath.ExtensionLowered == jpeg ? jpg : exifDirectory.FilePath.ExtensionLowered;
|
string checkFileExtension = exifDirectory.FilePath.ExtensionLowered == jpeg ? jpg : exifDirectory.FilePath.ExtensionLowered;
|
||||||
bool hasDateTimeOriginal = dateTime is not null;
|
bool hasDateTimeOriginal = dateTime is not null;
|
||||||
string paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, exifDirectory.FilePath.Id.Value, exifDirectory.FilePath.ExtensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, i);
|
string paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, exifDirectory.FilePath, hasIgnoreKeyword, hasDateTimeOriginal, i);
|
||||||
string checkDirectory = appSettings.RenameSettings.InPlaceWithOriginalName ? Path.Combine(exifDirectory.FilePath.DirectoryFullPath, exifDirectory.FilePath.FileNameFirstSegment) : exifDirectory.FilePath.DirectoryFullPath;
|
string checkDirectory = appSettings.RenameSettings.InPlaceWithOriginalName ? Path.Combine(exifDirectory.FilePath.DirectoryFullPath, exifDirectory.FilePath.FileNameFirstSegment) : exifDirectory.FilePath.DirectoryFullPath;
|
||||||
string checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
|
string checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
|
||||||
if (checkFile != exifDirectory.FilePath.FullName)
|
if (checkFile != exifDirectory.FilePath.FullName)
|
||||||
@ -483,7 +483,7 @@ public partial class Rename : IRename, IDisposable
|
|||||||
{
|
{
|
||||||
if (record.ExifDirectory.FilePath.Id is null)
|
if (record.ExifDirectory.FilePath.Id is null)
|
||||||
continue;
|
continue;
|
||||||
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.ExtensionLowered, record.HasIgnoreKeyword, record.HasDateTimeOriginal, index: null);
|
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath, record.HasIgnoreKeyword, record.HasDateTimeOriginal, index: null);
|
||||||
identifier = new([], record.HasDateTimeOriginal, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.Length, paddedId, record.DateTime.Ticks);
|
identifier = new([], record.HasDateTimeOriginal, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.Length, paddedId, record.DateTime.Ticks);
|
||||||
identifiers.Add(identifier);
|
identifiers.Add(identifier);
|
||||||
}
|
}
|
||||||
@ -523,7 +523,7 @@ public partial class Rename : IRename, IDisposable
|
|||||||
record = sorted[i];
|
record = sorted[i];
|
||||||
if (record.ExifDirectory.FilePath.Id is null)
|
if (record.ExifDirectory.FilePath.Id is null)
|
||||||
continue;
|
continue;
|
||||||
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.ExtensionLowered, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i);
|
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i);
|
||||||
checkDirectory = GetCheckDirectory(appSettings, directoryInfo, record, ids, multipleDirectoriesWithFiles, paddedId);
|
checkDirectory = GetCheckDirectory(appSettings, directoryInfo, record, ids, multipleDirectoriesWithFiles, paddedId);
|
||||||
if (string.IsNullOrEmpty(checkDirectory))
|
if (string.IsNullOrEmpty(checkDirectory))
|
||||||
continue;
|
continue;
|
||||||
|
@ -29,16 +29,19 @@ public interface IId
|
|||||||
public static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
|
public static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
|
||||||
fileNameFirstSegment.Length - 1 == metadataSettings.IntMinValueLength && fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
|
fileNameFirstSegment.Length - 1 == metadataSettings.IntMinValueLength && fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
|
||||||
|
|
||||||
|
public static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath, int? index) =>
|
||||||
|
Id.GetPaddedId(resultSettings, metadataSettings, filePath, index);
|
||||||
|
|
||||||
public static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
|
public static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
|
||||||
fileNameFirstSegment.Length == metadataSettings.IntMinValueLength + sortOrderOnlyLengthIndex + 1
|
fileNameFirstSegment.Length == metadataSettings.IntMinValueLength + sortOrderOnlyLengthIndex + 1
|
||||||
&& fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9'
|
&& fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9'
|
||||||
&& fileNameFirstSegment.All(char.IsNumber);
|
&& fileNameFirstSegment.All(char.IsNumber);
|
||||||
|
|
||||||
public static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
|
public static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
||||||
Id.GetIntelligentId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
|
filePath.Id is null ? throw new Exception() : GetPaddedId(resultSettings, metadataSettings, filePath.Id.Value, filePath.NameWithoutExtension, filePath.ExtensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
||||||
|
|
||||||
public static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
public static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string nameWithoutExtension, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
||||||
Id.GetPaddedId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
Id.GetPaddedId(resultSettings, metadataSettings, id, nameWithoutExtension, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
||||||
|
|
||||||
internal int TestStatic_GetDeterministicHashCode(byte[] value) =>
|
internal int TestStatic_GetDeterministicHashCode(byte[] value) =>
|
||||||
GetDeterministicHashCode(value);
|
GetDeterministicHashCode(value);
|
||||||
@ -61,13 +64,16 @@ public interface IId
|
|||||||
internal bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
|
internal bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
|
||||||
NameWithoutExtensionIsIntelligentIdFormat(metadataSettings, fileNameFirstSegment);
|
NameWithoutExtensionIsIntelligentIdFormat(metadataSettings, fileNameFirstSegment);
|
||||||
|
|
||||||
|
internal string TestStatic_GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath, int? index) =>
|
||||||
|
GetPaddedId(resultSettings, metadataSettings, filePath, index);
|
||||||
|
|
||||||
internal bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
|
internal bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
|
||||||
NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataSettings, sortOrderOnlyLengthIndex, fileNameFirstSegment);
|
NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataSettings, sortOrderOnlyLengthIndex, fileNameFirstSegment);
|
||||||
|
|
||||||
internal string TestStatic_GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, string extensionLowered, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
|
internal string TestStatic_GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
||||||
GetIntelligentId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
|
GetPaddedId(resultSettings, metadataSettings, filePath, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
||||||
|
|
||||||
internal string TestStatic_GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
internal string TestStatic_GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string nameWithoutExtension, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
||||||
GetPaddedId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
GetPaddedId(resultSettings, metadataSettings, id, nameWithoutExtension, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
||||||
|
|
||||||
}
|
}
|
@ -30,8 +30,14 @@ internal abstract class Id
|
|||||||
internal static byte GetHasDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
|
internal static byte GetHasDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
|
||||||
(byte)(IsIgnoreOrValidVideoFormatExtension(resultSettings, filePath) ? filePath.Id > -1 ? 6 : 4 : filePath.Id > -1 ? 9 : 1);
|
(byte)(IsIgnoreOrValidVideoFormatExtension(resultSettings, filePath) ? filePath.Id > -1 ? 6 : 4 : filePath.Id > -1 ? 9 : 1);
|
||||||
|
|
||||||
|
private static string GetExtension(string nameWithoutExtension, string extensionLowered) =>
|
||||||
|
extensionLowered is not ".xmp" and not ".json" ? extensionLowered : Path.GetExtension(nameWithoutExtension);
|
||||||
|
|
||||||
|
private static string GetExtension(FilePath filePath) =>
|
||||||
|
GetExtension(filePath.NameWithoutExtension, filePath.ExtensionLowered);
|
||||||
|
|
||||||
private static bool IsIgnoreOrValidVideoFormatExtension(ResultSettings resultSettings, FilePath filePath) =>
|
private static bool IsIgnoreOrValidVideoFormatExtension(ResultSettings resultSettings, FilePath filePath) =>
|
||||||
IsIgnoreOrValidVideoFormatExtension(resultSettings, filePath.ExtensionLowered);
|
IsIgnoreOrValidVideoFormatExtension(resultSettings, GetExtension(filePath));
|
||||||
|
|
||||||
private static bool IsIgnoreOrValidVideoFormatExtension(ResultSettings resultSettings, string extensionLowered) =>
|
private static bool IsIgnoreOrValidVideoFormatExtension(ResultSettings resultSettings, string extensionLowered) =>
|
||||||
resultSettings.IgnoreExtensions.Contains(extensionLowered)
|
resultSettings.IgnoreExtensions.Contains(extensionLowered)
|
||||||
@ -70,9 +76,7 @@ internal abstract class Id
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable IDE0060
|
private static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, string nameWithoutExtension, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
|
||||||
internal static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
|
|
||||||
#pragma warning restore IDE0060
|
|
||||||
{
|
{
|
||||||
string result;
|
string result;
|
||||||
StringBuilder stringBuilder = new();
|
StringBuilder stringBuilder = new();
|
||||||
@ -88,7 +92,8 @@ internal abstract class Id
|
|||||||
}
|
}
|
||||||
else if (id > -1)
|
else if (id > -1)
|
||||||
{
|
{
|
||||||
if (IsIgnoreOrValidVideoFormatExtension(resultSettings, extensionLowered))
|
string checkExtension = GetExtension(nameWithoutExtension, extensionLowered);
|
||||||
|
if (IsIgnoreOrValidVideoFormatExtension(resultSettings, checkExtension))
|
||||||
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 6 : 5;
|
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 6 : 5;
|
||||||
else
|
else
|
||||||
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : hasDateTimeOriginal.Value ? 9 : 7;
|
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : hasDateTimeOriginal.Value ? 9 : 7;
|
||||||
@ -96,7 +101,8 @@ internal abstract class Id
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (IsIgnoreOrValidVideoFormatExtension(resultSettings, extensionLowered))
|
string checkExtension = GetExtension(nameWithoutExtension, extensionLowered);
|
||||||
|
if (IsIgnoreOrValidVideoFormatExtension(resultSettings, checkExtension))
|
||||||
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 4 : 0;
|
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 4 : 0;
|
||||||
else
|
else
|
||||||
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : hasDateTimeOriginal.Value ? 1 : 3;
|
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : hasDateTimeOriginal.Value ? 1 : 3;
|
||||||
@ -110,14 +116,17 @@ internal abstract class Id
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
|
private static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath) =>
|
||||||
|
filePath.Id is null ? throw new Exception() : GetIntelligentId(resultSettings, metadataSettings, filePath.Id.Value, filePath.NameWithoutExtension, filePath.ExtensionLowered, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal);
|
||||||
|
|
||||||
|
internal static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string nameWithoutExtension, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
|
||||||
{
|
{
|
||||||
string result;
|
string result;
|
||||||
if (metadataSettings.Offset < 0)
|
if (metadataSettings.Offset < 0)
|
||||||
result = Guid.NewGuid().ToString();
|
result = Guid.NewGuid().ToString();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string intelligentId = GetIntelligentId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
|
string intelligentId = GetIntelligentId(resultSettings, metadataSettings, id, nameWithoutExtension, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
|
||||||
int check = GetId(resultSettings, metadataSettings, intelligentId);
|
int check = GetId(resultSettings, metadataSettings, intelligentId);
|
||||||
if (check != id)
|
if (check != id)
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
@ -126,4 +135,20 @@ internal abstract class Id
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath, int? index)
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
if (metadataSettings.Offset < 0)
|
||||||
|
result = Guid.NewGuid().ToString();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string intelligentId = GetIntelligentId(resultSettings, metadataSettings, filePath);
|
||||||
|
int check = GetId(resultSettings, metadataSettings, intelligentId);
|
||||||
|
if (filePath.Id is not null && check != filePath.Id.Value)
|
||||||
|
throw new NotSupportedException();
|
||||||
|
result = index is null || metadataSettings.Offset == IId.DeterministicHashCode ? intelligentId : $"{metadataSettings.Offset + index}{intelligentId}";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,15 +1,15 @@
|
|||||||
using CliWrap;
|
using CliWrap;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Phares.Metadata.Models;
|
||||||
|
using Phares.Metadata.Models.Stateless;
|
||||||
|
using Phares.Shared.Models;
|
||||||
|
using Phares.Shared.Models.Stateless;
|
||||||
using ShellProgressBar;
|
using ShellProgressBar;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Phares.Metadata.Models;
|
|
||||||
using Phares.Metadata.Models.Stateless;
|
|
||||||
using Phares.Shared.Models;
|
|
||||||
using Phares.Shared.Models.Stateless;
|
|
||||||
using View_by_Distance.Windows.Models;
|
using View_by_Distance.Windows.Models;
|
||||||
|
|
||||||
namespace View_by_Distance.Windows;
|
namespace View_by_Distance.Windows;
|
||||||
@ -268,9 +268,11 @@ public partial class Windows : IWindows, IDisposable
|
|||||||
messages.Add($"{nginxFileSystem.URI.OriginalString}");
|
messages.Add($"{nginxFileSystem.URI.OriginalString}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
string nameWithoutExtension = Path.GetFileNameWithoutExtension(nginxFileSystem.Name);
|
||||||
string paddedId = IId.GetPaddedId(resultSettings: appSettings.ResultSettings,
|
string paddedId = IId.GetPaddedId(resultSettings: appSettings.ResultSettings,
|
||||||
metadataSettings: appSettings.MetadataSettings,
|
metadataSettings: appSettings.MetadataSettings,
|
||||||
id: deterministicHashCode.Id.Value,
|
id: deterministicHashCode.Id.Value,
|
||||||
|
nameWithoutExtension: nameWithoutExtension,
|
||||||
extensionLowered: appSettings.ResultSettings.ValidImageFormatExtensions[0],
|
extensionLowered: appSettings.ResultSettings.ValidImageFormatExtensions[0],
|
||||||
hasIgnoreKeyword: null,
|
hasIgnoreKeyword: null,
|
||||||
hasDateTimeOriginal: null,
|
hasDateTimeOriginal: null,
|
||||||
|
Reference in New Issue
Block a user