AA.Compare Project to Match not runToDoCollectionFirst
Removed Layered AppSettings with Nested Objects at First Level
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -470,4 +470,5 @@ globalStorage/
|
||||
Shared/.kanbn
|
||||
|
||||
.vscode/Har-Files
|
||||
.vscode/.UserSecrets/*
|
||||
Rename/.vscode/.UserSecrets/secrets.json
|
||||
Compare/.vscode/.UserSecrets/secrets.json
|
||||
|
537
.vscode/launch.json
vendored
537
.vscode/launch.json
vendored
@ -1,318 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
},
|
||||
{
|
||||
"name": "Compare",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Compare/bin/Debug/net9.0/win-x64/Compare.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Copy-Distinct",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Copy-Distinct/bin/Debug/net9.0/win-x64/Copy-Distinct.dll",
|
||||
"args": [],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Duplicate-Search",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Duplicate-Search/bin/Debug/net9.0/win-x64/Duplicate-Search.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Date-Group",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Date-Group/bin/Debug/net9.0/win-x64/Date-Group.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Delete-By-Distinct",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Delete-By-Distinct/bin/Debug/net9.0/win-x64/Delete-By-Distinct.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Delete-By-Relative",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Delete-By-Relative/bin/Debug/net9.0/win-x64/Delete-By-Relative.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Drag-Drop",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Drag-Drop/bin/Debug/net9.0-windows/win-x64/Drag-Drop.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Drag-Drop-Explorer",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Drag-Drop-Explorer/bin/Debug/net9.0-windows/win-x64/Drag-Drop-Explorer.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Drag-Drop-Move",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Drag-Drop-Move/bin/Debug/net9.0-windows/win-x64/Drag-Drop-Move.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Drag-Drop-Set-Property-Item",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Drag-Drop-Set-Property-Item/bin/Debug/net9.0-windows/win-x64/Drag-Drop-Set-Property-Item.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Instance",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Instance/bin/Debug/net9.0/win-x64/Instance.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Mirror-Length",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Mirror-Length/bin/Debug/net9.0/win-x64/Mirror-Length.dll",
|
||||
"args": [],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Metadata-Query",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Metadata-Query/bin/Debug/net9.0/win-x64/Metadata-Query.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Move-By-Id",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Move-By-Id/bin/Debug/net9.0/win-x64/Move-By-Id.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Not-Copy-Copy",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Not-Copy-Copy/bin/Debug/net9.0/win-x64/Not-Copy-Copy.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Offset-Date-Time-Original",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Offset-Date-Time-Original/bin/Debug/net9.0/win-x64/Offset-Date-Time-Original.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "PrepareForOld",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/PrepareForOld/bin/Debug/net9.0/win-x64/PrepareForOld.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Person",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Person/bin/Debug/net9.0/Person.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Rename",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"preLaunchTask": "build-Rename",
|
||||
"program": "${workspaceFolder}/Rename/bin/Debug/net9.0/win-x64/AA.Rename.dll",
|
||||
"args": [
|
||||
"s"
|
||||
@ -326,11 +19,11 @@
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Set-Created-Date",
|
||||
"name": "Compare",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/Set-Created-Date/bin/Debug/net9.0/win-x64/Set-Created-Date.dll",
|
||||
"preLaunchTask": "build-Compare",
|
||||
"program": "${workspaceFolder}/Compare/bin/Debug/net9.0/win-x64/AA.Compare.dll",
|
||||
"args": [
|
||||
"s"
|
||||
],
|
||||
@ -343,224 +36,4 @@
|
||||
"requireExactSource": false
|
||||
}
|
||||
]
|
||||
}
|
||||
// https://www.amazon.com/photos/
|
||||
// "AmazonUsername": "phares36@gmail.com",
|
||||
// "AmazonPassword": "JlPhgtv@63",
|
||||
// Keep creating the .json file the same way
|
||||
// However load all .json files at the beginning
|
||||
// When looping through all files and file not in collection path&name or date is different from collection value
|
||||
// Get id in normal fashion
|
||||
// If id is in collection update collection to new path/name
|
||||
// If not save and add to collection
|
||||
// Nicéphore Niépce in 1826 or 182
|
||||
// https://dotnet.microsoft.com/en-us/download
|
||||
// https://git-scm.com/
|
||||
// https://code.visualstudio.com/
|
||||
// https://github.com/mikepharesjr/VS-Code-Settings
|
||||
// https://github.com/davisking/dlib-models
|
||||
// https://github.com/mikepharesjr/View-by-Distance-MKLink-Console
|
||||
// https://184.103.9.214/login?folder=/home/vscode/Notes&to=
|
||||
// Notes at 9/18/2022 10:29 PM
|
||||
// (637987913910140924) 9/14/2022 10:29 PM - 113 *.jpg && 109 *.json
|
||||
// (637989361172096980) 9/16/2022 02:41 PM - 094 *.jpg && 026 *.json
|
||||
// All including (637991052364021796) 9/18/2022 1:40 PM - 17435 *.jpg && 16237 *.json = 93.128763980499% with int match only
|
||||
// List of people with current person key formatted in tab form
|
||||
// Exclusion list for Robert
|
||||
// Person birthday allow #
|
||||
// Host face directory on Ubuntu
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/49158951_2264915190186558_112485584324263936_n.jpg?_nc_cat=106&ccb=1-7&_nc_sid=84a396&_nc_ohc=Q78znguHVeMAX88OKg9&_nc_ht=scontent-lax3-2.xx&oh=00_AT_gJFk9xytYuXp9p5smDm0mMDXzsPnFS_EfKEbiL3KYqg&oe=63523F37
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t39.30808-6/290625560_10217841255422109_3297482250419620718_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=730e14&_nc_ohc=O3ybqwDLK9AAX_V0FGP&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT-9rtBghXU7lAGkOy4KPHbV9Kyj8DGm-BGtUabWgQJbOg&oe=63335209
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t39.30808-6/243093116_10216723829287154_8982585928017225043_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=730e14&_nc_ohc=TW6UDC8v9egAX-_YcWf&_nc_ht=scontent-lax3-1.xx&oh=00_AT8rE2JOfkJINSsA5imGVBDbXNDvxx1_uZn0sLM-6ITDUw&oe=633238A9
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.18169-9/281249_1450445399251_3598212_n.jpg?_nc_cat=108&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=ihKT3xtvWXoAX-kiat-&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT9cmbHu00fc0-aR-k6MP7T_cja6geiAkobbvU4pdfG6KQ&oe=63527F21
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.18169-9/62557_157063120990498_3918482_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=de6eea&_nc_ohc=lV8bmtAes-0AX8Uk9pG&_nc_ht=scontent-lax3-1.xx&oh=00_AT-ieBL8wIZ4gQmRWAvTWDu9wBzAsPV5s4c82MvOs6oU5g&oe=6355846B
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.18169-9/166577_1634651034600_6777460_n.jpg?_nc_cat=108&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=nEZ0RR0cqn4AX-wA-VH&_nc_ht=scontent-lax3-1.xx&oh=00_AT_ig2HUMDQl1fhXY0Inm0QWC3i2__rPUAWYF2Wqx-pkew&oe=635290D6
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/97107804_10223189072017432_5088375698351980544_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=t0rqlgAAd_0AX-ANM1H&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT_hqG2piNkASRB5z94qeY8q7hy90ku9Dka4enL4wsAnTg&oe=6354CD8A
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/121491463_10159438417238072_1207001349879930424_n.jpg?_nc_cat=103&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=rZTiRaFTrCkAX_L4sVS&_nc_ht=scontent-lax3-2.xx&oh=00_AT_uKD2jeNUTwlyN5r6UXRK40aNNgRNCZrrJ9B-WW6tlag&oe=63522946
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/96370874_10158521458082578_8748970895894118400_n.jpg?_nc_cat=108&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=qvIn4gTjvo0AX-dHnuq&_nc_ht=scontent-lax3-1.xx&oh=00_AT8R-D8AsjWKMlUxK-AimPNzxuNT06zQCS43ESdf1LEeoQ&oe=63562F54
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/96681856_10157495047223435_2857053180632498176_n.jpg?_nc_cat=103&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=UwF0qUGoG9QAX-S1LcI&_nc_ht=scontent-lax3-2.xx&oh=00_AT-5j0EiZB6HOOc2jGqGOll2zFoNTdDLKLfJztq0HYWagg&oe=6355A2A9
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t31.18172-8/26850365_1810492538961485_3240264679723665015_o.jpg?_nc_cat=110&ccb=1-7&_nc_sid=0debeb&_nc_ohc=WrJYsgu1eS4AX8kIsyM&_nc_ht=scontent-lax3-1.xx&oh=00_AT-fzFN40kYFjF2-uYL6KcgKPxgFkDZ5BcyIUVFdCTf2tg&oe=635485B7
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/67735718_10219770387799941_2254439277247070208_n.jpg?_nc_cat=108&ccb=1-7&_nc_sid=730e14&_nc_ohc=MD8haDYY7uIAX_dwTY4&_nc_ht=scontent-lax3-1.xx&oh=00_AT_zhJpjbWomrihWiSq35KFkmpBfEgbXCSNM68icaABxoA&oe=6355871D
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/59983603_2215207855225638_2271200674882519040_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=-2zmPWdcy9IAX8KXJ_t&_nc_ht=scontent-lax3-2.xx&oh=00_AT_7N0yufGym-cuQfeMK6iceBNI7eEyaScRm3m3_4njTIQ&oe=6354C2B6
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/204501292_10222856568897395_8588510782161393042_n.jpg?_nc_cat=105&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=2gLdnkn19swAX_9mAG1&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT_ha5ncAI7ykhbgFNT1_4NWCvPGFWQLgdq6q20nZSIVQA&oe=63537D09
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/65945099_10217135244827869_3400689284598988800_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=NHnhR9_7UTkAX8wpbxK&_nc_ht=scontent-lax3-1.xx&oh=00_AT_-Q-zUeDoD11B9TY_n_MWEimMiXCokB_5TojplmU6_Eg&oe=6355A2B8
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/22055_581856018827_4131263_n.jpg?_nc_cat=107&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=zsqIe2xzpf0AX8lcyLV&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT_DzvUir_30cauAjp8eaSlYKVADdanzWo1NAQK7_b3M-w&oe=63548E83
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t31.18172-8/14525108_10202312892739292_8313619716862825541_o.jpg?_nc_cat=102&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=lZ4MpT6i_MIAX-3d-nV&_nc_ht=scontent-lax3-1.xx&oh=00_AT_9JoZdMdlNV71PdrkU7AmvFkkwqllEg9sVTvcGyzoESw&oe=6353993A
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/104325415_860091804515928_5960365004832843376_n.jpg?_nc_cat=106&ccb=1-7&_nc_sid=174925&_nc_ohc=y1AW-ld80OcAX_927wW&_nc_oc=AQluU8Pwuqxs9xBVYhQJ9ThWOh5vLIKvQdOQnCtZpN2-j-6d9PRMHWHR5aaFBkcJicA&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT8vQwiLvz7cJU61FkIPLtcc8j8tlL2tC6zRlH1i20VUmQ&oe=6356EACE
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/43565411_10217177505092918_6185540976904241152_n.jpg?_nc_cat=105&ccb=1-7&_nc_sid=730e14&_nc_ohc=yBls9BCXhdwAX_doX-2&_nc_ht=scontent-lax3-1.xx&oh=00_AT-PYyffphSxOeGM_3aC4oHSA4U9cEjm2OrappZ_jh6qIg&oe=6355895C
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/48422692_10157144542022625_3324340889383337984_n.jpg?_nc_cat=108&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=h_gtXGa5nSAAX-CB2E1&_nc_ht=scontent-lax3-1.xx&oh=00_AT8x7vZA5VN27hfoMPcQ39So-1COUnuNjDj4iz8d-cw6wQ&oe=6354B370
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/150153899_10208532293942419_1808101907825622971_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=3NZWKCpJ0VYAX911uCo&_nc_ht=scontent-lax3-2.xx&oh=00_AT_NhKwagHEsPuQVSERZNQ2_6MOIG2UKG5r32c0fIPaeQA&oe=63552624
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t31.18172-8/13641143_10209202266801205_7525162492297688727_o.jpg?_nc_cat=106&ccb=1-7&_nc_sid=174925&_nc_ohc=AETs7fkfLacAX-q9cnO&_nc_ht=scontent-lax3-2.xx&oh=00_AT89DOYAP9GbNvEgmlI0z9VK9Z8uJttFQyuO-tfe5OGbFQ&oe=6355CF5D
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/299875_2023116112081_1500929171_n.jpg?_nc_cat=103&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=bsY9uckSAQwAX-xxGaU&_nc_ht=scontent-lax3-2.xx&oh=00_AT8OStQmDU_7NLNrYLeNfoULcBA-onU1V3Z4bn35-ipzCQ&oe=63555339
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/1544444_10203587296855592_4576811169900508598_n.jpg?_nc_cat=100&ccb=1-7&_nc_sid=730e14&_nc_ohc=pYUGNkoYGPcAX8X2gtw&_nc_ht=scontent-lax3-2.xx&oh=00_AT_thzQZyWPWDA8t1acXqEzifLMXxcPTUOgqWqmeuk3SfQ&oe=63566033
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t31.18172-8/10887668_10205175511939476_7644367668075304275_o.jpg?_nc_cat=100&ccb=1-7&_nc_sid=730e14&_nc_ohc=2T_utQvqkXYAX94o-WV&_nc_ht=scontent-lax3-2.xx&oh=00_AT8aSCQ4JVgRULd1RxRJ8DYqPtB9EyGY-May2QwwqKSqjQ&oe=6356B098
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/78675669_10102984650797787_175454688261439488_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=7Plv_RW77xoAX9PiH9G&_nc_ht=scontent-lax3-2.xx&oh=00_AT-npzVNkTzAdYgQ4D1ltfyl8llC_xqOHJ9Mi8Vmh2khlw&oe=63571251
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/59569741_10214142910059142_9219208491263066112_n.jpg?_nc_cat=100&ccb=1-7&_nc_sid=730e14&_nc_ohc=F7GF2uCFljcAX8Skffr&_nc_ht=scontent-lax3-2.xx&oh=00_AT9e3f8x6hSmAcxQ5JDUvDPFEg0wcecKw175Qfj_WMwADQ&oe=635488AA
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/290559432_10209959340897099_842876274165104077_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=Vgxc93JsPBEAX8zrKaq&_nc_ht=scontent-lax3-2.xx&oh=00_AT-pPGd4o-t7kSeY4XKOTDeOzfg6wXOMBdY6rldQyrKQmw&oe=6335398E
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t39.30808-6/308587069_10210153867920153_6202104893656263114_n.jpg?stp=cp6_dst-jpg&_nc_cat=104&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=SKB2FSdXFQEAX_v1t5g&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT_E1y9uQ27O5_FAyAeRJ0XotEyBEXytkM2iDCGvldSxLw&oe=633549B9
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/67309987_10214298629206292_6820175905885782016_n.jpg?_nc_cat=106&ccb=1-7&_nc_sid=730e14&_nc_ohc=BTQEZF_dwRwAX-cxNEm&_nc_ht=scontent-lax3-2.xx&oh=00_AT8_z-JOBPmskVsJ0S9bOYgcYngpVrMjhVrfFk3r06Wj8Q&oe=635473AE
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t39.30808-6/263710427_1324163581376203_7502850514450466967_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=e3f864&_nc_ohc=2KB9VISs3y0AX-PiYUd&_nc_ht=scontent-lax3-1.xx&oh=00_AT_WaHBBSAR2-1dDEdpufugFf4eBttyGfIIflCwvCu_76g&oe=6334DBC3
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/67181620_114720179834180_3651229536021905408_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=CYF8Qyy506MAX-GBXV_&_nc_ht=scontent-lax3-2.xx&oh=00_AT_dFZiY42lloSfdiqNgSBlDpS9eGIEu85hhG5KIQD7DwA&oe=6354B320
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/68383564_10217654576270105_5209606295652401152_n.jpg?_nc_cat=109&ccb=1-7&_nc_sid=730e14&_nc_ohc=phqcunpNs5EAX-d33kB&_nc_ht=scontent-lax3-1.xx&oh=00_AT9n5kFQnIPSXUzNAMb19Wpue1YA-bHFpSCE-_tOXD8Gnw&oe=635615C2
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/285466259_7423531907720135_1229107015631332741_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=z85uLRC706sAX_H3_Ht&_nc_ht=scontent-lax3-2.xx&oh=00_AT-_wJpeYhqcdivdF4SbBO3B6M082qGD3qdGAPDmVP6nAA&oe=6334B2ED
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/32919393_10214483292069982_3789509822047584256_n.jpg?_nc_cat=105&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=oJzHahluIDYAX-5-UGl&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT9e7dHUoseqwf6qF39NHEnPzal6NzQgyCwio6SvzUZTUQ&oe=63575682
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t31.18172-8/22256636_1931521070438917_6414682443902494017_o.jpg?_nc_cat=102&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=bf99qSpRJOQAX_stzg5&_nc_ht=scontent-lax3-1.xx&oh=00_AT-eoWkn2Gaqj8BGasgNOycRzf3AiQE2GqHtKC28_-vFug&oe=63586C84
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.18169-9/600876_10151927436713567_876993105_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=dcNCC6RNOooAX985ENZ&_nc_ht=scontent-lax3-1.xx&oh=00_AT-9Q999B_sWiS-yPP48gVdrheH0bY-DAiOKmkExbnW5Rw&oe=6355ED8A
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/35235266_10213962597433782_6655457381733892096_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=uxfDliBhPHUAX_JXJNU&_nc_ht=scontent-lax3-1.xx&oh=00_AT9zGc-8QqaAHItoDqGzKAMEYOU0fEQz_ld1R_CGxPokug&oe=63565840
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/161932468_10159007128985149_3980438021322511452_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=6dCTPFwvFPcAX8ogdv8&_nc_ht=scontent-lax3-1.xx&oh=00_AT8pspicXFXgj-MOKzkTyp_ZpuboruYwyM-MMNwro_m9JA&oe=63564482
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/91250196_10215323899906785_1298078522899693568_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=730e14&_nc_ohc=CSXhKF_1hqsAX9qlbvP&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT-JZSnju_3XqWV6w9rSSUmuo2oIaCig5pn7JmrvFbBfGw&oe=6354B339
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.18169-9/428510_490436967638912_1438526428_n.jpg?_nc_cat=108&ccb=1-7&_nc_sid=ba80b0&_nc_ohc=s0ZgvdtWwOsAX87AD-I&_nc_ht=scontent-lax3-1.xx&oh=00_AT-gv7QKuJhEjRPbmQr5hLG163-EQXcx6FJ8aHCcPTfEXw&oe=63563591
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/50312617_10161338488075048_5525340242208358400_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=NcQcK_z6t9kAX9-On7t&_nc_ht=scontent-lax3-2.xx&oh=00_AT_WYrFNqGwzuSssGPBRmhpqGlyf1k2ulbdKakrs4xAHBg&oe=635832DB
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/247966826_10200232179385908_1907223790607385605_n.jpg?_nc_cat=106&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=Xn0j5rxLaCQAX8OD7k4&_nc_ht=scontent-lax3-2.xx&oh=00_AT_bVVWAuaGYpIcBOCbAtn_xyS2Rqu--uKs06-FKOfjuzg&oe=633583B8
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/57840024_10161651547530207_1744748790901899264_n.jpg?_nc_cat=107&ccb=1-7&_nc_sid=730e14&_nc_ohc=TKg3rTiXkYUAX-V8NO5&_nc_ht=scontent-lax3-2.xx&oh=00_AT-Mepp0iN8-mHhyKRHj-R5yBOu2wZGtQeApkSoZTzM3mg&oe=6356A1D2
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/269876615_612483726673361_2182050585964176784_n.jpg?_nc_cat=106&ccb=1-7&_nc_sid=174925&_nc_ohc=9QJt0X_FDlEAX_Okew3&_nc_ht=scontent-lax3-2.xx&oh=00_AT_Yq8OflcYUA39JsmM__UxFYi8YASqsrRYx-T1Qn2NPSw&oe=6336391B
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/126332086_10224303272989365_2242003924983097751_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=zpX0nadZ9XMAX8m1Kk1&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT9c_dq-TLiNxgeqmp9Pwnqp9xjUIG3_ijxmiAW4MPYBKw&oe=63578DD6
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/74624149_10215957575592842_7757392320252608512_n.jpg?_nc_cat=103&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=22WGGAPeTdAAX87greQ&_nc_ht=scontent-lax3-2.xx&oh=00_AT9J2qV9zBMEPuqsibA-87pjTBg3e3hVwHb211GO7lJicQ&oe=6355F050
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t31.18172-8/10649011_812346452173040_8035951943727654673_o.jpg?_nc_cat=104&ccb=1-7&_nc_sid=84a396&_nc_ohc=aDP-7N5XOgMAX9eflUn&_nc_ht=scontent-lax3-1.xx&oh=00_AT_mCAWB-OTJ32F1W90m-bPWu9GmjvFr-j1VdAQmkdNtXg&oe=63565628
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/289655024_10228170790084748_4126077972169573791_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=NeX9XD5pcwMAX9UBHLe&_nc_ht=scontent-lax3-2.xx&oh=00_AT-Qh77ATMIGL6JnKHr9rIim3Ui_UU7XH_S02S7-iu21qg&oe=633700F0
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/266998682_10227151119593623_456420668145746211_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=vjNb4pBYn5YAX_gWfIx&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT_rv-lqoI2u7Y-FMU8G8Vfxo6QyA-tTEIMKRpRx9TxZPg&oe=63353CCC
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/40093120_10217172129645111_2951374557987995648_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=X1j0ykpT26oAX8kDzfQ&_nc_ht=scontent-lax3-1.xx&oh=00_AT-jst6eXb7F4NsSJRj4jOQvKqQOiiMP4K5iT8xq2t4dTg&oe=63588214
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/31870362_10216285257993874_8330563136197230592_n.jpg?_nc_cat=103&ccb=1-7&_nc_sid=84a396&_nc_ohc=8kJ5pbwsKDMAX9hIVFA&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT9vRjGUZdT7KasJ4Ndti2LHfrPaTcahl4ipT9BmdRZJeg&oe=6356737F
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/230074448_10103880528811228_6909448863476427135_n.jpg?_nc_cat=103&ccb=1-7&_nc_sid=0debeb&_nc_ohc=cUpp1Y_DO8MAX92tN_t&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT-K2_yEERUmvvjkjUKf7zwaV2Ofq1fHIQAtEkCt7I2Bhw&oe=63352B95
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t31.18172-8/13502600_10210212049485607_95598559411189791_o.jpg?_nc_cat=101&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=AEojM0a2X5cAX8kmHfZ&_nc_ht=scontent-lax3-2.xx&oh=00_AT-ZX7vIfwazh_zcDpFCLxwLgp90gERyBG2CtGxIClEnYA&oe=6355A9C7
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t31.18172-8/14195939_1314130471931770_9026437168267009241_o.jpg?_nc_cat=106&ccb=1-7&_nc_sid=730e14&_nc_ohc=f8owaBX7kMQAX8ioj3m&_nc_ht=scontent-lax3-2.xx&oh=00_AT8N8GR4GXmKa3w_dP952fl1_bx_s7CTz8qFtxaBK91S2w&oe=6355DBA1
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/67428022_1287438048070254_7628190366730027008_n.jpg?_nc_cat=105&ccb=1-7&_nc_sid=b9115d&_nc_ohc=wO9dTzPDXjcAX85VZFY&_nc_ht=scontent-lax3-1.xx&oh=00_AT9ZDKZuwlVncwGAKb6fkty_AaqnBNmKj46n2L3V1Y_V5w&oe=63589442
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/95959209_10221111135660559_6413640771529867264_n.jpg?_nc_cat=106&ccb=1-7&_nc_sid=730e14&_nc_ohc=tN-1cmyDpEsAX-GecHc&_nc_ht=scontent-lax3-2.xx&oh=00_AT9w87N6Gq277785G3f6iF5IhEtBtJ7FTZwxkK5dipNqOw&oe=63575F14
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/207486_1959635958499_5198863_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=2c4854&_nc_ohc=Z2KnsIT4a60AX9wPnkk&_nc_ht=scontent-lax3-2.xx&oh=00_AT8Vxh2vW53SBZ4sH9cgZEofWKOUnpf8fpMjQRjZGCR6oQ&oe=6355FB36
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/37219804_10213591073105306_9108104030982242304_n.jpg?_nc_cat=108&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=pngJAV_sIesAX8zSJG-&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT8UXpCCDSQ8uOPtSrdzcty0tSpO6TCEKCL-K7q-BMb5CA&oe=6357898C
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/427539_3441473049618_710901137_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=3DPj0_PEbSsAX9ryGz1&_nc_ht=scontent-lax3-2.xx&oh=00_AT9t82wuMvW2xdHA8E1VokNBzIhHtgBbpH8fvrIxE_0rPQ&oe=6357DCF0
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t39.30808-6/241548211_237343028191419_6995991527624737723_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=nsC7CBQxaO8AX-De2g8&_nc_ht=scontent-lax3-1.xx&oh=00_AT_V2gvedewiVuhPIozJyq4ccKm6WoxBkXe0Ocm1fTZ3wA&oe=63365FAA
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.18169-9/10253934_10152334525164461_3158331570278601806_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=ba80b0&_nc_ohc=0A0Haq5W-IgAX8Y28oA&_nc_ht=scontent-lax3-1.xx&oh=00_AT-3tqlxtQj1n0D5t1jzVfQi22q3OMyQQrBcw2ZBrmYGaA&oe=6356ACAB
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/308817118_10218508885627876_6441766117990882318_n.jpg?_nc_cat=106&ccb=1-7&_nc_sid=730e14&_nc_ohc=1kMU8trJLw4AX_R76M2&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT9k53CcIY7KnW3CjRR4bbE8wjuAUVVi6D1an-kcbOaF2g&oe=63363C9E
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/41038997_10156771390897533_9196241072043327488_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=174925&_nc_ohc=CK2PqvoqhVsAX9xGRSK&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT8qHnIW7OsQMRb7htRwC7byyqcK4X5qAkdz8IM4caO_iA&oe=6357DA17
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t39.30808-6/279316302_4583494791751734_5208076832025426501_n.jpg?_nc_cat=109&ccb=1-7&_nc_sid=e3f864&_nc_ohc=Mt2sOGHVzbMAX-ghCmr&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT_xdFzERKvZfApmuZ19H39uWsTz0vrJR7fivTuaLGiE1g&oe=63372D6C
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/213757548_10222718701187533_9216326465615113019_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=bMlCbM1DFccAX-UEjOE&_nc_oc=AQlwSf-Xrfqx-ldXQ8cz-gImrOZnTfVoNReVucU0i3C--RLTsfldVzKT8ij7sn7Jn3E&_nc_ht=scontent-lax3-2.xx&oh=00_AT-YAQ9scfYOoaPy5Kzb6yWcpe4t5MNTfE1pAP-RD3oBAA&oe=63379E32
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.18169-9/1005197_172610252919953_1797035311_n.jpg?_nc_cat=102&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=9wV_ZoGuDfQAX9ZQWRM&_nc_ht=scontent-lax3-1.xx&oh=00_AT_M3_rgENbCEjmouX8Ar_N_s-P5Tke0TvPzza0Mwx90dg&oe=635764FD
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t31.18172-8/24130218_2001836833160114_7527164989319060274_o.jpg?_nc_cat=104&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=ZxM5zjih3tYAX8IsXx9&_nc_ht=scontent-lax3-1.xx&oh=00_AT-G2-I-OXpcoq9TKqn_YT3gObzj8GMavpJe-KH7xj_5Lg&oe=6357091E
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.18169-9/11745695_10206235519080812_1953505210138379535_n.jpg?_nc_cat=109&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=RDqIQjOVB0AAX8Q90_Z&_nc_ht=scontent-lax3-1.xx&oh=00_AT_FogaOVWhgLcesEzaidYx--sKeBVCD6xDg2IWJdvcafA&oe=6356095A
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/280118285_2094899397354077_5352291217200307805_n.jpg?_nc_cat=103&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=YQDlNI2xTrIAX-curq5&_nc_ht=scontent-lax3-2.xx&oh=00_AT8SuwF2W7tioXfAfCCun6zQsrIUjArOSr0ZlRb3F_Ej2Q&oe=63378827
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/13233144_10101471840461827_9039193337970955304_n.jpg?_nc_cat=107&ccb=1-7&_nc_sid=cdbe9c&_nc_ohc=aiRRaotqsnYAX-slwLd&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT_CxgSXV-0uNiiz_jLV_s3cPAk2vUyoro5MVsXsVyiEcg&oe=6358E40F
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t39.30808-6/305309404_10220461241674046_1323434462733520520_n.jpg?_nc_cat=108&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=NajFsslaLvcAX8-Kuz_&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT89eIRz0st3qjPnhURiW9W7wXbaOV0usVykQiKRL_TRhg&oe=6337A145
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/32481784_10216908834904287_1754862128918953984_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=8mU2Cf_iAFwAX80s1xj&_nc_ht=scontent-lax3-2.xx&oh=00_AT8SXOmaqoNTZvBik5j4hE7YEenshkhPggwNgfLdIjIn4g&oe=6355E4D5
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/22814020_10211731412395324_9169598565701382472_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=UTLq0qx2PQwAX-Y9rIg&_nc_ht=scontent-lax3-2.xx&oh=00_AT_T9Bqd0LwRhuUkbMuUHQcCxcB7I3TAumrw7BR-UuEe3Q&oe=63563325
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/94927050_10220780432954871_7311541287197343744_n.jpg?_nc_cat=105&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=qJofGIany9YAX_15Uhx&_nc_ht=scontent-lax3-1.xx&oh=00_AT_K9V-fQ-IOdVE3BtUQCtFvljScg3xdbjWK8LWh0fs3EQ&oe=6355F4D6
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/47685020_2061588197234140_3115001451777097728_n.jpg?_nc_cat=103&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=g2_slmBopOEAX-kvLeT&_nc_ht=scontent-lax3-2.xx&oh=00_AT9CXSXrom34MSI2zgqXx7qdPKAn8fhKG8dBqGE3l1c_Nw&oe=63567CA6
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.6435-9/39221047_10156127305716865_6824896341232058368_n.jpg?_nc_cat=106&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=osdlvseA5vkAX9zsbYp&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT96aJo9mM_AXgkaQau0AYQ6GgMk3m02z4fpuT1FwYvXtw&oe=6357B649
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/11037634_10204752644508765_3331659968007967889_n.jpg?_nc_cat=107&ccb=1-7&_nc_sid=730e14&_nc_ohc=mjDY6IkATEoAX_WhE62&_nc_ht=scontent-lax3-2.xx&oh=00_AT8gl0GngVzWAynqGAC8zrgZUqAEgahMmzE5X41KpPnYxQ&oe=635825BA
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/36371667_10213980051274137_6162910755566911488_n.jpg?_nc_cat=102&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=-j6ofEojPiIAX-yLrVV&_nc_ht=scontent-lax3-1.xx&oh=00_AT9ZvpazeIfHclhHr24SguNiu0m4WMAj2k5jm2I40JIv5w&oe=635612A1
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/13938350_626048368466_6216870049337020892_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=AbgIJkB2bH4AX-TuAoA&_nc_ht=scontent-lax3-2.xx&oh=00_AT-o0wi5Cz5vl1WDgcDm2BneTIBTuR-__qxxmR-3Fgyg_w&oe=63579473
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t39.30808-6/306356815_5223195104456342_2659949811667496237_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=730e14&_nc_ohc=NQdsObWTav0AX9EbW12&_nc_ht=scontent-lax3-1.xx&oh=00_AT8v-jsilYOUGKvQdlt7PXQmbdJRfZD0-GdycokWq-aNwg&oe=63365BAB
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/44500772_10217404839095591_8903105516822069248_n.jpg?_nc_cat=105&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=RqwiyNoil04AX_Y4FGb&_nc_ht=scontent-lax3-1.xx&oh=00_AT85t0AjyzMXSj_UcUhKqWf7ZXdQOFAy12aFdeOO9cjBYg&oe=6356ED81
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/23659202_10212567297792300_7982751930141302131_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=ad2b24&_nc_ohc=9vxjTRupUuAAX8fJ3Qa&_nc_ht=scontent-lax3-2.xx&oh=00_AT8wFIaUMmI2_kLSejxewgzzMK6g0ngEJVfhEQukM0zQdQ&oe=63596B35
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.6435-9/92554157_10158297246718523_986365937043111936_n.jpg?_nc_cat=105&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=lWhPQlbtKVoAX-4D9Ke&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-1.xx&oh=00_AT_P1suJEkfnHlSL3rPoDoYW2exSg_gQyN--HoylQbt_JQ&oe=63567442
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t39.30808-6/280689051_10209840036394561_8737214584492733274_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=iMAAHqNekCgAX8fvEAm&_nc_ht=scontent-lax3-2.xx&oh=00_AT8b7_ODsDN7lNgTKr7JZiUE65R7WWYxENl1vOUGeobpBw&oe=6336E678
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t1.18169-9/25110_107749419250772_1842086_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=09cbfe&_nc_ohc=xPyRCvImlaoAX9Z_UTs&_nc_ht=scontent-lax3-1.xx&oh=00_AT85xffhHDJlhzyFTBvWs8j3Gd4GyDCte_Pnd7hNM006sw&oe=635880C2
|
||||
// https://scontent-lax3-2.xx.fbcdn.net/v/t1.18169-9/10259940_10153561109653458_6837681277740526675_n.jpg?_nc_cat=100&ccb=1-7&_nc_sid=8bfeb9&_nc_ohc=T1v9Wfqz8rUAX8y6bxq&tn=D0unuoVdv--xjhpM&_nc_ht=scontent-lax3-2.xx&oh=00_AT9Y9j5s19n0EzgO_dZMom8tAweFYIrJLOEsrsr4HAWHkQ&oe=63588CAE
|
||||
// https://scontent-lax3-1.xx.fbcdn.net/v/t31.18172-8/17621858_10154253751886426_3939148233753829194_o.jpg?_nc_cat=104&ccb=1-7&_nc_sid=ad2b24&_nc_ohc=yMOqJQQBV80AX8Xlzr-&_nc_ht=scontent-lax3-1.xx&oh=00_AT_z0oeHJ0fqKhVA4WdgGE2cZSgL0bIKNXzPsY0zeLipAg&oe=6356AF4C
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-10-19 12.00.31.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-10-20 15.41.37.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-10-23 09.57.51.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-10-25 13.46.09.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-10-30 21.47.53.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-10-31 11.33.13.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-11-05 10.28.25.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-11-11 19.57.27.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-11-12 14.15.39.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-11-13 09.49.09.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-11-15 08.55.19.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-12-01 18.03.13.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-12-04 08.44.23.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-12-10 21.20.31.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2012\2012-12-16 18.17.53.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130317_113053.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130324_133916.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130330_114757.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130418_175845.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130418_175934.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130424_202523.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130424_202634.jpg>
|
||||
// FileSizeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130514_190936.jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130516_104450.jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Kristy Google Photos from 2013\IMG_20130522_193539.jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Herman Family Reunion 2008\Herman Family Reunion (160).jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Herman Family Reunion 2008\Herman Family Reunion (213).jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Herman Family Reunion 2008\Herman Family Reunion (215).jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Herman Family Reunion 2008\Herman Family Reunion (218).jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Herman Family Reunion 2008\Herman Family Reunion (225).jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Herman Family Reunion 2008\Herman Family Reunion (226).jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Herman Family Reunion 2008\Herman Family Reunion (231).jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Herman Family Reunion 2008\Herman Family Reunion (232).jpg>
|
||||
// LastWriteTimeChanged <F:\Tmp\Phares\Compare\Images-4d49b68\Herman Family Reunion 2008\Herman Family Reunion (234).jpg>
|
||||
// - Device Videos 2_0_0_3 - Current - Kristy Google
|
||||
// - Device Videos 2_0_0_3 - Current - Mike Google
|
||||
// - Device Videos 2_0_0_3 - Current - Tracy
|
||||
// Chester & Linores ?Pearson?
|
||||
// Distance face files date time?
|
||||
// If not Hog and Original image size resize face
|
||||
// New json file with known locations at original image size
|
||||
// Add person to metadata when saving to SaveFilteredOriginalImagesFromJLinksForOutputResolutions maybe SaveMappedForOutputResolutions
|
||||
// GetFaceLocations
|
||||
// Stream stream;
|
||||
// FileType fileType;
|
||||
// const string abd = ".abd";
|
||||
// List<string> jsonGroupFiles = GetFileCollection(jsonGroupFileCollection);
|
||||
// foreach (string jsonGroupFile in jsonGroupFiles)
|
||||
// File.Move(jsonGroupFile, string.Concat(jsonGroupFile, abd));
|
||||
// stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
// fileType = FileTypeDetector.DetectFileType(stream);
|
||||
// stream.Close();
|
||||
// stream.Dispose();
|
||||
// if (fileType == FileType.Unknown)
|
||||
// continue;
|
||||
// if (!isUniqueFileName || string.IsNullOrEmpty(resultAllInOneKey))
|
||||
// {
|
||||
// if (!isUniqueFileName && !string.IsNullOrEmpty(resultAllInOneKey))
|
||||
// File.Move(Path.Combine(resultAllInOneKey, fileName), Path.Combine(resultAllInOneKey, $"{fileName}.del"));
|
||||
// resultAllInOneKey = string.Concat(jsonGroupDirectory, directoryName[length..]);
|
||||
// }
|
||||
// if (!jsonGroupFileCollection.TryGetValue(resultAllInOneKey, out keyValuePairs) || !keyValuePairs.TryGetValue(fileName, out collection))
|
||||
// fileAndJsonGroupFiles.Add(new FileAndJsonGroupFile(file, isUniqueFileName, Path.Combine(resultAllInOneKey, fileName), false));
|
||||
// else
|
||||
// {
|
||||
// foreach (string jsonGroupFile in collection)
|
||||
// // File.Move(string.Concat(jsonGroupFile, abd), jsonGroupFile);
|
||||
// fileAndJsonGroupFiles.Add(new FileAndJsonGroupFile(file, isUniqueFileName, jsonGroupFile, true));
|
||||
// }
|
||||
// C:\Program Files\ImageMagick-7.1.0-Q16-HDRI>identify -format '%Q' "D:\7-Question\- - - Images\Mike Goolgle Photos 2014\-1507651551.jpg"
|
||||
// '98'
|
||||
// C:\Program Files\ImageMagick-7.1.0-Q16-HDRI>identify -format '%Q' "D:\7) Question\- - - Images\Mike Goolgle Photos 2014\197616258.jpg"
|
||||
// '90'
|
||||
// dotnet dead-csharp --inputs "**/*.cs" --excludes "**/obj/**" "**/I*.cs" "**/UnitTest*.cs"
|
||||
// " - Shortcut (2)"
|
||||
// _ Manual Copy Successful
|
||||
// # Don't know full birthday
|
||||
// ] Match
|
||||
// ^ Know birthday
|
||||
// _ Import of Partial info
|
||||
// ` Want to find
|
||||
// ~ Approxomite age
|
||||
// + Approxomite age and have at least one image
|
||||
// XPAuthor = 40093 | [3-1] ("Picasa") <Verified in Windows>
|
||||
// Artist = 315 | [182-3] ("Picasa", "unknown", " ")
|
||||
// Kristy, Mike, Tracy, Mackenzie, Julie, Patrick, ...
|
||||
// XPKeywords = 40094 | Vulgar !9, Blurry !4, Duplicate !3, Irrelevant !2, Review !1, Slideshow [0]
|
||||
// XPSubject = 40095 | Family Pictures, Scanned [2-1] (" ")
|
||||
// XPComment = 40092 | [9-2] ("\uFFFD", " ")
|
||||
// Rating = 18246 | [52-1] ("0")
|
||||
// UserComment = 37510 | [3570-^^^]
|
||||
// XPTitle = 40091 | [24-24] ("Logan and his Doggie", ...) <Verified in Windows>
|
||||
// ImageDescription = 270 | [2135-5^] 4+37+82+1354+647+11 ("Camera 360", "OLYMPUS DIGITAL CAMERA ", "SAMSUNG ", " ", " ")
|
||||
// int artist = (int)IExif.Tags.Artist;
|
||||
// int fileSource = (int)IExif.Tags.FileSource;
|
||||
// int userComment = (int)IExif.Tags.UserComment;
|
||||
// 0x9c9b XPTitle int8u IFD0 (tags 0x9c9b-0x9c9f are used by Windows Explorer; special characters in these values are converted to UTF-8 by default, or Windows Latin1 with the -L option. XPTitle is ignored by Windows Explorer if ImageDescription exists)
|
||||
// 0x9c9c XPComment int8u IFD0
|
||||
// 0x9c9d XPAuthor int8u IFD0 (ignored by Windows Explorer if Artist exists)
|
||||
// 0x9c9e XPKeywords int8u IFD0
|
||||
// 0x9c9f XPSubject int8u IFD0
|
||||
// 0xa300 FileSource undef ExifIFD 1 = Film Scanner
|
||||
// 2 = Reflection Print Scanner
|
||||
// 3 = Digital Camera
|
||||
// "\x03\x00\x00\x00" = Sigma Digital Camera
|
||||
// 0x9286 UserComment undef ExifIFD
|
||||
// 0x013b Artist string IFD0 (becomes a list-type tag when the MWG module is loaded)
|
||||
}
|
5
.vscode/mklink.md
vendored
5
.vscode/mklink.md
vendored
@ -17,3 +17,8 @@ mklink /J "L:\Git\AA\Shared\.kanbn" "D:\5-Other-Small\Kanban\View-by-Distance"
|
||||
```bash Sat Aug 17 2024 11:09:42 GMT-0700 (Mountain Standard Time)
|
||||
mklink /J "L:\Git\AA\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\fa0fa59b-afe4-4960-9afc-18fcbc7fb41b"
|
||||
```
|
||||
|
||||
```bash 1735322893065 = 638709196930650000 = Fri Dec 27 2024 11:08:12 GMT-0700 (Mountain Standard Time)
|
||||
mklink /J "L:\Git\AA\Rename\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\fa0fa59b-afe4-4960-9afc-18fcbc7fb41b"
|
||||
mklink /J "L:\Git\AA\Compare\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\770b6ae3-266e-4d5f-970a-173709b064de"
|
||||
```
|
||||
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -30,6 +30,7 @@
|
||||
"paramref",
|
||||
"permille",
|
||||
"permyriad",
|
||||
"pged",
|
||||
"Phares",
|
||||
"Phgtv",
|
||||
"Photoshop",
|
||||
|
139
.vscode/tasks.json
vendored
139
.vscode/tasks.json
vendored
@ -13,16 +13,6 @@
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Format-Whitespaces",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"format",
|
||||
"whitespace"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "User Secrets Set",
|
||||
"command": "dotnet",
|
||||
@ -38,11 +28,34 @@
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Format",
|
||||
"label": "Format-Rename-Whitespaces",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"format",
|
||||
"${workspaceFolder}/Rename/AA.Rename.csproj",
|
||||
"whitespace"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Format-Metadata-Whitespaces",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"format",
|
||||
"${workspaceFolder}/Metadata/AA.Metadata.csproj",
|
||||
"whitespace"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Format-Rename",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"format",
|
||||
"${workspaceFolder}/Rename/AA.Rename.csproj",
|
||||
"--report",
|
||||
".vscode",
|
||||
"--verbosity",
|
||||
@ -53,12 +66,112 @@
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build",
|
||||
"label": "Format-Metadata",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"format",
|
||||
"${workspaceFolder}/Metadata/AA.Metadata.csproj",
|
||||
"--report",
|
||||
".vscode",
|
||||
"--verbosity",
|
||||
"detailed",
|
||||
"--severity",
|
||||
"warn"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build-Shared",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/AA.sln",
|
||||
"${workspaceFolder}/Shared/AA.Shared.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build-Face",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Face/AA.Face.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build-People",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/People/AA.People.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build-Distance",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Distance/AA.Distance.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build-Metadata",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Metadata/AA.Metadata.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build-FaceRecognitionDotNet",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/FaceRecognitionDotNet/AA.FaceRecognitionDotNet.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build-Rename",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Rename/AA.Rename.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build-Compare",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Compare/AA.Compare.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
34
AA.sln
34
AA.sln
@ -1,34 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA.Shared", "Shared\AA.Shared.csproj", "{74E6778C-FCBE-4959-AFED-7C528BC5F855}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA.Metadata", "Metadata\AA.Metadata.csproj", "{9E1FE0DA-8A9C-49B4-82BC-67F68D42F49C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA.Rename", "Rename\AA.Rename.csproj", "{E80BF7A6-C892-426D-829C-910BD4700E69}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{74E6778C-FCBE-4959-AFED-7C528BC5F855}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{74E6778C-FCBE-4959-AFED-7C528BC5F855}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{74E6778C-FCBE-4959-AFED-7C528BC5F855}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{74E6778C-FCBE-4959-AFED-7C528BC5F855}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9E1FE0DA-8A9C-49B4-82BC-67F68D42F49C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9E1FE0DA-8A9C-49B4-82BC-67F68D42F49C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9E1FE0DA-8A9C-49B4-82BC-67F68D42F49C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9E1FE0DA-8A9C-49B4-82BC-67F68D42F49C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E80BF7A6-C892-426D-829C-910BD4700E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E80BF7A6-C892-426D-829C-910BD4700E69}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E80BF7A6-C892-426D-829C-910BD4700E69}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E80BF7A6-C892-426D-829C-910BD4700E69}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
50
Compare/AA.Compare.csproj
Normal file
50
Compare/AA.Compare.csproj
Normal file
@ -0,0 +1,50 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<UserSecretsId>770b6ae3-266e-4d5f-970a-173709b064de</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PackageId>Phares.View.by.Distance.Compare</PackageId>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<Version>9.0.100.0</Version>
|
||||
<Authors>Mike Phares</Authors>
|
||||
<Company>Phares</Company>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
|
||||
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
|
||||
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsWindows)'=='true'">
|
||||
<DefineConstants>Windows</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsOSX)'=='true'">
|
||||
<DefineConstants>OSX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsLinux)'=='true'">
|
||||
<DefineConstants>Linux</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
|
||||
<SupportedPlatform Include="browser" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Face\AA.Face.csproj" />
|
||||
<ProjectReference Include="..\Distance\AA.Distance.csproj" />
|
||||
<ProjectReference Include="..\Metadata\AA.Metadata.csproj" />
|
||||
<ProjectReference Include="..\People\AA.People.csproj" />
|
||||
<ProjectReference Include="..\Shared\AA.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
168
Compare/Compare.cs
Normal file
168
Compare/Compare.cs
Normal file
@ -0,0 +1,168 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ShellProgressBar;
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Compare.Models;
|
||||
using View_by_Distance.Distance.Models.Stateless.Methods;
|
||||
using View_by_Distance.Face.Models.Stateless.Methods;
|
||||
using View_by_Distance.Metadata.Models;
|
||||
using View_by_Distance.People.Models.Stateless.Methods;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Compare;
|
||||
|
||||
public partial class Compare : ICompare, IDisposable
|
||||
{
|
||||
|
||||
private ProgressBar? _ProgressBar;
|
||||
private readonly ProgressBarOptions _ProgressBarOptions;
|
||||
|
||||
public Compare(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
|
||||
{
|
||||
if (isSilent)
|
||||
{ }
|
||||
if (args is null)
|
||||
throw new NullReferenceException(nameof(args));
|
||||
if (console is null)
|
||||
throw new NullReferenceException(nameof(console));
|
||||
ICompare compare = this;
|
||||
long ticks = DateTime.Now.Ticks;
|
||||
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
|
||||
CompareWork(logger, appSettings, compare, ticks);
|
||||
}
|
||||
|
||||
void ICompare.Tick() =>
|
||||
_ProgressBar?.Tick();
|
||||
|
||||
void ICompare.ConstructProgressBar(int maxTicks, string message)
|
||||
{
|
||||
_ProgressBar?.Dispose();
|
||||
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
_ProgressBar?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private static bool GetRunToDoCollectionFirst(AppSettings appSettings, long ticks)
|
||||
{
|
||||
bool result = appSettings.DistanceSettings.SaveSortingWithoutPerson;
|
||||
if (!result)
|
||||
result = !IId.IsOffsetDeterministicHashCode(appSettings.MetadataSettings);
|
||||
if (!result)
|
||||
{
|
||||
string[] directories;
|
||||
directories = Directory.GetDirectories(appSettings.ResultSettings.RootDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
if (directories.Length == 0)
|
||||
result = true;
|
||||
else
|
||||
{
|
||||
string seasonDirectory;
|
||||
DirectoryInfo directoryInfo;
|
||||
DateTime dateTime = new(ticks);
|
||||
string rootDirectory = appSettings.ResultSettings.RootDirectory;
|
||||
(int season, string seasonName) = IDate.GetSeason(dateTime.DayOfYear);
|
||||
string eDistanceContentDirectory = IResult.GetResultsDateGroupDirectory(appSettings.ResultSettings, nameof(E_Distance), appSettings.ResultSettings.ResultContent);
|
||||
FileSystemInfo fileSystemInfo = new DirectoryInfo(eDistanceContentDirectory);
|
||||
string[] checkDirectories =
|
||||
[
|
||||
Path.Combine(rootDirectory, "Ancestry"),
|
||||
Path.Combine(rootDirectory, "Facebook"),
|
||||
Path.Combine(rootDirectory, "LinkedIn")
|
||||
];
|
||||
foreach (string checkDirectory in checkDirectories)
|
||||
{
|
||||
if (checkDirectory == rootDirectory)
|
||||
seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName}");
|
||||
else
|
||||
seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}");
|
||||
if (!Directory.Exists(seasonDirectory))
|
||||
_ = Directory.CreateDirectory(seasonDirectory);
|
||||
if (result)
|
||||
continue;
|
||||
directories = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string directory in directories)
|
||||
{
|
||||
directoryInfo = new(directory);
|
||||
if (directoryInfo.LastWriteTime > fileSystemInfo.LastWriteTime)
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
result = true;
|
||||
if (!result)
|
||||
result = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollections GetReadOnlyCollections(AppSettings appSettings)
|
||||
{
|
||||
ReadOnlyCollections result;
|
||||
ReadOnlyCollection<PersonContainer> personContainers = IPeople.GetPersonContainers(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.PeopleSettings, appSettings.CompareSettings);
|
||||
ReadOnlyCollection<long> personKeys = IPeople.GetPersonKeys(personContainers);
|
||||
result = IPeople.GetReadOnlyCollections(appSettings.ResultSettings, appSettings.PeopleSettings, appSettings.DistanceSettings, appSettings.CompareSettings, personContainers, personKeys);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<ExifDirectory> GetMappedExifDirectoryWithEncoding(AppSettings appSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections)
|
||||
{
|
||||
ReadOnlyCollection<ExifDirectory> results;
|
||||
ReadOnlyCollection<ExifDirectory> exifDirectories = IDistance.GetMapped(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.PeopleSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, readOnlyCollections);
|
||||
if (exifDirectories.Count == 0 && !appSettings.DistanceSettings.SaveSortingWithoutPerson)
|
||||
throw new NotSupportedException($"Switch {nameof(appSettings.DistanceSettings.SaveSortingWithoutPerson)}!");
|
||||
results = IDistance.GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
|
||||
if (results.Count == 0 && !appSettings.DistanceSettings.SaveSortingWithoutPerson)
|
||||
throw new NotSupportedException($"Switch {nameof(appSettings.DistanceSettings.SaveSortingWithoutPerson)}!");
|
||||
return results;
|
||||
}
|
||||
|
||||
private void CompareWork(ILogger<Program>? logger, AppSettings appSettings, ICompare compare, long ticks)
|
||||
{
|
||||
const int updated = 0;
|
||||
DistanceLimits? distanceLimits;
|
||||
logger?.LogInformation("{Ticks}", ticks);
|
||||
ReadOnlyCollection<LocationContainer> matrix;
|
||||
ReadOnlyCollection<SaveContainer> saveContainers;
|
||||
ReadOnlyCollection<ExifDirectory> exifDirectories;
|
||||
ReadOnlyCollection<LocationContainer> preFiltered;
|
||||
ReadOnlyCollection<LocationContainer> postFiltered;
|
||||
ReadOnlyDictionary<string, LocationContainer> onlyOne;
|
||||
bool runToDoCollectionFirst = GetRunToDoCollectionFirst(appSettings, ticks);
|
||||
ReadOnlyCollections readOnlyCollections = GetReadOnlyCollections(appSettings);
|
||||
ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding = GetMappedExifDirectoryWithEncoding(appSettings, compare, ticks, readOnlyCollections);
|
||||
ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs = IDistance.Extract(appSettings.CompareSettings, mappedExifDirectoryWithEncoding);
|
||||
foreach (string outputResolution in appSettings.CompareSettings.OutputResolutions)
|
||||
{
|
||||
if (runToDoCollectionFirst || outputResolution.Any(char.IsNumber))
|
||||
continue;
|
||||
_ProgressBar?.Dispose();
|
||||
logger?.LogInformation("{outputResolution}", outputResolution);
|
||||
exifDirectories = IFace.GetExifDirectories(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, outputResolution);
|
||||
preFiltered = IDistance.GetPreFilterLocationContainer(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
|
||||
if (preFiltered.Count == 0)
|
||||
continue;
|
||||
distanceLimits = new(appSettings.DistanceSettings);
|
||||
postFiltered = IDistance.GetPostFilterLocationContainer(preFiltered, distanceLimits);
|
||||
if (postFiltered.Count == 0)
|
||||
continue;
|
||||
matrix = IDistance.GetMatrixLocationContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
|
||||
if (matrix.Count == 0)
|
||||
continue;
|
||||
onlyOne = IDistance.GetOnlyOne(appSettings.DistanceSettings, matrix);
|
||||
if (onlyOne.Count == 0)
|
||||
continue;
|
||||
saveContainers = IDistance.GetSaveContainers(appSettings.ResultSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, outputResolution, onlyOne);
|
||||
if (saveContainers.Count == 0)
|
||||
continue;
|
||||
IDistance.SaveContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, updated, saveContainers);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
78
Compare/Models/AppSettings.cs
Normal file
78
Compare/Models/AppSettings.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using View_by_Distance.Shared.Models;
|
||||
|
||||
namespace View_by_Distance.Compare.Models;
|
||||
|
||||
public record AppSettings(ResultSettings ResultSettings,
|
||||
MetadataSettings MetadataSettings,
|
||||
PeopleSettings PeopleSettings,
|
||||
DistanceSettings DistanceSettings,
|
||||
CompareSettings CompareSettings)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void Verify(AppSettings appSettings)
|
||||
{
|
||||
if (appSettings.DistanceSettings.RangeDaysDeltaTolerance.Length != 3)
|
||||
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDaysDeltaTolerance));
|
||||
if (appSettings.DistanceSettings.RangeDistanceTolerance.Length != 3)
|
||||
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDistanceTolerance));
|
||||
if (appSettings.DistanceSettings.RangeFaceAreaTolerance.Length != 3)
|
||||
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceAreaTolerance));
|
||||
if (appSettings.DistanceSettings.RangeFaceConfidence.Length != 3)
|
||||
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceConfidence));
|
||||
_ = DateTime.Now.AddDays(-appSettings.DistanceSettings.RangeDaysDeltaTolerance[1]);
|
||||
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && appSettings.PeopleSettings.JLinks.Length > 0)
|
||||
throw new Exception("Settings has SaveSortingWithoutPerson and JLinks!");
|
||||
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && !string.IsNullOrEmpty(appSettings.DistanceSettings.FocusModel))
|
||||
throw new Exception("Settings has SaveSortingWithoutPerson and FocusModel!");
|
||||
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && !string.IsNullOrEmpty(appSettings.DistanceSettings.FocusDirectory))
|
||||
throw new Exception("Settings has SaveSortingWithoutPerson and FocusDirectory!");
|
||||
if (appSettings.CompareSettings.MaxDegreeOfParallelism > Environment.ProcessorCount)
|
||||
throw new Exception("MaxDegreeOfParallelism must be =< Environment.ProcessorCount!");
|
||||
if (!string.IsNullOrEmpty(appSettings.DistanceSettings.FocusDirectory) && appSettings.DistanceSettings.FocusDirectory.Length != 2)
|
||||
throw new NotSupportedException($"{nameof(appSettings.DistanceSettings.FocusDirectory)} currently only works with output directory! Example 00.");
|
||||
}
|
||||
|
||||
public static AppSettings Get(IConfigurationRoot configurationRoot)
|
||||
{
|
||||
AppSettings result;
|
||||
#pragma warning disable IL3050, IL2026
|
||||
ResultSettings? resultSettings = configurationRoot.GetSection(nameof(ResultSettings)).Get<ResultSettings>();
|
||||
MetadataSettings? metadataSettings = configurationRoot.GetSection(nameof(MetadataSettings)).Get<MetadataSettings>();
|
||||
PeopleSettings? peopleSettings = configurationRoot.GetSection(nameof(PeopleSettings)).Get<PeopleSettings>();
|
||||
DistanceSettings? distanceSettings = configurationRoot.GetSection(nameof(DistanceSettings)).Get<DistanceSettings>();
|
||||
CompareSettings? compareSettings = configurationRoot.GetSection(nameof(CompareSettings)).Get<CompareSettings>();
|
||||
#pragma warning restore IL3050, IL2026
|
||||
if (resultSettings is null || metadataSettings is null || peopleSettings is null || distanceSettings is null || compareSettings?.Company is null)
|
||||
{
|
||||
List<string> paths = [];
|
||||
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
|
||||
{
|
||||
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
|
||||
continue;
|
||||
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
|
||||
continue;
|
||||
paths.Add(physicalFileProvider.Root);
|
||||
}
|
||||
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
|
||||
}
|
||||
result = new(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings);
|
||||
Verify(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(AppSettings))]
|
||||
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
30
Compare/Models/CompareSettings.cs
Normal file
30
Compare/Models/CompareSettings.cs
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Compare.Models;
|
||||
|
||||
public record CompareSettings(string Company,
|
||||
string FacesFileNameExtension,
|
||||
string FacesHiddenFileNameExtension,
|
||||
string FacesPartsFileNameExtension,
|
||||
string[] IgnoreExtensions,
|
||||
int MaxDegreeOfParallelism,
|
||||
string[] OutputResolutions,
|
||||
string[] ValidImageFormatExtensions,
|
||||
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.ICompareSettings
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, CompareSettingsSourceGenerationContext.Default.CompareSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(CompareSettings))]
|
||||
internal partial class CompareSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
32
Compare/Models/Identifier.cs
Normal file
32
Compare/Models/Identifier.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Compare.Models;
|
||||
|
||||
internal sealed record Identifier(string[] DirectoryNames,
|
||||
bool? HasDateTimeOriginal,
|
||||
int Id,
|
||||
long Length,
|
||||
string PaddedId,
|
||||
long Ticks)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, IdentifierSourceGenerationContext.Default.Identifier);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(Identifier))]
|
||||
internal partial class IdentifierSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(Identifier[]))]
|
||||
internal partial class IdentifierCollectionSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
53
Compare/Program.cs
Normal file
53
Compare/Program.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using View_by_Distance.Compare.Models;
|
||||
|
||||
namespace View_by_Distance.Compare;
|
||||
|
||||
public class Program
|
||||
{
|
||||
|
||||
public static void Secondary(ILogger<Program> logger, List<string> args)
|
||||
{
|
||||
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.AddUserSecrets<Program>();
|
||||
IConfigurationRoot configurationRoot = configurationBuilder.Build();
|
||||
AppSettings appSettings = AppSettings.Get(configurationRoot);
|
||||
int silentIndex = args.IndexOf("s");
|
||||
if (silentIndex > -1)
|
||||
args.RemoveAt(silentIndex);
|
||||
try
|
||||
{
|
||||
if (args is null)
|
||||
throw new Exception("args is null!");
|
||||
Shared.Models.Console console = new();
|
||||
_ = new Compare(args, logger, appSettings, silentIndex > -1, console);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger?.LogError(ex, "Error!");
|
||||
}
|
||||
if (silentIndex > -1)
|
||||
logger?.LogInformation("Done. Bye");
|
||||
else
|
||||
{
|
||||
logger?.LogInformation("Done. Press 'Enter' to end");
|
||||
_ = Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#pragma warning disable IL3050
|
||||
ILogger<Program>? logger = Host.CreateDefaultBuilder(args).Build().Services.GetRequiredService<ILogger<Program>>();
|
||||
#pragma warning restore IL3050
|
||||
if (args is not null)
|
||||
Secondary(logger, args.ToList());
|
||||
else
|
||||
Secondary(logger, []);
|
||||
}
|
||||
|
||||
}
|
46
Distance/AA.Distance.csproj
Normal file
46
Distance/AA.Distance.csproj
Normal file
@ -0,0 +1,46 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>library</OutputType>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PackageId>Phares.AA.Distance</PackageId>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<Version>9.0.100.0</Version>
|
||||
<Authors>Mike Phares</Authors>
|
||||
<Company>Phares</Company>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
|
||||
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
|
||||
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsWindows)'=='true'">
|
||||
<DefineConstants>Windows</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsOSX)'=='true'">
|
||||
<DefineConstants>OSX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsLinux)'=='true'">
|
||||
<DefineConstants>Linux</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
|
||||
<SupportedPlatform Include="browser" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FaceRecognitionDotNet\AA.FaceRecognitionDotNet.csproj" />
|
||||
<ProjectReference Include="..\Metadata\AA.Metadata.csproj" />
|
||||
<ProjectReference Include="..\Shared\AA.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
3
Distance/Models/C_Resize.cs
Normal file
3
Distance/Models/C_Resize.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace View_by_Distance.Metadata.Models;
|
||||
|
||||
public class C_Resize() { }
|
3
Distance/Models/D2_FaceParts.cs
Normal file
3
Distance/Models/D2_FaceParts.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace View_by_Distance.Metadata.Models;
|
||||
|
||||
public class D2_FaceParts() { }
|
59
Distance/Models/DistanceLimits.cs
Normal file
59
Distance/Models/DistanceLimits.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Methods;
|
||||
|
||||
namespace View_by_Distance.Metadata.Models;
|
||||
|
||||
public class DistanceLimits : IDistanceLimits
|
||||
{
|
||||
|
||||
private int _Days;
|
||||
private int _Distance;
|
||||
|
||||
public double FaceAreaPermyriad { init; get; }
|
||||
public double RangeDaysDeltaTolerance { init; get; }
|
||||
public double FaceConfidencePercent { init; get; }
|
||||
public double FaceDistancePermyriad { init; get; }
|
||||
public int SortingMaximumPerFaceShouldBeHigh { init; get; }
|
||||
public bool RangeDaysDeltaTargetLessThenUpper { init; get; }
|
||||
public double RangeDistanceToleranceUpperLimit { init; get; }
|
||||
|
||||
public DistanceLimits(DistanceSettings distanceSettings, int? useFiltersCounter = null)
|
||||
{
|
||||
RangeDistanceToleranceUpperLimit = distanceSettings.RangeDistanceTolerance[2];
|
||||
SortingMaximumPerFaceShouldBeHigh = distanceSettings.SortingMaximumPerFaceShouldBeHigh;
|
||||
RangeDaysDeltaTargetLessThenUpper = distanceSettings.RangeDaysDeltaTolerance[1] > distanceSettings.RangeDaysDeltaTolerance[2];
|
||||
if (useFiltersCounter is null)
|
||||
{
|
||||
RangeDaysDeltaTolerance = distanceSettings.RangeDaysDeltaTolerance[1];
|
||||
FaceConfidencePercent = distanceSettings.FaceConfidencePercent * distanceSettings.RangeFaceConfidence[1];
|
||||
FaceAreaPermyriad = distanceSettings.FaceAreaPermyriad * distanceSettings.RangeFaceAreaTolerance[1];
|
||||
FaceDistancePermyriad = distanceSettings.FaceDistancePermyriad * distanceSettings.RangeDistanceTolerance[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
RangeDaysDeltaTolerance = ((distanceSettings.RangeDaysDeltaTolerance[2] - distanceSettings.RangeDaysDeltaTolerance[0]) * 0.01 * useFiltersCounter.Value) + distanceSettings.RangeDaysDeltaTolerance[1];
|
||||
FaceConfidencePercent = (distanceSettings.FaceConfidencePercent * ((distanceSettings.RangeFaceConfidence[2] - distanceSettings.RangeFaceConfidence[0]) * 0.01 * useFiltersCounter.Value)) + distanceSettings.RangeFaceConfidence[1];
|
||||
FaceDistancePermyriad = (distanceSettings.FaceDistancePermyriad * ((distanceSettings.RangeDistanceTolerance[2] - distanceSettings.RangeDistanceTolerance[0]) * 0.01 * useFiltersCounter.Value)) + distanceSettings.RangeDistanceTolerance[1];
|
||||
FaceAreaPermyriad = (distanceSettings.FaceAreaPermyriad * ((distanceSettings.RangeFaceAreaTolerance[2] - distanceSettings.RangeFaceAreaTolerance[0]) * 0.01 * useFiltersCounter.Value)) + distanceSettings.RangeFaceAreaTolerance[1];
|
||||
}
|
||||
}
|
||||
|
||||
string IDistanceLimits.GetCounts()
|
||||
{
|
||||
string result;
|
||||
List<(int Value, string Name)> results =
|
||||
[
|
||||
new(_Days, nameof(_Days)),
|
||||
new(_Distance, nameof(_Distance))
|
||||
];
|
||||
result = string.Join(' ', from l in results orderby l.Value descending select $"{l.Name}_{l.Value};");
|
||||
return result;
|
||||
}
|
||||
|
||||
void IDistanceLimits.AddCounts(int days, int distance)
|
||||
{
|
||||
_Days += days;
|
||||
_Distance += distance;
|
||||
}
|
||||
|
||||
}
|
3
Distance/Models/E_Distance.cs
Normal file
3
Distance/Models/E_Distance.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace View_by_Distance.Metadata.Models;
|
||||
|
||||
public class E_Distance() { }
|
17
Distance/Models/IDistanceLimits.cs
Normal file
17
Distance/Models/IDistanceLimits.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace View_by_Distance.Shared.Models.Methods;
|
||||
|
||||
public interface IDistanceLimits
|
||||
{
|
||||
|
||||
public double FaceAreaPermyriad { init; get; }
|
||||
public double RangeDaysDeltaTolerance { init; get; }
|
||||
public double FaceConfidencePercent { init; get; }
|
||||
public double FaceDistancePermyriad { init; get; }
|
||||
public int SortingMaximumPerFaceShouldBeHigh { init; get; }
|
||||
public bool RangeDaysDeltaTargetLessThenUpper { init; get; }
|
||||
public double RangeDistanceToleranceUpperLimit { init; get; }
|
||||
|
||||
string GetCounts();
|
||||
void AddCounts(int days, int distance);
|
||||
|
||||
}
|
35
Distance/Models/Stateless/FaceEncodingLogic.cs
Normal file
35
Distance/Models/Stateless/FaceEncodingLogic.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Distance.Models.Stateless.Methods;
|
||||
|
||||
internal static class FaceEncodingLogic
|
||||
{
|
||||
|
||||
internal static ReadOnlyCollection<ExifDirectory> GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories)
|
||||
{
|
||||
List<ExifDirectory> results = [];
|
||||
string? json;
|
||||
FaceEncoding? faceEncoding;
|
||||
ExifDirectory exifDirectory;
|
||||
FaceRecognitionDotNet.FaceEncoding? encoding;
|
||||
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
||||
string message = $") Building Mapped with Encoding Face Files Collection - {totalSeconds} total second(s)";
|
||||
compare.ConstructProgressBar(exifDirectories.Count, message);
|
||||
foreach (ExifDirectory e in exifDirectories)
|
||||
{
|
||||
compare.Tick();
|
||||
json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(e);
|
||||
faceEncoding = json is null ? null : JsonSerializer.Deserialize<FaceEncoding>(json);
|
||||
if (faceEncoding is null)
|
||||
continue;
|
||||
encoding = FaceRecognitionDotNet.FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding);
|
||||
exifDirectory = ExifDirectory.Get(encoding, e);
|
||||
results.Add(exifDirectory);
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
}
|
93
Distance/Models/Stateless/FilterLogicA.cs
Normal file
93
Distance/Models/Stateless/FilterLogicA.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Distance.Models.Stateless.Methods;
|
||||
|
||||
internal static class FilterLogicA
|
||||
{
|
||||
|
||||
private static bool? CanReMap(LocationContainer locationContainer)
|
||||
{
|
||||
bool? result = null;
|
||||
ArgumentNullException.ThrowIfNull(locationContainer);
|
||||
// if (locationContainer.WholePercentages is null)
|
||||
// result = null;
|
||||
// else
|
||||
// throw new NotImplementedException();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool? IsFocusPerson(DistanceSettings distanceSettings, ReadOnlyCollections readOnlyCollections, LocationContainer locationContainer)
|
||||
{
|
||||
bool? result;
|
||||
if (distanceSettings.SkipPersonWithMoreThen < 1 && readOnlyCollections.JLinkResolvedPersonKeys.Count == 0)
|
||||
result = null;
|
||||
else if (locationContainer.WholePercentages is null)
|
||||
result = null;
|
||||
else
|
||||
throw new NotImplementedException();
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<LocationContainer> GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories)
|
||||
{
|
||||
List<LocationContainer> results = [];
|
||||
string? json;
|
||||
string? model;
|
||||
bool? canReMap;
|
||||
bool? isFocusPerson;
|
||||
bool? inSkipCollection;
|
||||
FaceEncoding? faceEncoding;
|
||||
FaceRecognitionDotNet.FaceEncoding? encoding;
|
||||
ReadOnlyDictionary<int, FilePath>? keyValues;
|
||||
List<FilePathAndWholePercentages>? wholePercentagesCollection;
|
||||
ReadOnlyCollection<LocationContainer> locationContainers = FilterLogicB.GetLocationContainers(distanceSettings, compareSettings, compare, ticks, exifDirectories, nameof(FilterLogicA));
|
||||
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
||||
string message = $") PreFiltering LocationContainers Face Files Collection - {totalSeconds} total second(s)";
|
||||
compare.ConstructProgressBar(locationContainers.Count, message);
|
||||
foreach (LocationContainer locationContainer in locationContainers)
|
||||
{
|
||||
compare.Tick();
|
||||
if (locationContainer.FilePath.Id is null || locationContainer.WholePercentages is null)
|
||||
continue;
|
||||
if (keyValuePairs.TryGetValue(locationContainer.FilePath.Id.Value, out keyValues))
|
||||
{
|
||||
if (keyValues.ContainsKey(locationContainer.WholePercentages.Value))
|
||||
continue;
|
||||
}
|
||||
if (locationContainer.ExifDirectory is null || locationContainer.FaceFile is null)
|
||||
continue;
|
||||
inSkipCollection = readOnlyCollections.SkipNotSkipCollection.TryGetValue(locationContainer.FilePath.Id.Value, out wholePercentagesCollection) && wholePercentagesCollection.Any(l => l.WholePercentages == locationContainer.WholePercentages.Value);
|
||||
if (inSkipCollection is not null && inSkipCollection.Value)
|
||||
continue;
|
||||
canReMap = CanReMap(locationContainer);
|
||||
if (canReMap is not null && !canReMap.Value)
|
||||
continue;
|
||||
isFocusPerson = IsFocusPerson(distanceSettings, readOnlyCollections, locationContainer);
|
||||
if (isFocusPerson is not null && !isFocusPerson.Value)
|
||||
continue;
|
||||
if (!string.IsNullOrEmpty(distanceSettings.FocusModel))
|
||||
{
|
||||
model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(locationContainer.ExifDirectory);
|
||||
if (string.IsNullOrEmpty(model) || !model.Contains(distanceSettings.FocusModel))
|
||||
continue;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(distanceSettings.FocusDirectory))
|
||||
{
|
||||
if (!locationContainer.FilePath.DirectoryFullPath.Contains(distanceSettings.FocusDirectory))
|
||||
continue;
|
||||
}
|
||||
json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory);
|
||||
faceEncoding = json is null ? null : JsonSerializer.Deserialize<FaceEncoding>(json);
|
||||
if (faceEncoding is null)
|
||||
continue;
|
||||
encoding = FaceRecognitionDotNet.FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding);
|
||||
results.Add(LocationContainer.Get(locationContainer, encoding, keepExifDirectory: false));
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
}
|
80
Distance/Models/Stateless/FilterLogicB.cs
Normal file
80
Distance/Models/Stateless/FilterLogicB.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.Text.Json;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Distance.Models.Stateless.Methods;
|
||||
|
||||
internal static class FilterLogicB
|
||||
{
|
||||
|
||||
private static void MoveUnableToMatch(FilePath filePath)
|
||||
{
|
||||
string checkFile = $"{filePath.FullName}.unk";
|
||||
if (File.Exists(filePath.FullName) && !File.Exists(checkFile))
|
||||
File.Move(filePath.FullName, checkFile);
|
||||
}
|
||||
|
||||
private static void LocationContainersParallelFor(DistanceSettings distanceSettings, ICompareSettings compareSettings, List<LocationContainer> locationContainers, ExifDirectory exifDirectory)
|
||||
{
|
||||
string? json;
|
||||
if (exifDirectory.FilePath.Id is null)
|
||||
return;
|
||||
DateOnly dateOnly = DateOnly.FromDateTime(new DateTime(exifDirectory.FilePath.CreationTicks));
|
||||
int? wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath);
|
||||
if (wholePercentages is null)
|
||||
{
|
||||
if (distanceSettings.DistanceMoveUnableToMatch)
|
||||
MoveUnableToMatch(exifDirectory.FilePath);
|
||||
return;
|
||||
}
|
||||
json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(exifDirectory);
|
||||
if (json is null || !json.Contains(nameof(DateTime)))
|
||||
{
|
||||
if (distanceSettings.DistanceMoveUnableToMatch)
|
||||
MoveUnableToMatch(exifDirectory.FilePath);
|
||||
return;
|
||||
}
|
||||
FaceFile? faceFile = JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
|
||||
if (faceFile is null || faceFile.Location is null)
|
||||
{
|
||||
if (distanceSettings.DistanceMoveUnableToMatch)
|
||||
MoveUnableToMatch(exifDirectory.FilePath);
|
||||
return;
|
||||
}
|
||||
RectangleF? rectangle = Shared.Models.Stateless.Methods.ILocation.GetPercentagesRectangle(distanceSettings, wholePercentages.Value);
|
||||
if (rectangle is null)
|
||||
return;
|
||||
LocationContainer locationContainer = new(dateOnly,
|
||||
exifDirectory,
|
||||
exifDirectory.Encoding,
|
||||
faceFile,
|
||||
exifDirectory.FilePath,
|
||||
null,
|
||||
null,
|
||||
exifDirectory.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
|
||||
rectangle,
|
||||
wholePercentages);
|
||||
lock (locationContainers)
|
||||
locationContainers.Add(locationContainer);
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<LocationContainer> GetLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories, string sourceClass)
|
||||
{
|
||||
List<LocationContainer> results = [];
|
||||
int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
|
||||
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
||||
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
|
||||
string message = $") Building LocationContainers Face Files Collection {sourceClass} - {totalSeconds} total second(s)";
|
||||
compare.ConstructProgressBar(exifDirectories.Count, message);
|
||||
_ = Parallel.For(0, exifDirectories.Count, parallelOptions, (i, state) =>
|
||||
{
|
||||
compare.Tick();
|
||||
LocationContainersParallelFor(distanceSettings, compareSettings, results, exifDirectories[i]);
|
||||
});
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
}
|
100
Distance/Models/Stateless/FilterLogicC.cs
Normal file
100
Distance/Models/Stateless/FilterLogicC.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Metadata.Models;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Distance.Models.Stateless.Methods;
|
||||
|
||||
internal static class FilterLogicC
|
||||
{
|
||||
|
||||
internal static ReadOnlyCollection<LocationContainer> GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits)
|
||||
{
|
||||
List<LocationContainer> results = [];
|
||||
foreach (LocationContainer locationContainer in preFiltered)
|
||||
{
|
||||
if (locationContainer.FaceFile is null)
|
||||
continue;
|
||||
if (locationContainer.FaceFile.AreaPermyriad < distanceLimits.FaceAreaPermyriad)
|
||||
continue;
|
||||
if (locationContainer.FaceFile.ConfidencePercent < distanceLimits.FaceConfidencePercent)
|
||||
continue;
|
||||
results.Add(locationContainer);
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<LocationContainer> GetCombined(DistanceSettings distanceSettings, Shared.Models.Properties.ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories, ReadOnlyCollection<LocationContainer> postFiltered)
|
||||
{
|
||||
List<LocationContainer> results = [];
|
||||
foreach (LocationContainer locationContainer in postFiltered)
|
||||
results.Add(locationContainer);
|
||||
ReadOnlyCollection<LocationContainer> locationContainers = FilterLogicB.GetLocationContainers(distanceSettings, compareSettings, compare, ticks, exifDirectories, nameof(FilterLogicC));
|
||||
foreach (LocationContainer locationContainer in locationContainers)
|
||||
results.Add(locationContainer);
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(DistanceSettings distanceSettings, Shared.Models.Properties.ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered)
|
||||
{
|
||||
List<LocationContainer> results = [];
|
||||
ReadOnlyCollection<LocationContainer> collection;
|
||||
ReadOnlyCollection<LocationContainer> locationContainers = GetCombined(distanceSettings, compareSettings, compare, ticks, mappedExifDirectoryWithEncoding, postFiltered);
|
||||
string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
|
||||
compare.ConstructProgressBar(postFiltered.Count, message);
|
||||
foreach (LocationContainer locationContainer in postFiltered)
|
||||
{
|
||||
compare.Tick();
|
||||
if (locationContainer.FilePath.Id is null)
|
||||
continue;
|
||||
collection = FaceRecognitionDotNet.FaceRecognition.GetLocationContainers(distanceSettings.FaceDistancePermyriad, locationContainers, locationContainer);
|
||||
foreach (LocationContainer l in collection)
|
||||
{
|
||||
// if (locationContainer.FilePath.Id == -626078035 && l.FilePath.Id == -626078035)
|
||||
// continue;
|
||||
if (l.FilePath.Id is null)
|
||||
continue;
|
||||
if (l.LengthPermyriad is null)
|
||||
continue;
|
||||
if (l.LengthPermyriad > distanceLimits.FaceDistancePermyriad)
|
||||
break;
|
||||
if (!distanceSettings.SaveSortingWithoutPerson && l.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
|
||||
continue;
|
||||
if (l.FilePath.Id.Value == locationContainer.FilePath.Id.Value && l.WholePercentages == locationContainer.WholePercentages)
|
||||
continue;
|
||||
results.Add(l);
|
||||
}
|
||||
}
|
||||
LocationContainer[] array = results.OrderBy(l => l.LengthPermyriad).ToArray();
|
||||
return array.AsReadOnly();
|
||||
}
|
||||
|
||||
internal static ReadOnlyDictionary<string, LocationContainer> GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix)
|
||||
{
|
||||
Dictionary<string, LocationContainer> results = [];
|
||||
List<string> added = [];
|
||||
LocationContainer? tryGetValue;
|
||||
foreach (LocationContainer locationContainer in matrix)
|
||||
{
|
||||
if (distanceSettings.SaveIndividually)
|
||||
break;
|
||||
if (locationContainer.LengthSource is null)
|
||||
continue;
|
||||
if (distanceSettings.UseExtraPersonKeyCheck)
|
||||
{
|
||||
if (results.TryGetValue(locationContainer.LengthSource.Name, out tryGetValue))
|
||||
{
|
||||
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is not null && tryGetValue.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is not null && locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyTicks != tryGetValue.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyTicks)
|
||||
_ = results.Remove(locationContainer.LengthSource.Name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (added.Contains(locationContainer.LengthSource.Name))
|
||||
continue;
|
||||
added.Add(locationContainer.LengthSource.Name);
|
||||
results.Add(locationContainer.LengthSource.Name, locationContainer);
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
}
|
288
Distance/Models/Stateless/FilterLogicD.cs
Normal file
288
Distance/Models/Stateless/FilterLogicD.cs
Normal file
@ -0,0 +1,288 @@
|
||||
using Humanizer;
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Metadata.Models;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
using WindowsShortcutFactory;
|
||||
|
||||
namespace View_by_Distance.Distance.Models.Stateless.Methods;
|
||||
|
||||
internal static class FilterLogicD
|
||||
{
|
||||
|
||||
internal record RecordA(long? Ticks, string? Directory);
|
||||
|
||||
internal record RecordB(string ByValue, bool IsByMapping, bool IsBySorting);
|
||||
|
||||
internal record RecordC(string? DebugDirectory, string? Directory, long? Ticks, string? PersonDirectory);
|
||||
|
||||
internal static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName)
|
||||
{
|
||||
RecordB result;
|
||||
string byValue;
|
||||
bool isByMapping;
|
||||
bool isBySorting;
|
||||
if (by is null)
|
||||
{
|
||||
isByMapping = false;
|
||||
isBySorting = !sortingContainersAny;
|
||||
byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Mapping)}Null";
|
||||
}
|
||||
else
|
||||
{
|
||||
isByMapping = by == Shared.Models.Stateless.IMapLogic.Mapping;
|
||||
isBySorting = by == Shared.Models.Stateless.IMapLogic.Sorting;
|
||||
bool isDefaultName = displayDirectoryName is not null && IPerson.IsDefaultName(displayDirectoryName);
|
||||
if (isBySorting && displayDirectoryName is null)
|
||||
byValue = saveIndividually ? nameof(Shared.Models.Stateless.IMapLogic.Individually) : $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)} Without Person{(distancePermyriad < 2000 ? "-A" : "-Z")}";
|
||||
else if (isBySorting && useFiltersCounter.HasValue)
|
||||
byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}";
|
||||
else
|
||||
{
|
||||
byValue = $"{by.Value switch
|
||||
{
|
||||
Shared.Models.Stateless.IMapLogic.Mapping => nameof(Shared.Models.Stateless.IMapLogic.Mapping),
|
||||
Shared.Models.Stateless.IMapLogic.Sorting => saveIndividually ? nameof(Shared.Models.Stateless.IMapLogic.Individually) : nameof(Shared.Models.Stateless.IMapLogic.Sorting),
|
||||
Shared.Models.Stateless.IMapLogic.ForceSingleImage => forceSingleImageHumanized,
|
||||
_ => throw new NotImplementedException()
|
||||
}}{(!isDefaultName ? "-A" : "-Z")}";
|
||||
}
|
||||
}
|
||||
result = new(byValue, isByMapping, isBySorting);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) =>
|
||||
Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, personKeyFormattedAndKeyTicksAndDisplayDirectoryName is null ? null : Shared.Models.Stateless.IMapLogic.Mapping, personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName);
|
||||
|
||||
private static RecordC Get(string eDistanceContentTicksDirectory, RecordB recordB, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, string segmentB)
|
||||
{
|
||||
RecordC result;
|
||||
if (string.IsNullOrEmpty(personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName))
|
||||
throw new NotImplementedException();
|
||||
long? ticks = null;
|
||||
string? debugDirectory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName);
|
||||
string? directory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, segmentB);
|
||||
string? personDirectory = Path.Combine(directory, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName, "lnk");
|
||||
result = new(debugDirectory, directory, ticks, personDirectory);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static string GetResizeContentDirectory(ResultSettings resultSettings, string cContentDirectory, FilePath filePath)
|
||||
{
|
||||
string result;
|
||||
(string directoryName, _) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
|
||||
result = Path.Combine(cContentDirectory, directoryName);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static string GetFacePartsDirectoryX(ResultSettings resultSettings, string d2FacePartsContentDirectory, FilePath filePath)
|
||||
{
|
||||
string result;
|
||||
(string directoryName, _) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
|
||||
result = Path.Combine(d2FacePartsContentDirectory, directoryName, filePath.NameWithoutExtension);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne)
|
||||
{
|
||||
List<SaveContainer> results = [];
|
||||
RecordB recordB;
|
||||
RecordC recordC;
|
||||
string segmentB;
|
||||
string checkFile;
|
||||
string? directory;
|
||||
string shortcutFile;
|
||||
string facesDirectory;
|
||||
bool isCounterPersonYear;
|
||||
string facePartsDirectory;
|
||||
FileHolder? faceFileHolder;
|
||||
SaveContainer? saveContainer;
|
||||
FileHolder? resizedFileHolder;
|
||||
int? useFiltersCounter = null;
|
||||
string resizeContentDirectory;
|
||||
FileHolder? facePartsFileHolder;
|
||||
FileHolder? hiddenFaceFileHolder;
|
||||
LocationContainer locationContainer;
|
||||
bool sortingContainersAny = onlyOne.Count > 0;
|
||||
string eResultsFullGroupDirectory = IResult.GetResultsDateGroupDirectory(resultSettings,
|
||||
nameof(E_Distance),
|
||||
resultSettings.ResultContent);
|
||||
string cResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
|
||||
nameof(C_Resize),
|
||||
outputResolution,
|
||||
includeResizeGroup: true,
|
||||
includeModel: false,
|
||||
includePredictorModel: false);
|
||||
string d2ResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
|
||||
nameof(D2_FaceParts),
|
||||
outputResolution,
|
||||
includeResizeGroup: true,
|
||||
includeModel: true,
|
||||
includePredictorModel: true);
|
||||
string cContentDirectory = Path.Combine(cResultsFullGroupDirectory, resultSettings.ResultContent);
|
||||
if (!Directory.Exists(cContentDirectory))
|
||||
_ = Directory.CreateDirectory(cContentDirectory);
|
||||
string eDistanceContentTicksDirectory = Path.Combine(eResultsFullGroupDirectory, ticks.ToString());
|
||||
if (!Directory.Exists(eDistanceContentTicksDirectory))
|
||||
_ = Directory.CreateDirectory(eDistanceContentTicksDirectory);
|
||||
string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, resultSettings.ResultContent);
|
||||
if (!Directory.Exists(d2FacePartsContentDirectory))
|
||||
_ = Directory.CreateDirectory(d2FacePartsContentDirectory);
|
||||
string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
|
||||
string message = $") Building Save Container Collection - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
|
||||
compare.ConstructProgressBar(onlyOne.Count, message);
|
||||
foreach (KeyValuePair<string, LocationContainer> keyValuePair in onlyOne)
|
||||
{
|
||||
if (distanceSettings.SaveIndividually)
|
||||
break;
|
||||
locationContainer = keyValuePair.Value;
|
||||
if (locationContainer.LengthPermyriad is null || locationContainer.LengthSource is null)
|
||||
continue;
|
||||
segmentB = locationContainer.LengthPermyriad.Value.ToString().PadLeft(2, '0')[..2];
|
||||
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
|
||||
continue;
|
||||
isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyTicks);
|
||||
recordB = Get(useFiltersCounter, distanceSettings.SaveIndividually, sortingContainersAny, forceSingleImageHumanized, locationContainer.LengthPermyriad, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName);
|
||||
recordC = Get(eDistanceContentTicksDirectory, recordB, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, segmentB);
|
||||
if (string.IsNullOrEmpty(recordC.Directory) || string.IsNullOrEmpty(recordC.PersonDirectory))
|
||||
continue;
|
||||
directory = recordC.Directory;
|
||||
if (!string.IsNullOrEmpty(recordC.DebugDirectory))
|
||||
results.Add(SaveContainer.Get(recordC.DebugDirectory));
|
||||
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
|
||||
{
|
||||
if (!distanceSettings.SaveSortingWithoutPerson)
|
||||
throw new NotSupportedException();
|
||||
if (recordC.Ticks is null)
|
||||
continue;
|
||||
}
|
||||
results.Add(SaveContainer.Get(recordC.PersonDirectory));
|
||||
facesDirectory = locationContainer.LengthSource.DirectoryFullPath;
|
||||
faceFileHolder = FileHolder.Get(locationContainer.LengthSource.FullName);
|
||||
checkFile = Path.Combine(directory, $"{locationContainer.LengthSource.Name}");
|
||||
shortcutFile = Path.Combine(recordC.PersonDirectory, $"{locationContainer.LengthSource.Name}.lnk");
|
||||
resizeContentDirectory = GetResizeContentDirectory(resultSettings, cContentDirectory, locationContainer.LengthSource);
|
||||
facePartsDirectory = GetFacePartsDirectoryX(resultSettings, d2FacePartsContentDirectory, locationContainer.LengthSource);
|
||||
hiddenFaceFileHolder = FileHolder.Get(Path.Combine(facesDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesHiddenFileNameExtension}"));
|
||||
facePartsFileHolder = FileHolder.Get(Path.Combine(facePartsDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesPartsFileNameExtension}"));
|
||||
resizedFileHolder = FileHolder.Get(Path.Combine(resizeContentDirectory, $"{locationContainer.LengthSource.FileNameFirstSegment}{Path.GetExtension(locationContainer.LengthSource.NameWithoutExtension)}"));
|
||||
saveContainer = SaveContainer.Get(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, resizedFileHolder, shortcutFile);
|
||||
results.Add(saveContainer);
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
internal static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers)
|
||||
{
|
||||
string fileName;
|
||||
string checkFile;
|
||||
string sourceFile;
|
||||
List<string> distinct = [];
|
||||
WindowsShortcut windowsShortcut;
|
||||
string[] directories = (from l in saveContainers select l.Directory).Distinct().ToArray();
|
||||
foreach (string directory in directories)
|
||||
{
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
continue;
|
||||
if (!Directory.Exists(directory))
|
||||
_ = Directory.CreateDirectory(directory);
|
||||
}
|
||||
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
||||
string message;
|
||||
if (updated is null)
|
||||
message = $") {saveContainers.Count:000} save(s) - {totalSeconds} total second(s)";
|
||||
else
|
||||
message = $") {saveContainers.Count:000} save(s) - {updated} Updated - {totalSeconds} total second(s)";
|
||||
compare.ConstructProgressBar(saveContainers.Count, message);
|
||||
foreach (SaveContainer saveContainer in saveContainers)
|
||||
{
|
||||
compare.Tick();
|
||||
if (string.IsNullOrEmpty(saveContainer.Directory) || string.IsNullOrEmpty(saveContainer.CheckFile) || saveContainer.FaceFileHolder is null)
|
||||
continue;
|
||||
if (saveContainer.FacePartsFileHolder is null && saveContainer.HiddenFaceFileHolder is null && saveContainer.ResizedFileHolder is null)
|
||||
{
|
||||
checkFile = saveContainer.CheckFile;
|
||||
sourceFile = saveContainer.FaceFileHolder.FullName;
|
||||
}
|
||||
else if (!saveContainer.FaceFileHolder.Exists && saveContainer.ResizedFileHolder is not null && saveContainer.ResizedFileHolder.Exists)
|
||||
{
|
||||
checkFile = saveContainer.CheckFile;
|
||||
sourceFile = saveContainer.ResizedFileHolder.FullName;
|
||||
}
|
||||
else if (saveContainer.FaceFileHolder.Exists)
|
||||
{
|
||||
sourceFile = saveContainer.FaceFileHolder.FullName;
|
||||
checkFile = $"{saveContainer.CheckFile}{compareSettings.FacesFileNameExtension}";
|
||||
}
|
||||
else
|
||||
continue;
|
||||
if (distanceSettings.SaveIndividually)
|
||||
{
|
||||
fileName = Path.GetFileName(checkFile);
|
||||
if (distinct.Contains(fileName))
|
||||
continue;
|
||||
distinct.Add(fileName);
|
||||
}
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Copy(sourceFile, checkFile);
|
||||
if (distanceSettings.SaveIndividually)
|
||||
continue;
|
||||
if (saveContainer.MakeAllHidden)
|
||||
File.SetAttributes(checkFile, FileAttributes.Hidden);
|
||||
if (saveContainer.HiddenFaceFileHolder is not null && saveContainer.HiddenFaceFileHolder.Exists)
|
||||
{
|
||||
sourceFile = saveContainer.HiddenFaceFileHolder.FullName;
|
||||
checkFile = $"{saveContainer.CheckFile}{compareSettings.FacesHiddenFileNameExtension}";
|
||||
}
|
||||
else if (saveContainer.FacePartsFileHolder is not null && saveContainer.FacePartsFileHolder.Exists)
|
||||
{
|
||||
sourceFile = saveContainer.FacePartsFileHolder.FullName;
|
||||
checkFile = $"{saveContainer.CheckFile}{compareSettings.FacesPartsFileNameExtension}";
|
||||
}
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Copy(sourceFile, checkFile);
|
||||
if (saveContainer.MakeAllHidden)
|
||||
File.SetAttributes(checkFile, FileAttributes.Hidden);
|
||||
}
|
||||
if (updated is null)
|
||||
{
|
||||
foreach (SaveContainer saveContainer in saveContainers)
|
||||
{
|
||||
if (string.IsNullOrEmpty(saveContainer.Directory) || string.IsNullOrEmpty(saveContainer.CheckFile) || saveContainer.ResizedFileHolder is null || !saveContainer.ResizedFileHolder.Exists)
|
||||
continue;
|
||||
checkFile = saveContainer.CheckFile;
|
||||
sourceFile = saveContainer.ResizedFileHolder.FullName;
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Copy(sourceFile, checkFile);
|
||||
if (saveContainer.MakeAllHidden)
|
||||
File.SetAttributes(checkFile, FileAttributes.Hidden);
|
||||
}
|
||||
}
|
||||
foreach (SaveContainer saveContainer in saveContainers)
|
||||
{
|
||||
if (string.IsNullOrEmpty(saveContainer.Directory) || string.IsNullOrEmpty(saveContainer.ShortcutFile) || saveContainer.ResizedFileHolder is null || !saveContainer.ResizedFileHolder.Exists)
|
||||
continue;
|
||||
try
|
||||
{
|
||||
string description;
|
||||
if (saveContainer.FaceFileHolder is not null && saveContainer.FaceFileHolder.Name.StartsWith(saveContainer.ResizedFileHolder.Name))
|
||||
description = saveContainer.FaceFileHolder.Name;
|
||||
else
|
||||
description = saveContainer.ResizedFileHolder.Name;
|
||||
windowsShortcut = new() { Path = saveContainer.ResizedFileHolder.FullName, Description = description };
|
||||
windowsShortcut.Save(saveContainer.ShortcutFile);
|
||||
windowsShortcut.Dispose();
|
||||
if (saveContainer.MakeAllHidden)
|
||||
File.SetAttributes(saveContainer.ShortcutFile, FileAttributes.Hidden);
|
||||
}
|
||||
catch (Exception)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
193
Distance/Models/Stateless/MappedLogicA.cs
Normal file
193
Distance/Models/Stateless/MappedLogicA.cs
Normal file
@ -0,0 +1,193 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Metadata.Models;
|
||||
using View_by_Distance.Metadata.Models.Stateless.Methods;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Distance.Models.Stateless.Methods;
|
||||
|
||||
internal static class MappedLogicA
|
||||
{
|
||||
|
||||
internal record MappedFile(PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
|
||||
string? PersonDisplayDirectoryName,
|
||||
FilePath FilePath);
|
||||
|
||||
private static List<MappedFile> GetDisplayDirectoryAllFiles(PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections)
|
||||
{
|
||||
List<MappedFile> results = [];
|
||||
MappedFile mappedFile;
|
||||
string personKeyFormatted;
|
||||
List<string> distinct = [];
|
||||
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
|
||||
foreach (PersonContainer personContainer in readOnlyCollections.PersonContainers)
|
||||
{
|
||||
if (personContainer.Key is null)
|
||||
continue;
|
||||
for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--)
|
||||
{
|
||||
if (personContainer.DisplayDirectoryAllFilePaths[i].ExtensionLowered != compareSettings.FacesFileNameExtension)
|
||||
continue;
|
||||
if (distinct.Contains(personContainer.DisplayDirectoryAllFilePaths[i].Name))
|
||||
continue;
|
||||
distinct.Add(personContainer.DisplayDirectoryAllFilePaths[i].Name);
|
||||
personKeyFormatted = IPersonBirthday.GetFormatted(peopleSettings.PersonBirthdayFormat, personContainer.Key.Value);
|
||||
personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(personKeyFormatted, personContainer.Key.Value, personContainer.DisplayDirectoryAllFilePaths[i].Name);
|
||||
mappedFile = new(personKeyFormattedAndKeyTicksAndDisplayDirectoryName, personContainer.DisplayDirectoryName, personContainer.DisplayDirectoryAllFilePaths[i]);
|
||||
results.Add(mappedFile);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<MappedFile> GetMappedFiles(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections, ReadOnlyCollection<MappedLogicB.Record> records)
|
||||
{
|
||||
List<MappedFile> results = [];
|
||||
long personKey;
|
||||
string checkFile;
|
||||
FilePath filePath;
|
||||
FileHolder fileHolder;
|
||||
MappedFile mappedFile;
|
||||
List<string> distinct = [];
|
||||
PersonBirthday? personBirthday;
|
||||
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
|
||||
results.AddRange(GetDisplayDirectoryAllFiles(peopleSettings, compareSettings, readOnlyCollections));
|
||||
foreach (MappedLogicB.Record record in records)
|
||||
{
|
||||
personBirthday = IPersonBirthday.GetPersonBirthday(peopleSettings.PersonBirthdayFormat, record.PersonKeyFormatted);
|
||||
if (personBirthday is null)
|
||||
continue;
|
||||
if (distinct.Contains(record.MappedFaceFilePath.Name))
|
||||
continue;
|
||||
distinct.Add(record.MappedFaceFilePath.Name);
|
||||
personKey = personBirthday.Value.Ticks;
|
||||
personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(record.PersonKeyFormatted, personKey, record.PersonDisplayDirectoryName);
|
||||
mappedFile = new(personKeyFormattedAndKeyTicksAndDisplayDirectoryName, record.PersonDisplayDirectoryName, record.MappedFaceFilePath);
|
||||
results.Add(mappedFile);
|
||||
}
|
||||
for (int i = results.Count - 1; i > -1; i--)
|
||||
{
|
||||
filePath = results[i].FilePath;
|
||||
if (filePath.Name.EndsWith(".old"))
|
||||
{
|
||||
results.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
if (!filePath.Name.EndsWith(".abd") && !filePath.Name.EndsWith(".brt") && !filePath.Name.EndsWith(".dup") && !filePath.Name.EndsWith(".unk"))
|
||||
continue;
|
||||
checkFile = filePath.FullName[..^4];
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Move(filePath.FullName, checkFile);
|
||||
fileHolder = FileHolder.Get(checkFile);
|
||||
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
results[i] = new(results[i].PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, results[i].PersonDisplayDirectoryName, filePath);
|
||||
}
|
||||
for (int i = results.Count - 1; i > -1; i--)
|
||||
{
|
||||
if (File.Exists(results[i].FilePath.FullName))
|
||||
continue;
|
||||
results.RemoveAt(i);
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static void MappedParallelFor(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, List<ExifDirectory> exifDirectories, ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipCollection, MappedFile mappedFile)
|
||||
{
|
||||
int? id;
|
||||
int? wholePercentages;
|
||||
const string lnk = ".lnk";
|
||||
if (!mappedFile.FilePath.Name.EndsWith(lnk))
|
||||
{
|
||||
if (mappedFile.FilePath.Id is null)
|
||||
return;
|
||||
id = mappedFile.FilePath.Id;
|
||||
wholePercentages = IMapping.GetWholePercentages(compareSettings, mappedFile.FilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileHolder fileHolder = FileHolder.Get(mappedFile.FilePath.FullName[..^4]);
|
||||
FilePath filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
if (filePath.Id is null)
|
||||
return;
|
||||
id = filePath.Id;
|
||||
wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath);
|
||||
}
|
||||
if (wholePercentages is null)
|
||||
return;
|
||||
if (distanceSettings.LinkedAlpha is null && string.IsNullOrEmpty(distanceSettings.LocationContainerDebugDirectory))
|
||||
{
|
||||
if (skipCollection.TryGetValue(id.Value, out List<FilePathAndWholePercentages>? wholePercentagesCollection))
|
||||
{
|
||||
string checkFile;
|
||||
FilePath[] filePathMatches = (from l in wholePercentagesCollection where l.WholePercentages == wholePercentages select l.FilePath).ToArray();
|
||||
foreach (FilePath filePathMatch in filePathMatches)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePathMatch.FullName) || !File.Exists(filePathMatch.FullName))
|
||||
continue;
|
||||
checkFile = $"{filePathMatch}.dup";
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Move(filePathMatch.FullName, checkFile);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mappedFile.FilePath.Name.EndsWith(lnk) || !File.Exists(mappedFile.FilePath.FullName))
|
||||
return;
|
||||
ExifDirectory exifDirectory = IMetadata.GetExifDirectory(mappedFile.FilePath, mappedFile.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName);
|
||||
lock (exifDirectories)
|
||||
exifDirectories.Add(exifDirectory);
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections)
|
||||
{
|
||||
List<ExifDirectory> results = [];
|
||||
string eDistanceContentDirectory = IResult.GetResultsDateGroupDirectory(resultSettings, nameof(E_Distance), resultSettings.ResultContent);
|
||||
ReadOnlyCollection<MappedLogicB.Record> records = MappedLogicB.DeleteEmptyDirectoriesAndGetCollection(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, eDistanceContentDirectory, readOnlyCollections);
|
||||
ReadOnlyCollection<MappedFile> mappedFiles = GetMappedFiles(resultSettings, metadataSettings, peopleSettings, compareSettings, readOnlyCollections, records);
|
||||
if (mappedFiles.Count > 0)
|
||||
{
|
||||
int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
|
||||
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
||||
string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)";
|
||||
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
|
||||
ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipNotSkipCollection = readOnlyCollections.SkipNotSkipCollection;
|
||||
compare.ConstructProgressBar(mappedFiles.Count, message);
|
||||
_ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
|
||||
{
|
||||
compare.Tick();
|
||||
MappedParallelFor(resultSettings, metadataSettings, distanceSettings, compareSettings, results, skipNotSkipCollection, mappedFiles[i]);
|
||||
});
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories)
|
||||
{
|
||||
Dictionary<int, ReadOnlyDictionary<int, FilePath>> results = [];
|
||||
int? wholePercentages;
|
||||
Dictionary<int, FilePath>? keyValues;
|
||||
Dictionary<int, Dictionary<int, FilePath>> keyValuePairs = [];
|
||||
foreach (ExifDirectory exifDirectory in exifDirectories)
|
||||
{
|
||||
if (exifDirectory.FilePath.Id is null)
|
||||
continue;
|
||||
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
|
||||
{
|
||||
keyValuePairs.Add(exifDirectory.FilePath.Id.Value, []);
|
||||
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
|
||||
throw new Exception();
|
||||
}
|
||||
wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath);
|
||||
if (wholePercentages is null)
|
||||
continue;
|
||||
keyValues.Add(wholePercentages.Value, exifDirectory.FilePath);
|
||||
}
|
||||
foreach (KeyValuePair<int, Dictionary<int, FilePath>> keyValuePair in keyValuePairs)
|
||||
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
}
|
500
Distance/Models/Stateless/MappedLogicB.cs
Normal file
500
Distance/Models/Stateless/MappedLogicB.cs
Normal file
@ -0,0 +1,500 @@
|
||||
using Humanizer;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Distance.Models.Stateless.Methods;
|
||||
|
||||
internal static class MappedLogicB
|
||||
{
|
||||
|
||||
internal record Record(int DirectoryNumber,
|
||||
bool? IsDefault,
|
||||
int? LinksCount,
|
||||
FilePath MappedFaceFilePath,
|
||||
string? PersonDisplayDirectoryName,
|
||||
string PersonKeyFormatted);
|
||||
|
||||
internal record TicksDirectory(DateTime AlternateDirectoryDateTime,
|
||||
string Directory,
|
||||
DateTime DirectoryDateTime,
|
||||
string DirectoryName,
|
||||
bool? IsLocationContainerDebugDirectory,
|
||||
float? TotalDays);
|
||||
|
||||
private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames)
|
||||
{
|
||||
string checkFile;
|
||||
string actionDirectoryName = Path.GetFileName(actionDirectory);
|
||||
string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName);
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
_ = Directory.CreateDirectory(checkDirectory);
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (facesFileNames.Contains(file))
|
||||
{
|
||||
checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Move(file, checkFile);
|
||||
continue;
|
||||
}
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
|
||||
{
|
||||
string[] files;
|
||||
string checkFile;
|
||||
string? checkDirectory;
|
||||
string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string directory in directories)
|
||||
{
|
||||
checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory));
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
Directory.Move(directory, checkDirectory);
|
||||
else
|
||||
{
|
||||
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted))
|
||||
continue;
|
||||
checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted);
|
||||
checkDirectory = Path.GetDirectoryName(checkFile);
|
||||
if (checkDirectory is null)
|
||||
continue;
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
_ = Directory.CreateDirectory(checkDirectory);
|
||||
File.Move(file, checkFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = IPath.DeleteEmptyDirectories(personKeyDirectory);
|
||||
}
|
||||
|
||||
private static List<TicksDirectory> UpdateDateVerifyAndGetTicksDirectories(DistanceSettings distanceSettings, string eDistanceContentDirectory)
|
||||
{
|
||||
List<TicksDirectory> results = [];
|
||||
float? totalDays;
|
||||
long? next = null;
|
||||
string? checkDirectory;
|
||||
string ticksDirectoryName;
|
||||
DateTime directoryDateTime;
|
||||
DirectoryInfo directoryInfo;
|
||||
TicksDirectory ticksDirectory;
|
||||
long? lastDirectoryTicks = null;
|
||||
DateTime dateTime = DateTime.Now;
|
||||
DateTime alternateDirectoryDateTime;
|
||||
bool? isLocationContainerDebugDirectory;
|
||||
long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks;
|
||||
for (int i = 1; i < 5; i++)
|
||||
_ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory);
|
||||
if (!Directory.Exists(eDistanceContentDirectory))
|
||||
_ = Directory.CreateDirectory(eDistanceContentDirectory);
|
||||
string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string ticksFullPath in ticksFullPaths)
|
||||
{
|
||||
ticksDirectoryName = Path.GetFileName(ticksFullPath);
|
||||
if (ticksDirectoryName.Length < 3)
|
||||
continue;
|
||||
if (!long.TryParse(ticksDirectoryName, out long directoryTicks))
|
||||
throw new NotSupportedException();
|
||||
if (next is null)
|
||||
next = new DateTime(directoryTicks).Ticks;
|
||||
else
|
||||
{
|
||||
next += month;
|
||||
checkDirectory = Path.GetDirectoryName(ticksFullPath);
|
||||
if (string.IsNullOrEmpty(checkDirectory))
|
||||
{
|
||||
if (string.IsNullOrEmpty(checkDirectory))
|
||||
continue;
|
||||
checkDirectory = Path.Combine(checkDirectory, next.Value.ToString());
|
||||
if (ticksFullPath == checkDirectory || !checkDirectory.EndsWith(distanceSettings.LocationContainerDirectoryPattern))
|
||||
continue;
|
||||
Directory.Move(ticksFullPath, checkDirectory);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
directoryInfo = new(ticksFullPath);
|
||||
directoryDateTime = new DateTime(directoryTicks);
|
||||
if (directoryInfo.CreationTime.Ticks != directoryTicks)
|
||||
Directory.SetCreationTime(ticksFullPath, new DateTime(directoryTicks));
|
||||
if (directoryInfo.LastWriteTime.Ticks != directoryTicks)
|
||||
Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks));
|
||||
alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1);
|
||||
isLocationContainerDebugDirectory = distanceSettings.LocationContainerDebugDirectory is null ? null : ticksDirectoryName.EndsWith(distanceSettings.LocationContainerDebugDirectory);
|
||||
totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays;
|
||||
ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), ticksDirectoryName, isLocationContainerDebugDirectory, totalDays);
|
||||
results.Add(ticksDirectory);
|
||||
if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0)
|
||||
continue;
|
||||
lastDirectoryTicks = directoryTicks;
|
||||
}
|
||||
string[] compare = (from l in results where l.TotalDays is not null and < 9.95f select l.Directory).ToArray();
|
||||
if (compare.Length > 0 && distanceSettings.ReMap)
|
||||
throw new Exception($"Please Consolidate <{string.Join(Environment.NewLine, compare)}>");
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void Individually(ICompareSettings compareSettings, TicksDirectory ticksDirectory, string directory)
|
||||
{
|
||||
bool isDefault;
|
||||
string[] files;
|
||||
FileInfo[] collection;
|
||||
string[] facesFileNames;
|
||||
string yearDirectoryName;
|
||||
string[] yearDirectories;
|
||||
string alphaDirectoryName;
|
||||
string matchDirectoryName;
|
||||
string personKeyFormatted;
|
||||
string[] alphaDirectories;
|
||||
string[] matchDirectories;
|
||||
string[] actionDirectories;
|
||||
string personDisplayDirectory;
|
||||
string[] personKeyDirectories;
|
||||
string[] segmentCDirectories = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string segmentCDirectory in segmentCDirectories)
|
||||
{
|
||||
personKeyDirectories = Directory.GetDirectories(segmentCDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string personKeyDirectory in personKeyDirectories)
|
||||
{
|
||||
personKeyFormatted = Path.GetFileName(personKeyDirectory);
|
||||
yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string yearDirectory in yearDirectories)
|
||||
{
|
||||
yearDirectoryName = Path.GetFileName(yearDirectory);
|
||||
if (yearDirectoryName.StartsWith('='))
|
||||
Directory.Move(yearDirectory, yearDirectory.Replace('=', '~'));
|
||||
}
|
||||
yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string yearDirectory in yearDirectories)
|
||||
{
|
||||
yearDirectoryName = Path.GetFileName(yearDirectory);
|
||||
matchDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
alphaDirectories = matchDirectories.Where(l => !long.TryParse(Path.GetFileName(l), out long a)).ToArray();
|
||||
if (alphaDirectories.Length == 0)
|
||||
continue;
|
||||
alphaDirectoryName = Path.GetFileName(alphaDirectories[0]);
|
||||
foreach (string matchDirectory in matchDirectories)
|
||||
{
|
||||
matchDirectoryName = Path.GetFileName(matchDirectory);
|
||||
files = Directory.GetFiles(matchDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
if (files.Length != 4)
|
||||
continue;
|
||||
collection = files.Select(l => new FileInfo(l)).ToArray();
|
||||
isDefault = IPerson.IsDefaultName(alphaDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]);
|
||||
if (isDefault)
|
||||
facesFileNames = (from l in collection where l.Extension == compareSettings.FacesFileNameExtension select l.FullName).ToArray();
|
||||
else
|
||||
facesFileNames = (from l in collection where l.Extension == compareSettings.FacesFileNameExtension && l.Name.Contains(matchDirectoryName) select l.FullName).ToArray();
|
||||
if (facesFileNames.Length == 0)
|
||||
continue;
|
||||
personDisplayDirectory = Path.Combine(matchDirectory, alphaDirectoryName);
|
||||
if (!Directory.Exists(personDisplayDirectory) || !Directory.Exists(matchDirectory))
|
||||
continue;
|
||||
_ = Process.Start("explorer", matchDirectory);
|
||||
for (int i = 0; i < int.MaxValue; i++)
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
actionDirectories = Directory.GetDirectories(matchDirectory, "*", SearchOption.TopDirectoryOnly).Where(l => l != personDisplayDirectory && !l.EndsWith("Maybe")).ToArray();
|
||||
if (actionDirectories.Length > 0)
|
||||
{
|
||||
MoveTo(actionDirectories[0], ticksDirectory, directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName, files, facesFileNames);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Record> GetRecords(ResultSettings resultSettings, MetadataSettings metadataSettings, ICompareSettings compareSettings, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List<string> distinct, string? personDisplayDirectoryName)
|
||||
{
|
||||
List<Record> results = [];
|
||||
Record record;
|
||||
string fileName;
|
||||
string checkFile;
|
||||
FilePath filePath;
|
||||
FileHolder fileHolder;
|
||||
int? wholePercentages;
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (file.EndsWith(".lnk"))
|
||||
continue;
|
||||
fileHolder = FileHolder.Get(file);
|
||||
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
if (filePath.Id is null)
|
||||
continue;
|
||||
wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath);
|
||||
if (wholePercentages is null)
|
||||
continue;
|
||||
fileName = Path.GetFileName(file);
|
||||
if (distinct.Contains(fileName))
|
||||
{
|
||||
checkFile = $"{file}.dup";
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Move(file, checkFile);
|
||||
continue;
|
||||
}
|
||||
distinct.Add(fileName);
|
||||
record = new(directoryNumber, isDefault, linksCount, filePath, personDisplayDirectoryName, personKeyFormatted);
|
||||
results.Add(record);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static string[] RenameBirth(string[] files)
|
||||
{
|
||||
List<string> results = [];
|
||||
string checkFile;
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (file.EndsWith(".brt"))
|
||||
{
|
||||
results.Add(file);
|
||||
continue;
|
||||
}
|
||||
checkFile = $"{file}.brt";
|
||||
if (File.Exists(checkFile))
|
||||
{
|
||||
results.Add(file);
|
||||
continue;
|
||||
}
|
||||
File.Move(file, checkFile);
|
||||
results.Add(checkFile);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
private static void MovedToNewestPersonKeyFormatted(string personKeyFormatted, string newestPersonKeyFormatted, TicksDirectory ticksDirectory, string personKeyDirectory)
|
||||
{
|
||||
string newestPersonKeyDirectory = Path.Combine(ticksDirectory.Directory, newestPersonKeyFormatted);
|
||||
if (Directory.Exists(newestPersonKeyDirectory))
|
||||
MoveFiles(personKeyFormatted, personKeyDirectory, newestPersonKeyFormatted, newestPersonKeyDirectory);
|
||||
else
|
||||
Directory.Move(personKeyDirectory, newestPersonKeyDirectory);
|
||||
}
|
||||
|
||||
private static int? GetLinksCount(string yearDirectory)
|
||||
{
|
||||
int? result;
|
||||
string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-');
|
||||
if (yearDirectoryNameSegments.Length != 3)
|
||||
result = null;
|
||||
else
|
||||
{
|
||||
string lastSegment = yearDirectoryNameSegments[^1];
|
||||
if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0]))
|
||||
result = null;
|
||||
else
|
||||
result = lastSegment[0] - 65;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<Record> DeleteEmptyDirectoriesAndGetCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string eDistanceContentDirectory, ReadOnlyCollections readOnlyCollections)
|
||||
{
|
||||
List<Record> results = [];
|
||||
bool check;
|
||||
string message;
|
||||
string[] files;
|
||||
bool? isDefault;
|
||||
int? linksCount;
|
||||
int totalSeconds;
|
||||
DateTime dateTime;
|
||||
TimeSpan timeSpan;
|
||||
int directoryNumber;
|
||||
string? checkDirectory;
|
||||
string[] yearDirectories;
|
||||
string personKeyFormatted;
|
||||
List<string> distinct = [];
|
||||
string? personFirstInitial;
|
||||
bool isReservedDirectoryName;
|
||||
string[] personNameDirectories;
|
||||
string? newestPersonKeyFormatted;
|
||||
string? personDisplayDirectoryName;
|
||||
string[] personNameLinkDirectories;
|
||||
string? personFirstInitialDirectory;
|
||||
List<TicksDirectory> ticksDirectories;
|
||||
string[] personKeyFormattedDirectories;
|
||||
string manualCopyHumanized = nameof(Shared.Models.Stateless.IMapLogic.ManualCopy).Humanize(LetterCasing.Title);
|
||||
string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
|
||||
for (int i = 1; i < 6; i++)
|
||||
{
|
||||
check = false;
|
||||
results.Clear();
|
||||
distinct.Clear();
|
||||
directoryNumber = 0;
|
||||
ticksDirectories = UpdateDateVerifyAndGetTicksDirectories(distanceSettings, eDistanceContentDirectory);
|
||||
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
|
||||
message = $"{i}) {ticksDirectories.Count:000} compile from and clean ticks Director(ies) - B - {totalSeconds} total second(s)";
|
||||
compare.ConstructProgressBar(ticksDirectories.Count, message);
|
||||
foreach (TicksDirectory ticksDirectory in ticksDirectories)
|
||||
{
|
||||
if (i == 1)
|
||||
compare.Tick();
|
||||
personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories)
|
||||
{
|
||||
personKeyFormatted = Path.GetFileName(personKeyFormattedDirectory);
|
||||
isReservedDirectoryName = personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Sorting)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Mapping)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.ManualCopy));
|
||||
if (!isReservedDirectoryName && personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Individually)))
|
||||
{
|
||||
Individually(compareSettings, ticksDirectory, personKeyFormattedDirectory);
|
||||
throw new Exception($"B) Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!");
|
||||
}
|
||||
_ = readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.TryGetValue(personKeyFormatted, out newestPersonKeyFormatted);
|
||||
if (readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
|
||||
{
|
||||
timeSpan = new TimeSpan(DateTime.Now.Ticks - ticksDirectory.DirectoryDateTime.Ticks);
|
||||
if (timeSpan.TotalDays > 6)
|
||||
throw new Exception($"{distanceSettings.MappingDefaultName} <{ticksDirectory.DirectoryDateTime}> are only allowed within x days!");
|
||||
}
|
||||
yearDirectories = Directory.GetDirectories(personKeyFormattedDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string yearDirectory in yearDirectories)
|
||||
{
|
||||
if (check && !Directory.Exists(yearDirectory))
|
||||
continue;
|
||||
if (ticksDirectory.IsLocationContainerDebugDirectory is null || !ticksDirectory.IsLocationContainerDebugDirectory.Value)
|
||||
linksCount = null;
|
||||
else
|
||||
linksCount = GetLinksCount(yearDirectory);
|
||||
if (ticksDirectory.DirectoryName != distanceSettings.LocationContainerDebugDirectory)
|
||||
{
|
||||
files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string file in files)
|
||||
File.Delete(file);
|
||||
}
|
||||
if (ticksDirectory.DirectoryName == distanceSettings.LocationContainerDebugDirectory)
|
||||
{
|
||||
isDefault = null;
|
||||
personDisplayDirectoryName = null;
|
||||
files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
results.AddRange(GetRecords(resultSettings, metadataSettings, compareSettings, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName));
|
||||
files = Directory.GetFiles(yearDirectory, "*.lnk", SearchOption.AllDirectories);
|
||||
foreach (string file in files)
|
||||
File.Delete(file);
|
||||
continue;
|
||||
}
|
||||
personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
if (personNameDirectories.Length > 1)
|
||||
throw new NotSupportedException("Try deleting *.lnk files!");
|
||||
foreach (string personNameDirectory in personNameDirectories)
|
||||
{
|
||||
directoryNumber++;
|
||||
personDisplayDirectoryName = Path.GetFileName(personNameDirectory);
|
||||
isDefault = IPerson.IsDefaultName(personDisplayDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]);
|
||||
if (isDefault.Value && personDisplayDirectoryName.Length == 1)
|
||||
{
|
||||
if (personKeyFormatted.Length != peopleSettings.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormatted, peopleSettings.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
|
||||
continue;
|
||||
checkDirectory = Path.Combine(yearDirectory, $"X+{dateTime.Ticks}");
|
||||
if (Directory.Exists(checkDirectory))
|
||||
{
|
||||
Directory.Delete(yearDirectory, recursive: true);
|
||||
continue;
|
||||
}
|
||||
Directory.Move(personNameDirectory, checkDirectory);
|
||||
if (!check)
|
||||
check = true;
|
||||
continue;
|
||||
}
|
||||
if (isDefault.Value && (ticksDirectory.DirectoryDateTime.Hour != 0 || ticksDirectory.DirectoryDateTime.Minute != 0 || ticksDirectory.DirectoryDateTime.Second != 0))
|
||||
{
|
||||
checkDirectory = Path.GetDirectoryName(ticksDirectory.Directory);
|
||||
if (checkDirectory is null)
|
||||
continue;
|
||||
checkDirectory = Path.Combine(checkDirectory, ticksDirectory.AlternateDirectoryDateTime.Ticks.ToString());
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
_ = Directory.CreateDirectory(checkDirectory);
|
||||
checkDirectory = Path.Combine(checkDirectory, personKeyFormatted);
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
{
|
||||
Directory.Move(personKeyFormattedDirectory, checkDirectory);
|
||||
if (!check)
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
if (isReservedDirectoryName && files.Length > 0)
|
||||
throw new Exception($"Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!");
|
||||
if (personKeyFormatted == manualCopyHumanized && files.Length > 0)
|
||||
throw new Exception($"Move personKey directories up one from {manualCopyHumanized} and delete {manualCopyHumanized} directory!");
|
||||
if (personKeyFormatted == forceSingleImageHumanized && files.Length > 0)
|
||||
throw new Exception($"Move personKey directories up one from {forceSingleImageHumanized} and delete {forceSingleImageHumanized} directory!");
|
||||
if (!isDefault.Value)
|
||||
{
|
||||
if (readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
|
||||
files = RenameBirth(files);
|
||||
else if (newestPersonKeyFormatted is not null && personKeyFormatted != newestPersonKeyFormatted)
|
||||
{
|
||||
if (!check)
|
||||
check = true;
|
||||
MovedToNewestPersonKeyFormatted(personKeyFormatted, newestPersonKeyFormatted, ticksDirectory, personKeyFormattedDirectory);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (personKeyFormatted.Length != peopleSettings.PersonBirthdayFormat.Length)
|
||||
continue;
|
||||
if (personDisplayDirectoryName.Length == 1 || isDefault.Value || !readOnlyCollections.PersonKeyFormattedCollection.Contains(personKeyFormatted))
|
||||
personFirstInitialDirectory = personNameDirectory;
|
||||
else
|
||||
{
|
||||
personFirstInitial = personDisplayDirectoryName[..1];
|
||||
if (personFirstInitial.All(char.IsDigit))
|
||||
{
|
||||
foreach (string file in files)
|
||||
File.Delete(file);
|
||||
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.AllDirectories);
|
||||
foreach (string file in files)
|
||||
File.Delete(file);
|
||||
_ = IPath.DeleteEmptyDirectories(personNameDirectory);
|
||||
continue;
|
||||
}
|
||||
personFirstInitialDirectory = Path.Combine(yearDirectory, personFirstInitial.ToString());
|
||||
if (Directory.Exists(personFirstInitialDirectory))
|
||||
throw new Exception("Forgot to ...");
|
||||
Directory.Move(personNameDirectory, personFirstInitialDirectory);
|
||||
files = Directory.GetFiles(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
results.AddRange(GetRecords(resultSettings, metadataSettings, compareSettings, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName));
|
||||
personNameLinkDirectories = Directory.GetDirectories(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string personNameLinkDirectory in personNameLinkDirectories)
|
||||
{
|
||||
files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (!file.EndsWith(".lnk"))
|
||||
continue;
|
||||
File.Delete(file);
|
||||
}
|
||||
_ = IPath.DeleteEmptyDirectories(personNameLinkDirectory);
|
||||
}
|
||||
_ = IPath.DeleteEmptyDirectories(personFirstInitialDirectory);
|
||||
}
|
||||
_ = IPath.DeleteEmptyDirectories(yearDirectory);
|
||||
}
|
||||
_ = IPath.DeleteEmptyDirectories(personKeyFormattedDirectory);
|
||||
}
|
||||
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
|
||||
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
|
||||
}
|
||||
if (check)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
}
|
57
Distance/Models/Stateless/Methods/IDistance.cs
Normal file
57
Distance/Models/Stateless/Methods/IDistance.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Metadata.Models;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Distance.Models.Stateless.Methods;
|
||||
|
||||
public interface IDistance
|
||||
{
|
||||
|
||||
static ReadOnlyCollection<ExifDirectory> TestStatic_GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) =>
|
||||
GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, readOnlyCollections);
|
||||
static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) =>
|
||||
MappedLogicA.GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, readOnlyCollections);
|
||||
|
||||
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> TestStatic_Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
|
||||
Extract(compareSettings, exifDirectories);
|
||||
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
|
||||
MappedLogicA.Extract(compareSettings, exifDirectories);
|
||||
|
||||
static ReadOnlyCollection<ExifDirectory> TestStatic_GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
|
||||
GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
|
||||
static ReadOnlyCollection<ExifDirectory> GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
|
||||
FaceEncodingLogic.GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
|
||||
|
||||
static ReadOnlyCollection<LocationContainer> TestStatic_GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
|
||||
GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
|
||||
static ReadOnlyCollection<LocationContainer> GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
|
||||
FilterLogicA.GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
|
||||
|
||||
static ReadOnlyCollection<LocationContainer> TestStatic_GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
|
||||
GetPostFilterLocationContainer(preFiltered, distanceLimits);
|
||||
static ReadOnlyCollection<LocationContainer> GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
|
||||
FilterLogicC.GetPostFilterLocationContainer(preFiltered, distanceLimits);
|
||||
|
||||
static ReadOnlyCollection<LocationContainer> TestStatic_GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
|
||||
GetMatrixLocationContainers(distanceSettings, compareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
|
||||
static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
|
||||
FilterLogicC.GetMatrixLocationContainers(distanceSettings, compareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
|
||||
|
||||
static ReadOnlyDictionary<string, LocationContainer> TestStatic_GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
|
||||
GetOnlyOne(distanceSettings, matrix);
|
||||
static ReadOnlyDictionary<string, LocationContainer> GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
|
||||
FilterLogicC.GetOnlyOne(distanceSettings, matrix);
|
||||
|
||||
static ReadOnlyCollection<SaveContainer> TestStatic_GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) =>
|
||||
GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, ticks, outputResolution, onlyOne);
|
||||
static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) =>
|
||||
FilterLogicD.GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, ticks, outputResolution, onlyOne);
|
||||
|
||||
static void TestStatic_SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
|
||||
SaveContainers(distanceSettings, compareSettings, compare, ticks, updated, saveContainers);
|
||||
static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
|
||||
FilterLogicD.SaveContainers(distanceSettings, compareSettings, compare, ticks, updated, saveContainers);
|
||||
|
||||
}
|
44
Face/AA.Face.csproj
Normal file
44
Face/AA.Face.csproj
Normal file
@ -0,0 +1,44 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>library</OutputType>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PackageId>Phares.AA.Face</PackageId>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<Version>9.0.100.0</Version>
|
||||
<Authors>Mike Phares</Authors>
|
||||
<Company>Phares</Company>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
|
||||
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
|
||||
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsWindows)'=='true'">
|
||||
<DefineConstants>Windows</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsOSX)'=='true'">
|
||||
<DefineConstants>OSX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsLinux)'=='true'">
|
||||
<DefineConstants>Linux</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
|
||||
<SupportedPlatform Include="browser" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Metadata\AA.Metadata.csproj" />
|
||||
<ProjectReference Include="..\Shared\AA.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
3
Face/Models/D_Face.cs
Normal file
3
Face/Models/D_Face.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace View_by_Distance.Metadata.Models;
|
||||
|
||||
public class D_Face() { }
|
53
Face/Models/Stateless/Face.cs
Normal file
53
Face/Models/Stateless/Face.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Metadata.Models;
|
||||
using View_by_Distance.Metadata.Models.Stateless.Methods;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Face.Models.Stateless.Methods;
|
||||
|
||||
internal static class Face
|
||||
{
|
||||
|
||||
private static void ExifDirectoriesParallelFor(ResultSettings resultSettings, MetadataSettings metadataSettings, List<ExifDirectory> results, FileInfo fileInfo)
|
||||
{
|
||||
FileHolder fileHolder = FileHolder.Get(fileInfo, id: null);
|
||||
const PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName = null;
|
||||
FilePath filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
ExifDirectory exifDirectory = IMetadata.GetExifDirectory(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
|
||||
results.Add(exifDirectory);
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<ExifDirectory> GetExifDirectories(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution)
|
||||
{
|
||||
List<ExifDirectory> results = [];
|
||||
FileInfo fileInfo;
|
||||
int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
|
||||
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
|
||||
long? skipOlderThan = distanceSettings.SkipOlderThanDays < 1 ? null : new DateTime(ticks).AddDays(-distanceSettings.SkipOlderThanDays).Ticks;
|
||||
string resultsFullGroupDirectory = Path.GetFullPath(IResult.GetResultsFullGroupDirectory(resultSettings,
|
||||
nameof(D_Face),
|
||||
outputResolution,
|
||||
includeResizeGroup: true,
|
||||
includeModel: true,
|
||||
includePredictorModel: true));
|
||||
string contentDirectory = Path.Combine(resultsFullGroupDirectory, resultSettings.ResultContent);
|
||||
if (!Directory.Exists(contentDirectory))
|
||||
_ = Directory.CreateDirectory(contentDirectory);
|
||||
string collectionDirectory = Path.Combine(resultsFullGroupDirectory, resultSettings.ResultCollection);
|
||||
if (!Directory.Exists(collectionDirectory))
|
||||
_ = Directory.CreateDirectory(collectionDirectory);
|
||||
string[] files = Directory.GetFiles(contentDirectory, $"*{compareSettings.FacesFileNameExtension}", SearchOption.AllDirectories);
|
||||
compare.ConstructProgressBar(files.Length, $"{nameof(D_Face)}.{nameof(GetExifDirectories)}");
|
||||
_ = Parallel.For(0, files.Length, parallelOptions, (i, state) =>
|
||||
{
|
||||
compare.Tick();
|
||||
fileInfo = new(files[i]);
|
||||
if (skipOlderThan is null || (fileInfo.Exists && fileInfo.LastWriteTime.Ticks >= skipOlderThan.Value))
|
||||
ExifDirectoriesParallelFor(resultSettings, metadataSettings, results, fileInfo);
|
||||
});
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
}
|
16
Face/Models/Stateless/Methods/IFace.cs
Normal file
16
Face/Models/Stateless/Methods/IFace.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Face.Models.Stateless.Methods;
|
||||
|
||||
public interface IFace
|
||||
{
|
||||
|
||||
ReadOnlyCollection<ExifDirectory> TestStatic_GetExifDirectories(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution) =>
|
||||
GetExifDirectories(resultSettings, metadataSettings, distanceSettings, compareSettings, compare, ticks, outputResolution);
|
||||
static ReadOnlyCollection<ExifDirectory> GetExifDirectories(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution) =>
|
||||
Face.GetExifDirectories(resultSettings, metadataSettings, distanceSettings, compareSettings, compare, ticks, outputResolution);
|
||||
|
||||
}
|
51
FaceRecognitionDotNet/AA.FaceRecognitionDotNet.csproj
Normal file
51
FaceRecognitionDotNet/AA.FaceRecognitionDotNet.csproj
Normal file
@ -0,0 +1,51 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>library</OutputType>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PackageId>Phares.AA.FaceRecognitionDotNet</PackageId>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<Version>9.0.100.1</Version>
|
||||
<Authors>Mike Phares</Authors>
|
||||
<Company>Phares</Company>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
|
||||
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
|
||||
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsWindows)'=='true'">
|
||||
<DefineConstants>Windows</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsOSX)'=='true'">
|
||||
<DefineConstants>OSX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsLinux)'=='true'">
|
||||
<DefineConstants>Linux</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
|
||||
<SupportedPlatform Include="browser" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DlibDotNet" Version="19.21.0.20220724" />
|
||||
<!--PackageReference Include="configuration.MKL" Version="19.21.0.20210302" /-->
|
||||
<!--PackageReference Include="DlibDotNet-WithCUDA" Version="19.17.0.20190429" /-->
|
||||
<!--PackageReference Include="configuration.CUDA92" Version="19.21.0.20210302" /-->
|
||||
<!--PackageReference Include="configuration.CUDA102" Version="19.21.0.20210302" /-->
|
||||
<!--PackageReference Include="configuration.CUDA110" Version="19.21.0.20210302" /-->
|
||||
<!--PackageReference Include="configuration.CUDA111" Version="19.21.0.20210302" /-->
|
||||
<!--PackageReference Include="configuration.CUDA112" Version="19.21.0.20210302" /-->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\AA.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
97
FaceRecognitionDotNet/DisposableObject.cs
Normal file
97
FaceRecognitionDotNet/DisposableObject.cs
Normal file
@ -0,0 +1,97 @@
|
||||
namespace View_by_Distance.FaceRecognitionDotNet;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a class which has managed or unmanaged resources.
|
||||
/// </summary>
|
||||
public abstract class DisposableObject : IDisposable
|
||||
{
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance has been disposed.
|
||||
/// </summary>
|
||||
/// <returns>true if this instance has been disposed; otherwise, false.</returns>
|
||||
public bool IsDisposed
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// If this object is disposed, then <see cref="ObjectDisposedException"/> is thrown.
|
||||
/// </summary>
|
||||
public void ThrowIfDisposed() =>
|
||||
ObjectDisposedException.ThrowIf(IsDisposed, this);
|
||||
|
||||
internal void ThrowIfDisposed(string objectName)
|
||||
{
|
||||
#pragma warning disable CA1513
|
||||
if (IsDisposed)
|
||||
throw new ObjectDisposedException(objectName);
|
||||
#pragma warning restore CA1513
|
||||
}
|
||||
|
||||
#region Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Releases all managed resources.
|
||||
/// </summary>
|
||||
protected virtual void DisposeManaged()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all unmanaged resources.
|
||||
/// </summary>
|
||||
protected virtual void DisposeUnmanaged()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources used by this <see cref="DisposableObject"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources used by this <see cref="DisposableObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Indicate value whether <see cref="IDisposable.Dispose"/> method was called.</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
IsDisposed = true;
|
||||
|
||||
if (disposing)
|
||||
|
||||
DisposeManaged();
|
||||
|
||||
DisposeUnmanaged();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
111
FaceRecognitionDotNet/Dlib/Python/CnnFaceDetectionModelV1.cs
Normal file
111
FaceRecognitionDotNet/Dlib/Python/CnnFaceDetectionModelV1.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using DlibDotNet;
|
||||
using DlibDotNet.Dnn;
|
||||
using View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet.Dlib.Python;
|
||||
|
||||
internal sealed class CnnFaceDetectionModelV1
|
||||
{
|
||||
|
||||
#region Methods
|
||||
|
||||
public static IEnumerable<MModRect> Detect(LossMmod net, Image image, int upsampleNumTimes)
|
||||
{
|
||||
using PyramidDown? pyr = new(2);
|
||||
List<MModRect>? rects = [];
|
||||
|
||||
// Copy the data into dlib based objects
|
||||
using Matrix<RgbPixel>? matrix = new();
|
||||
Mode type = image.Mode;
|
||||
switch (type)
|
||||
{
|
||||
case Mode.Greyscale:
|
||||
case Mode.Rgb:
|
||||
DlibDotNet.Dlib.AssignImage(image.Matrix, matrix);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported image type, must be 8bit gray or RGB image.");
|
||||
}
|
||||
|
||||
// Upsampling the image will allow us to detect smaller faces but will cause the
|
||||
// program to use more RAM and run longer.
|
||||
int levels = upsampleNumTimes;
|
||||
while (levels > 0)
|
||||
{
|
||||
levels--;
|
||||
DlibDotNet.Dlib.PyramidUp<PyramidDown>(matrix, 2);
|
||||
}
|
||||
|
||||
OutputLabels<IEnumerable<MModRect>>? dets = net.Operator(matrix);
|
||||
|
||||
// Scale the detection locations back to the original image size
|
||||
// if the image was upscaled.
|
||||
foreach (MModRect? d in dets.First())
|
||||
{
|
||||
DRectangle drect = pyr.RectDown(new DRectangle(d.Rect), (uint)upsampleNumTimes);
|
||||
d.Rect = new Rectangle((int)drect.Left, (int)drect.Top, (int)drect.Right, (int)drect.Bottom);
|
||||
rects.Add(d);
|
||||
}
|
||||
|
||||
return rects;
|
||||
}
|
||||
|
||||
public static IEnumerable<IEnumerable<MModRect>> DetectMulti(LossMmod net, IEnumerable<Image> images, int upsampleNumTimes, int batchSize = 128)
|
||||
{
|
||||
List<Matrix<RgbPixel>>? destImages = [];
|
||||
List<IEnumerable<MModRect>>? allRects = [];
|
||||
|
||||
try
|
||||
{
|
||||
using PyramidDown? pyr = new(2);
|
||||
// Copy the data into dlib based objects
|
||||
foreach (Image? image in images)
|
||||
{
|
||||
Matrix<RgbPixel>? matrix = new();
|
||||
Mode type = image.Mode;
|
||||
switch (type)
|
||||
{
|
||||
case Mode.Greyscale:
|
||||
case Mode.Rgb:
|
||||
DlibDotNet.Dlib.AssignImage(image.Matrix, matrix);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported image type, must be 8bit gray or RGB image.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < upsampleNumTimes; i++)
|
||||
DlibDotNet.Dlib.PyramidUp(matrix);
|
||||
|
||||
destImages.Add(matrix);
|
||||
}
|
||||
|
||||
for (int i = 1; i < destImages.Count; i++)
|
||||
if (destImages[i - 1].Columns != destImages[i].Columns || destImages[i - 1].Rows != destImages[i].Rows)
|
||||
throw new ArgumentException("Images in list must all have the same dimensions.");
|
||||
|
||||
OutputLabels<IEnumerable<MModRect>>? dets = net.Operator(destImages, (ulong)batchSize);
|
||||
foreach (IEnumerable<MModRect>? det in dets)
|
||||
{
|
||||
List<MModRect>? rects = [];
|
||||
foreach (MModRect? d in det)
|
||||
{
|
||||
DRectangle drect = pyr.RectDown(new DRectangle(d.Rect), (uint)upsampleNumTimes);
|
||||
d.Rect = new Rectangle((int)drect.Left, (int)drect.Top, (int)drect.Right, (int)drect.Bottom);
|
||||
rects.Add(d);
|
||||
}
|
||||
|
||||
allRects.Add(rects);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (Matrix<RgbPixel>? matrix in destImages)
|
||||
matrix.Dispose();
|
||||
}
|
||||
|
||||
return allRects;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
129
FaceRecognitionDotNet/Dlib/Python/FaceRecognitionModelV1.cs
Normal file
129
FaceRecognitionDotNet/Dlib/Python/FaceRecognitionModelV1.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using DlibDotNet;
|
||||
using DlibDotNet.Dnn;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet.Dlib.Python;
|
||||
|
||||
internal sealed class FaceRecognitionModelV1
|
||||
{
|
||||
|
||||
#region Methods
|
||||
|
||||
public static Matrix<double> ComputeFaceDescriptor(LossMetric net, Image img, FullObjectDetection face, int numberOfJitters)
|
||||
{
|
||||
FullObjectDetection[]? faces = [face];
|
||||
return ComputeFaceDescriptors(net, img, faces, numberOfJitters).First();
|
||||
}
|
||||
|
||||
public static IEnumerable<Matrix<double>> ComputeFaceDescriptors(LossMetric net, Image img, IEnumerable<FullObjectDetection> faces, int numberOfJitters)
|
||||
{
|
||||
Image[]? batchImage = [img];
|
||||
IEnumerable<FullObjectDetection>[]? batchFaces = [faces];
|
||||
return BatchComputeFaceDescriptors(net, batchImage, batchFaces, numberOfJitters).First();
|
||||
}
|
||||
|
||||
public static IEnumerable<IEnumerable<Matrix<double>>> BatchComputeFaceDescriptors(LossMetric net,
|
||||
IList<Image> batchImages,
|
||||
IList<IEnumerable<FullObjectDetection>> batchFaces,
|
||||
int numberOfJitters)
|
||||
{
|
||||
if (batchImages.Count != batchFaces.Count)
|
||||
throw new ArgumentException("The array of images and the array of array of locations must be of the same size");
|
||||
|
||||
foreach (IEnumerable<FullObjectDetection>? faces in batchFaces)
|
||||
foreach (FullObjectDetection? f in faces)
|
||||
{
|
||||
if (f.Parts is not 68 and not 5)
|
||||
throw new ArgumentException("The full_object_detection must use the iBUG 300W 68 point face landmark style or dlib's 5 point style.");
|
||||
}
|
||||
|
||||
List<Array<Matrix<RgbPixel>>>? faceChipsArray = new(batchImages.Count);
|
||||
List<Matrix<RgbPixel>>? faceChips = [];
|
||||
for (int i = 0; i < batchImages.Count; ++i)
|
||||
{
|
||||
IEnumerable<FullObjectDetection>? faces = batchFaces[i];
|
||||
Image? img = batchImages[i];
|
||||
|
||||
List<ChipDetails>? dets = new(faces.Count());
|
||||
foreach (FullObjectDetection? f in faces)
|
||||
dets.Add(DlibDotNet.Dlib.GetFaceChipDetails(f, 150, 0.25));
|
||||
|
||||
Array<Matrix<RgbPixel>>? thisImageFaceChips = DlibDotNet.Dlib.ExtractImageChips<RgbPixel>(img.Matrix, dets);
|
||||
foreach (Matrix<RgbPixel>? chip in thisImageFaceChips)
|
||||
faceChips.Add(chip);
|
||||
faceChipsArray.Add(thisImageFaceChips);
|
||||
|
||||
foreach (ChipDetails? det in dets)
|
||||
det.Dispose();
|
||||
}
|
||||
|
||||
List<List<Matrix<double>>>? faceDescriptors = [];
|
||||
for (int i = 0, count = batchImages.Count; i < count; i++)
|
||||
faceDescriptors.Add([]);
|
||||
|
||||
if (numberOfJitters <= 1)
|
||||
{
|
||||
// extract descriptors and convert from float vectors to double vectors
|
||||
OutputLabels<Matrix<float>>? descriptors = net.Operator(faceChips, 16);
|
||||
int index = 0;
|
||||
Matrix<float>[]? list = descriptors.Select(matrix => matrix).ToArray();
|
||||
for (int i = 0; i < batchFaces.Count; ++i)
|
||||
for (int j = 0; j < batchFaces[i].Count(); ++j)
|
||||
faceDescriptors[i].Add(DlibDotNet.Dlib.MatrixCast<double>(list[index++]));
|
||||
|
||||
if (index != list.Length)
|
||||
throw new ApplicationException();
|
||||
}
|
||||
else
|
||||
{
|
||||
// extract descriptors and convert from float vectors to double vectors
|
||||
int index = 0;
|
||||
for (int i = 0; i < batchFaces.Count; ++i)
|
||||
for (int j = 0; j < batchFaces[i].Count(); ++j)
|
||||
{
|
||||
Matrix<RgbPixel>[]? tmp = JitterImage(faceChips[index++], numberOfJitters).ToArray();
|
||||
using (OutputLabels<Matrix<float>>? tmp2 = net.Operator(tmp, 16))
|
||||
using (MatrixOp? mat = DlibDotNet.Dlib.Mat(tmp2))
|
||||
{
|
||||
Matrix<double>? r = DlibDotNet.Dlib.Mean<double>(mat);
|
||||
faceDescriptors[i].Add(r);
|
||||
}
|
||||
|
||||
foreach (Matrix<RgbPixel>? matrix in tmp)
|
||||
matrix.Dispose();
|
||||
}
|
||||
|
||||
if (index != faceChips.Count)
|
||||
throw new ApplicationException();
|
||||
}
|
||||
|
||||
if (faceChipsArray.Count > 0)
|
||||
{
|
||||
foreach (Array<Matrix<RgbPixel>>? array in faceChipsArray)
|
||||
{
|
||||
foreach (Matrix<RgbPixel>? faceChip in array)
|
||||
faceChip.Dispose();
|
||||
array.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return faceDescriptors;
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private static readonly Rand _Rand = new();
|
||||
|
||||
private static IEnumerable<Matrix<RgbPixel>> JitterImage(Matrix<RgbPixel> img, int numberOfJitters)
|
||||
{
|
||||
List<Matrix<RgbPixel>>? crops = [];
|
||||
for (int i = 0; i < numberOfJitters; ++i)
|
||||
crops.Add(DlibDotNet.Dlib.JitterImage(img, _Rand));
|
||||
|
||||
return crops;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
169
FaceRecognitionDotNet/Dlib/Python/SimpleObjectDetector.cs
Normal file
169
FaceRecognitionDotNet/Dlib/Python/SimpleObjectDetector.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using DlibDotNet;
|
||||
using View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet.Dlib.Python;
|
||||
|
||||
internal sealed class SimpleObjectDetector
|
||||
{
|
||||
|
||||
#region Methods
|
||||
|
||||
public static IEnumerable<Rectangle> RunDetectorWithUpscale1(FrontalFaceDetector detector,
|
||||
Image img,
|
||||
uint upsamplingAmount,
|
||||
double adjustThreshold,
|
||||
List<double> detectionConfidences,
|
||||
List<ulong> weightIndices)
|
||||
{
|
||||
List<Rectangle>? rectangles = [];
|
||||
|
||||
if (img.Mode == Mode.Greyscale)
|
||||
{
|
||||
Matrix<byte>? greyscaleMatrix = img.Matrix as Matrix<byte>;
|
||||
if (upsamplingAmount == 0)
|
||||
{
|
||||
detector.Operator(greyscaleMatrix, out IEnumerable<RectDetection>? rectDetections, adjustThreshold);
|
||||
|
||||
RectDetection[]? dets = rectDetections.ToArray();
|
||||
SplitRectDetections(dets, rectangles, detectionConfidences, weightIndices);
|
||||
|
||||
foreach (RectDetection? rectDetection in dets)
|
||||
rectDetection.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
using PyramidDown? pyr = new(2);
|
||||
Matrix<byte>? temp = null;
|
||||
|
||||
try
|
||||
{
|
||||
DlibDotNet.Dlib.PyramidUp(greyscaleMatrix, pyr, out temp);
|
||||
|
||||
uint levels = upsamplingAmount - 1;
|
||||
while (levels > 0)
|
||||
{
|
||||
levels--;
|
||||
DlibDotNet.Dlib.PyramidUp(temp);
|
||||
}
|
||||
|
||||
detector.Operator(temp, out IEnumerable<RectDetection>? rectDetections, adjustThreshold);
|
||||
|
||||
RectDetection[]? dets = rectDetections.ToArray();
|
||||
foreach (RectDetection? t in dets)
|
||||
t.Rect = pyr.RectDown(t.Rect, upsamplingAmount);
|
||||
|
||||
SplitRectDetections(dets, rectangles, detectionConfidences, weightIndices);
|
||||
|
||||
foreach (RectDetection? rectDetection in dets)
|
||||
rectDetection.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
temp?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return rectangles;
|
||||
}
|
||||
else
|
||||
{
|
||||
Matrix<RgbPixel>? rgbMatrix = img.Matrix as Matrix<RgbPixel>;
|
||||
if (upsamplingAmount == 0)
|
||||
{
|
||||
detector.Operator(rgbMatrix, out IEnumerable<RectDetection>? rectDetections, adjustThreshold);
|
||||
|
||||
RectDetection[]? dets = rectDetections.ToArray();
|
||||
SplitRectDetections(dets, rectangles, detectionConfidences, weightIndices);
|
||||
|
||||
foreach (RectDetection? rectDetection in dets)
|
||||
rectDetection.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
using PyramidDown? pyr = new(2);
|
||||
Matrix<RgbPixel>? temp = null;
|
||||
|
||||
try
|
||||
{
|
||||
DlibDotNet.Dlib.PyramidUp(rgbMatrix, pyr, out temp);
|
||||
|
||||
uint levels = upsamplingAmount - 1;
|
||||
while (levels > 0)
|
||||
{
|
||||
levels--;
|
||||
DlibDotNet.Dlib.PyramidUp(temp);
|
||||
}
|
||||
|
||||
detector.Operator(temp, out IEnumerable<RectDetection>? rectDetections, adjustThreshold);
|
||||
|
||||
RectDetection[]? dets = rectDetections.ToArray();
|
||||
foreach (RectDetection? t in dets)
|
||||
t.Rect = pyr.RectDown(t.Rect, upsamplingAmount);
|
||||
|
||||
SplitRectDetections(dets, rectangles, detectionConfidences, weightIndices);
|
||||
|
||||
foreach (RectDetection? rectDetection in dets)
|
||||
rectDetection.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
temp?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return rectangles;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Tuple<Rectangle, double>> RunDetectorWithUpscale2(FrontalFaceDetector detector,
|
||||
Image image,
|
||||
uint upsamplingAmount)
|
||||
{
|
||||
if (detector == null)
|
||||
throw new NullReferenceException(nameof(detector));
|
||||
if (image == null)
|
||||
throw new NullReferenceException(nameof(image));
|
||||
|
||||
detector.ThrowIfDisposed();
|
||||
image.ThrowIfDisposed();
|
||||
|
||||
List<double>? detectionConfidences = [];
|
||||
List<ulong>? weightIndices = [];
|
||||
const double adjustThreshold = 0.0;
|
||||
|
||||
Rectangle[]? rects = RunDetectorWithUpscale1(detector,
|
||||
image,
|
||||
upsamplingAmount,
|
||||
adjustThreshold,
|
||||
detectionConfidences,
|
||||
weightIndices).ToArray();
|
||||
|
||||
int index = 0;
|
||||
foreach (Rectangle rect in rects)
|
||||
yield return new Tuple<Rectangle, double>(rect, detectionConfidences[index++]);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private static void SplitRectDetections(RectDetection[] rectDetections,
|
||||
List<Rectangle> rectangles,
|
||||
List<double> detectionConfidences,
|
||||
List<ulong> weightIndices)
|
||||
{
|
||||
rectangles.Clear();
|
||||
detectionConfidences.Clear();
|
||||
weightIndices.Clear();
|
||||
|
||||
foreach (RectDetection? rectDetection in rectDetections)
|
||||
{
|
||||
rectangles.Add(rectDetection.Rect);
|
||||
detectionConfidences.Add(rectDetection.DetectionConfidence);
|
||||
weightIndices.Add(rectDetection.WeightIndex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
26
FaceRecognitionDotNet/Extensions/FaceDetector.cs
Normal file
26
FaceRecognitionDotNet/Extensions/FaceDetector.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using DlibDotNet;
|
||||
using View_by_Distance.Shared.Models;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// An abstract base class that provides functionality to detect face locations from image.
|
||||
/// </summary>
|
||||
public abstract class FaceDetector : DisposableObject
|
||||
{
|
||||
|
||||
#region Methods
|
||||
|
||||
internal IEnumerable<Location> Detect(Image image, int numberOfTimesToUpsample) => RawDetect(image.Matrix, numberOfTimesToUpsample);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerable collection of face location correspond to all faces in specified image.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix contains a face.</param>
|
||||
/// <param name="numberOfTimesToUpsample">The number of times to up-sample the image when finding faces.</param>
|
||||
/// <returns>An enumerable collection of face location correspond to all faces.</returns>
|
||||
protected abstract IEnumerable<Location> RawDetect(MatrixBase matrix, int numberOfTimesToUpsample);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
36
FaceRecognitionDotNet/Extensions/FaceLandmarkDetector.cs
Normal file
36
FaceRecognitionDotNet/Extensions/FaceLandmarkDetector.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using DlibDotNet;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// An abstract base class that provides functionality to detect face parts locations from face image.
|
||||
/// </summary>
|
||||
public abstract class FaceLandmarkDetector : DisposableObject
|
||||
{
|
||||
|
||||
#region Methods
|
||||
|
||||
internal FullObjectDetection Detect(Image image, Location location) => RawDetect(image.Matrix, location);
|
||||
|
||||
internal IEnumerable<Dictionary<FacePart, IEnumerable<FacePoint>>> GetLandmarks(IEnumerable<FacePoint[]> landmarkTuples) => RawGetLandmarks(landmarkTuples);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an object contains information of face parts corresponds to specified location in specified image.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix contains a face.</param>
|
||||
/// <param name="location">The location rectangle for a face.</param>
|
||||
/// <returns>An object contains information of face parts.</returns>
|
||||
protected abstract FullObjectDetection RawDetect(MatrixBase matrix, Location location);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerable collection of dictionary of face parts locations (eyes, nose, etc).
|
||||
/// </summary>
|
||||
/// <param name="landmarkTuples">The enumerable collection of face parts location.</param>
|
||||
/// <returns>An enumerable collection of dictionary of face parts locations (eyes, nose, etc).</returns>
|
||||
protected abstract IEnumerable<Dictionary<FacePart, IEnumerable<FacePoint>>> RawGetLandmarks(IEnumerable<FacePoint[]> landmarkTuples);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
105
FaceRecognitionDotNet/FaceEncoding.cs
Normal file
105
FaceRecognitionDotNet/FaceEncoding.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using DlibDotNet;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a feature data of face. This class cannot be inherited.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class FaceEncoding : DisposableObject, ISerializable
|
||||
{
|
||||
|
||||
#region Fields
|
||||
|
||||
[NonSerialized]
|
||||
private readonly Matrix<double> _Encoding;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal FaceEncoding(Matrix<double> encoding) => _Encoding = encoding;
|
||||
|
||||
private FaceEncoding(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info == null)
|
||||
throw new NullReferenceException(nameof(info));
|
||||
|
||||
double[]? array = info.GetValue(nameof(_Encoding), typeof(double[])) as double[];
|
||||
int? row = (int?)info.GetValue(nameof(_Encoding.Rows), typeof(int));
|
||||
int? column = (int?)info.GetValue(nameof(_Encoding.Columns), typeof(int));
|
||||
if (row is null)
|
||||
throw new NullReferenceException(nameof(row));
|
||||
if (column is null)
|
||||
throw new NullReferenceException(nameof(column));
|
||||
_Encoding = new Matrix<double>(array, row.Value, column.Value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
internal Matrix<double> Encoding => _Encoding;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of feature data.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException">This object is disposed.</exception>
|
||||
public int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return _Encoding.Size;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets a feature data of face as raw format.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="double"/> array that represents a feature data.</returns>
|
||||
/// <remarks><see cref="FaceEncoding"/> class supports serialization. This method is for interoperability between FaceRecognitionotNet and dlib.</remarks>
|
||||
/// <exception cref="ObjectDisposedException">This object is disposed.</exception>
|
||||
public double[] GetRawEncoding()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return _Encoding.ToArray();
|
||||
}
|
||||
|
||||
#region Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Releases all unmanaged resources.
|
||||
/// </summary>
|
||||
protected override void DisposeUnmanaged()
|
||||
{
|
||||
base.DisposeUnmanaged();
|
||||
_Encoding?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region ISerializable Members
|
||||
|
||||
/// <summary>
|
||||
/// Populates a <see cref="SerializationInfo"/> with the data needed to serialize the target object.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="SerializationInfo"/> to populate with data.</param>
|
||||
/// <param name="context">The destination (see <see cref="StreamingContext"/>) for this serialization.</param>
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
info.AddValue(nameof(_Encoding), _Encoding.ToArray());
|
||||
info.AddValue(nameof(_Encoding.Rows), _Encoding.Rows);
|
||||
info.AddValue(nameof(_Encoding.Columns), _Encoding.Columns);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
497
FaceRecognitionDotNet/FaceRecognition.cs
Normal file
497
FaceRecognitionDotNet/FaceRecognition.cs
Normal file
@ -0,0 +1,497 @@
|
||||
using DlibDotNet;
|
||||
using DlibDotNet.Dnn;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using View_by_Distance.FaceRecognitionDotNet.Dlib.Python;
|
||||
using View_by_Distance.FaceRecognitionDotNet.Extensions;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Stateless;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet;
|
||||
|
||||
public class FaceRecognition : DisposableObject
|
||||
{
|
||||
|
||||
public FaceDetector? CustomFaceDetector { get; set; }
|
||||
public FaceLandmarkDetector? CustomFaceLandmarkDetector { get; set; }
|
||||
|
||||
private readonly Model _Model;
|
||||
private readonly int _NumberOfJitters;
|
||||
private readonly LossMetric _FaceEncoder;
|
||||
private readonly LossMmod _CnnFaceDetector;
|
||||
private readonly int _NumberOfTimesToUpsample;
|
||||
private readonly PredictorModel _PredictorModel;
|
||||
private readonly FrontalFaceDetector _FaceDetector;
|
||||
private readonly ShapePredictor _PosePredictor5Point;
|
||||
private readonly ShapePredictor _PosePredictor68Point;
|
||||
|
||||
private record Record(Location Location, List<FaceEncoding?> FaceEncodings, List<List<FacePartAndFacePointArray>> FaceParts);
|
||||
|
||||
public FaceRecognition(int numberOfJitters, int numberOfTimesToUpsample, Model model, ModelParameter modelParameter, PredictorModel predictorModel)
|
||||
{
|
||||
if (modelParameter is null)
|
||||
throw new NullReferenceException(nameof(modelParameter));
|
||||
if (modelParameter.PosePredictor5FaceLandmarksModel is null)
|
||||
throw new NullReferenceException(nameof(modelParameter.PosePredictor5FaceLandmarksModel));
|
||||
if (modelParameter.PosePredictor68FaceLandmarksModel is null)
|
||||
throw new NullReferenceException(nameof(modelParameter.PosePredictor68FaceLandmarksModel));
|
||||
if (modelParameter.CnnFaceDetectorModel is null)
|
||||
throw new NullReferenceException(nameof(modelParameter.CnnFaceDetectorModel));
|
||||
if (modelParameter.FaceRecognitionModel is null)
|
||||
throw new NullReferenceException(nameof(modelParameter.FaceRecognitionModel));
|
||||
_Model = model;
|
||||
_PredictorModel = predictorModel;
|
||||
_NumberOfJitters = numberOfJitters;
|
||||
_NumberOfTimesToUpsample = numberOfTimesToUpsample;
|
||||
_FaceDetector?.Dispose();
|
||||
_FaceDetector = DlibDotNet.Dlib.GetFrontalFaceDetector();
|
||||
_PosePredictor68Point?.Dispose();
|
||||
_PosePredictor68Point = ShapePredictor.Deserialize(modelParameter.PosePredictor68FaceLandmarksModel);
|
||||
_PosePredictor5Point?.Dispose();
|
||||
_PosePredictor5Point = ShapePredictor.Deserialize(modelParameter.PosePredictor5FaceLandmarksModel);
|
||||
_CnnFaceDetector?.Dispose();
|
||||
_CnnFaceDetector = LossMmod.Deserialize(modelParameter.CnnFaceDetectorModel);
|
||||
_FaceEncoder?.Dispose();
|
||||
_FaceEncoder = LossMetric.Deserialize(modelParameter.FaceRecognitionModel);
|
||||
}
|
||||
|
||||
public static double FaceDistance(FaceEncoding faceEncoding, FaceEncoding faceToCompare)
|
||||
{
|
||||
if (faceEncoding is null)
|
||||
throw new NullReferenceException(nameof(faceEncoding));
|
||||
if (faceToCompare is null)
|
||||
throw new NullReferenceException(nameof(faceToCompare));
|
||||
faceEncoding.ThrowIfDisposed();
|
||||
faceToCompare.ThrowIfDisposed();
|
||||
if (faceEncoding.Encoding.Size == 0)
|
||||
return 0;
|
||||
using Matrix<double>? diff = faceEncoding.Encoding - faceToCompare.Encoding;
|
||||
return DlibDotNet.Dlib.Length(diff);
|
||||
}
|
||||
|
||||
private static FacePoint[] Join(IEnumerable<FacePoint> facePoints1, IEnumerable<FacePoint> facePoints2)
|
||||
{
|
||||
List<FacePoint> results = [.. facePoints1, .. facePoints2];
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
private List<FacePartAndFacePointArray> GetFaceParts(FullObjectDetection fullObjectDetection)
|
||||
{
|
||||
List<FacePartAndFacePointArray> results = [];
|
||||
FacePoint[] facePoints = Enumerable.Range(0, (int)fullObjectDetection.Parts)
|
||||
.Select(index => new FacePoint(index, fullObjectDetection.GetPart((uint)index).X, fullObjectDetection.GetPart((uint)index).Y))
|
||||
.ToArray();
|
||||
switch (_PredictorModel)
|
||||
{
|
||||
case PredictorModel.Custom:
|
||||
throw new NotImplementedException();
|
||||
case PredictorModel.Large:
|
||||
if (facePoints.Length == 68)
|
||||
{
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.Chin, facePoints.Skip(0).Take(17).ToArray()));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.LeftEyebrow, facePoints.Skip(17).Take(5).ToArray()));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.RightEyebrow, facePoints.Skip(22).Take(5).ToArray()));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.NoseBridge, facePoints.Skip(27).Take(5).ToArray()));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.NoseTip, facePoints.Skip(31).Take(5).ToArray()));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.LeftEye, facePoints.Skip(36).Take(6).ToArray()));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.RightEye, facePoints.Skip(42).Take(6).ToArray()));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.TopLip, Join(facePoints.Skip(48).Take(7), facePoints.Skip(60).Take(5))));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.BottomLip, Join(facePoints.Skip(55).Take(5), facePoints.Skip(65).Take(3))));
|
||||
}
|
||||
break;
|
||||
case PredictorModel.Small:
|
||||
if (facePoints.Length == 5)
|
||||
{
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.RightEye, facePoints.Skip(0).Take(2).ToArray()));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.LeftEye, facePoints.Skip(2).Take(2).ToArray()));
|
||||
results.Add(new FacePartAndFacePointArray(FacePart.NoseTip, facePoints.Skip(4).Take(1).ToArray()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private MModRect[] GetMModRects(Image image)
|
||||
{
|
||||
switch (_Model)
|
||||
{
|
||||
case Model.Cnn:
|
||||
return CnnFaceDetectionModelV1.Detect(_CnnFaceDetector, image, _NumberOfTimesToUpsample).ToArray();
|
||||
case Model.Hog:
|
||||
IEnumerable<Tuple<DlibDotNet.Rectangle, double>>? locations = SimpleObjectDetector.RunDetectorWithUpscale2(_FaceDetector, image, (uint)_NumberOfTimesToUpsample);
|
||||
return locations.Select(l => new MModRect { Rect = l.Item1, DetectionConfidence = l.Item2 }).ToArray();
|
||||
case Model.Custom:
|
||||
if (CustomFaceDetector is null)
|
||||
throw new NotSupportedException("The custom face detector is not ready.");
|
||||
return CustomFaceDetector.Detect(image, _NumberOfTimesToUpsample).Select(rect => new MModRect
|
||||
{
|
||||
Rect = new DlibDotNet.Rectangle(rect.Left, rect.Top, rect.Right, rect.Bottom),
|
||||
DetectionConfidence = rect.Confidence
|
||||
}).ToArray();
|
||||
default:
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Location> FaceLocations(Image image)
|
||||
{
|
||||
if (image is null)
|
||||
throw new NullReferenceException(nameof(image));
|
||||
image.ThrowIfDisposed();
|
||||
ThrowIfDisposed();
|
||||
List<Location> results = [];
|
||||
System.Drawing.Rectangle rectangle;
|
||||
IEnumerable<MModRect> mModRects = GetMModRects(image);
|
||||
foreach (MModRect? mModRect in mModRects)
|
||||
{
|
||||
rectangle = new(mModRect.Rect.Left, mModRect.Rect.Top, (int)mModRect.Rect.Width, (int)mModRect.Rect.Height);
|
||||
Location location = ILocation.TrimBound(mModRect.DetectionConfidence, rectangle, image.Width, image.Height, mModRects.Count());
|
||||
mModRect.Dispose();
|
||||
results.Add(location);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<FullObjectDetection> GetFullObjectDetections(Image image, List<Location> locations)
|
||||
{
|
||||
List<FullObjectDetection> results = [];
|
||||
if (_PredictorModel == PredictorModel.Custom)
|
||||
{
|
||||
if (CustomFaceLandmarkDetector is null)
|
||||
throw new NullReferenceException(nameof(CustomFaceLandmarkDetector));
|
||||
foreach (Location location in locations)
|
||||
{
|
||||
FullObjectDetection fullObjectDetection = CustomFaceLandmarkDetector.Detect(image, location);
|
||||
results.Add(fullObjectDetection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShapePredictor posePredictor = _PredictorModel switch
|
||||
{
|
||||
PredictorModel.Large => _PosePredictor68Point,
|
||||
PredictorModel.Small => _PosePredictor5Point,
|
||||
PredictorModel.Custom => throw new NotImplementedException(),
|
||||
_ => throw new Exception()
|
||||
};
|
||||
foreach (Location location in locations)
|
||||
{
|
||||
DlibDotNet.Rectangle rectangle = new(location.Left, location.Top, location.Right, location.Bottom);
|
||||
FullObjectDetection fullObjectDetection = posePredictor.Detect(image.Matrix, rectangle);
|
||||
results.Add(fullObjectDetection);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<Location> GetLocations(Image image)
|
||||
{
|
||||
List<Location> results = [];
|
||||
MModRect[] mModRects = GetMModRects(image);
|
||||
if (mModRects.Length != 0)
|
||||
{
|
||||
Location location;
|
||||
System.Drawing.Rectangle rectangle;
|
||||
foreach (MModRect? mModRect in mModRects)
|
||||
{
|
||||
rectangle = new(mModRect.Rect.Left, mModRect.Rect.Top, (int)mModRect.Rect.Width, (int)mModRect.Rect.Height);
|
||||
location = ILocation.TrimBound(mModRect.DetectionConfidence, rectangle, image.Width, image.Height, mModRects.Length);
|
||||
mModRect.Dispose();
|
||||
results.Add(location);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public List<FaceRecognitionGroup> GetCollection(Image image, List<Location> locations, bool includeFaceEncoding, bool includeFaceParts)
|
||||
{
|
||||
List<FaceRecognitionGroup> results = [];
|
||||
if (image is null)
|
||||
throw new NullReferenceException(nameof(image));
|
||||
image.ThrowIfDisposed();
|
||||
ThrowIfDisposed();
|
||||
if (_PredictorModel == PredictorModel.Custom)
|
||||
throw new NotSupportedException("FaceRecognition.PredictorModel.Custom is not supported.");
|
||||
if (locations.Count == 0)
|
||||
locations.AddRange(GetLocations(image));
|
||||
List<FullObjectDetection> fullObjectDetections = GetFullObjectDetections(image, locations);
|
||||
if (fullObjectDetections.Count != locations.Count)
|
||||
throw new Exception();
|
||||
Record record;
|
||||
List<Record> records = [];
|
||||
foreach (Location location in locations)
|
||||
{
|
||||
record = new(location, [], []);
|
||||
records.Add(record);
|
||||
}
|
||||
if (locations.Count != records.Count)
|
||||
throw new Exception();
|
||||
if (!includeFaceEncoding)
|
||||
{
|
||||
for (int i = 0; i < records.Count; i++)
|
||||
records[i].FaceEncodings.Add(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
Matrix<double> doubles;
|
||||
FaceEncoding faceEncoding;
|
||||
for (int i = 0; i < records.Count; i++)
|
||||
{
|
||||
doubles = FaceRecognitionModelV1.ComputeFaceDescriptor(_FaceEncoder, image, fullObjectDetections[i], _NumberOfJitters);
|
||||
faceEncoding = new(doubles);
|
||||
records[i].FaceEncodings.Add(faceEncoding);
|
||||
}
|
||||
}
|
||||
if (!includeFaceParts)
|
||||
{
|
||||
for (int i = 0; i < records.Count; i++)
|
||||
records[i].FaceParts.Add([]);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<FacePartAndFacePointArray> faceParts;
|
||||
for (int i = 0; i < records.Count; i++)
|
||||
{
|
||||
faceParts = GetFaceParts(fullObjectDetections[i]);
|
||||
records[i].FaceParts.Add(faceParts);
|
||||
}
|
||||
}
|
||||
foreach (FullObjectDetection fullObjectDetection in fullObjectDetections)
|
||||
fullObjectDetection.Dispose();
|
||||
const int indexZero = 0;
|
||||
FaceRecognitionGroup faceRecognitionGroupB;
|
||||
Dictionary<FacePart, FacePoint[]> keyValuePairs;
|
||||
foreach (Record r in records)
|
||||
{
|
||||
if (r.FaceEncodings.Count != 1 || r.FaceParts.Count != 1)
|
||||
continue;
|
||||
if (r.FaceParts[indexZero].Count == 0)
|
||||
faceRecognitionGroupB = new(r.Location, r.FaceEncodings[indexZero], null);
|
||||
else
|
||||
{
|
||||
keyValuePairs = [];
|
||||
foreach (FacePartAndFacePointArray facePartAndFacePointArray in r.FaceParts[indexZero])
|
||||
keyValuePairs.Add(facePartAndFacePointArray.FacePart, facePartAndFacePointArray.FacePoints);
|
||||
faceRecognitionGroupB = new(r.Location, r.FaceEncodings[indexZero], keyValuePairs);
|
||||
}
|
||||
results.Add(faceRecognitionGroupB);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static FaceEncoding LoadFaceEncoding(double[] encoding)
|
||||
{
|
||||
if (encoding is null)
|
||||
throw new NullReferenceException(nameof(encoding));
|
||||
if (encoding.Length != 128)
|
||||
{
|
||||
string message = $"{nameof(encoding)}.{nameof(encoding.Length)} must be 128.";
|
||||
throw new ArgumentOutOfRangeException(message);
|
||||
}
|
||||
#pragma warning disable
|
||||
Matrix<double>? matrix = Matrix<double>.CreateTemplateParameterizeMatrix(0, 1);
|
||||
#pragma warning restore
|
||||
matrix.SetSize(128);
|
||||
matrix.Assign(encoding);
|
||||
return new FaceEncoding(matrix);
|
||||
}
|
||||
|
||||
public static FaceEncoding LoadBFaceEncoding(double[] encoding)
|
||||
{
|
||||
if (encoding is null)
|
||||
throw new NullReferenceException(nameof(encoding));
|
||||
if (encoding.Length != 512)
|
||||
{
|
||||
string message = $"{nameof(encoding)}.{nameof(encoding.Length)} must be 512.";
|
||||
throw new ArgumentOutOfRangeException(message);
|
||||
}
|
||||
#pragma warning disable
|
||||
Matrix<double>? matrix = Matrix<double>.CreateTemplateParameterizeMatrix(0, 1);
|
||||
#pragma warning restore
|
||||
matrix.SetSize(512);
|
||||
matrix.Assign(encoding);
|
||||
return new FaceEncoding(matrix);
|
||||
}
|
||||
|
||||
public static Image LoadImageFile(string file, Mode mode = Mode.Rgb)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
throw new FileNotFoundException(file);
|
||||
return mode switch
|
||||
{
|
||||
Mode.Rgb => new Image(DlibDotNet.Dlib.LoadImageAsMatrix<RgbPixel>(file), mode),
|
||||
Mode.Greyscale => new Image(DlibDotNet.Dlib.LoadImageAsMatrix<byte>(file), mode),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
|
||||
public static Image? LoadImage(Bitmap bitmap)
|
||||
{
|
||||
Mode mode;
|
||||
int dstChannel;
|
||||
int srcChannel;
|
||||
int width = bitmap.Width;
|
||||
int height = bitmap.Height;
|
||||
PixelFormat format = bitmap.PixelFormat;
|
||||
System.Drawing.Rectangle rect = new(0, 0, width, height);
|
||||
#pragma warning disable IDE0010
|
||||
switch (format)
|
||||
{
|
||||
case PixelFormat.Format8bppIndexed:
|
||||
mode = Mode.Greyscale;
|
||||
srcChannel = 1;
|
||||
dstChannel = 1;
|
||||
break;
|
||||
case PixelFormat.Format24bppRgb:
|
||||
mode = Mode.Rgb;
|
||||
srcChannel = 3;
|
||||
dstChannel = 3;
|
||||
break;
|
||||
case PixelFormat.Format32bppRgb:
|
||||
case PixelFormat.Format32bppArgb:
|
||||
mode = Mode.Rgb;
|
||||
srcChannel = 4;
|
||||
dstChannel = 3;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"{nameof(bitmap)}", $"The specified {nameof(PixelFormat)} is not supported.");
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
BitmapData? data = null;
|
||||
try
|
||||
{
|
||||
data = bitmap.LockBits(rect, ImageLockMode.ReadOnly, format);
|
||||
unsafe
|
||||
{
|
||||
byte[]? array = new byte[width * height * dstChannel];
|
||||
fixed (byte* pArray = &array[0])
|
||||
{
|
||||
byte* dst = pArray;
|
||||
|
||||
switch (srcChannel)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
IntPtr src = data.Scan0;
|
||||
int stride = data.Stride;
|
||||
|
||||
for (int h = 0; h < height; h++)
|
||||
Marshal.Copy(IntPtr.Add(src, h * stride), array, h * width, width * dstChannel);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
{
|
||||
byte* src = (byte*)data.Scan0;
|
||||
int stride = data.Stride;
|
||||
for (int h = 0; h < height; h++)
|
||||
{
|
||||
int srcOffset = h * stride;
|
||||
int dstOffset = h * width * dstChannel;
|
||||
for (int w = 0; w < width; w++)
|
||||
{
|
||||
// BGR order to RGB order
|
||||
dst[dstOffset + (w * dstChannel) + 0] = src[srcOffset + (w * srcChannel) + 2];
|
||||
dst[dstOffset + (w * dstChannel) + 1] = src[srcOffset + (w * srcChannel) + 1];
|
||||
dst[dstOffset + (w * dstChannel) + 2] = src[srcOffset + (w * srcChannel) + 0];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
IntPtr ptr = (IntPtr)pArray;
|
||||
switch (mode)
|
||||
{
|
||||
case Mode.Rgb:
|
||||
return new Image(new Matrix<RgbPixel>(ptr, height, width, width * 3), Mode.Rgb);
|
||||
case Mode.Greyscale:
|
||||
return new Image(new Matrix<byte>(ptr, height, width, width), Mode.Greyscale);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (data != null)
|
||||
bitmap.UnlockBits(data);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ReadOnlyCollection<LocationContainer> GetLocationContainers(int permyriad, ReadOnlyCollection<LocationContainer> locationContainers, LocationContainer locationContainer)
|
||||
{
|
||||
List<LocationContainer> results = [];
|
||||
int lengthPermyriad;
|
||||
if (locationContainers.Count != 0)
|
||||
{
|
||||
double length;
|
||||
LocationContainer result;
|
||||
if (locationContainer.Encoding is not FaceEncoding faceEncodingToCompare)
|
||||
throw new NullReferenceException(nameof(locationContainer));
|
||||
faceEncodingToCompare.ThrowIfDisposed();
|
||||
foreach (LocationContainer l in locationContainers)
|
||||
{
|
||||
#pragma warning disable CA1513
|
||||
if (l.Encoding is not FaceEncoding faceEncoding || faceEncoding.IsDisposed)
|
||||
throw new ObjectDisposedException($"{nameof(l)} contains disposed object.");
|
||||
#pragma warning restore CA1513
|
||||
using (Matrix<double> diff = faceEncoding.Encoding - faceEncodingToCompare.Encoding)
|
||||
length = DlibDotNet.Dlib.Length(diff);
|
||||
lengthPermyriad = (int)(length * permyriad);
|
||||
result = LocationContainer.Get(locationContainer, l, lengthPermyriad, keepExifDirectory: false, keepEncoding: false);
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
LocationContainer[] array = results.OrderBy(l => l.LengthPermyriad).ToArray();
|
||||
return array.AsReadOnly();
|
||||
}
|
||||
|
||||
public static List<FaceDistance> FaceDistances(ReadOnlyCollection<FaceDistance> faceDistances, FaceDistance faceDistanceToCompare)
|
||||
{
|
||||
List<FaceDistance> results = [];
|
||||
if (faceDistances.Count != 0)
|
||||
{
|
||||
double length;
|
||||
FaceDistance result;
|
||||
if (faceDistanceToCompare.Encoding is not FaceEncoding faceEncodingToCompare)
|
||||
throw new NullReferenceException(nameof(faceDistanceToCompare));
|
||||
faceEncodingToCompare.ThrowIfDisposed();
|
||||
foreach (FaceDistance faceDistance in faceDistances)
|
||||
{
|
||||
#pragma warning disable CA1513
|
||||
if (faceDistance.Encoding is not FaceEncoding faceEncoding || faceEncoding.IsDisposed)
|
||||
throw new ObjectDisposedException($"{nameof(faceDistances)} contains disposed object.");
|
||||
#pragma warning restore CA1513
|
||||
using (Matrix<double> diff = faceEncoding.Encoding - faceEncodingToCompare.Encoding)
|
||||
length = DlibDotNet.Dlib.Length(diff);
|
||||
result = new(faceDistance, length);
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
#pragma warning restore CA1416
|
||||
|
||||
protected override void DisposeUnmanaged()
|
||||
{
|
||||
base.DisposeUnmanaged();
|
||||
_PosePredictor68Point?.Dispose();
|
||||
_PosePredictor5Point?.Dispose();
|
||||
_CnnFaceDetector?.Dispose();
|
||||
_FaceEncoder?.Dispose();
|
||||
_FaceDetector?.Dispose();
|
||||
}
|
||||
|
||||
}
|
6
FaceRecognitionDotNet/FaceRecognitionGroup.cs
Normal file
6
FaceRecognitionDotNet/FaceRecognitionGroup.cs
Normal file
@ -0,0 +1,6 @@
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet;
|
||||
|
||||
public record FaceRecognitionGroup(Location Location, FaceEncoding? FaceEncoding, Dictionary<FacePart, FacePoint[]>? KeyValuePairs);
|
22
FaceRecognitionDotNet/FaceRecognitionModels.cs
Normal file
22
FaceRecognitionDotNet/FaceRecognitionModels.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace View_by_Distance.FaceRecognitionDotNet;
|
||||
|
||||
internal sealed class FaceRecognitionModels
|
||||
{
|
||||
|
||||
public static string GetPosePredictorModelLocation() => "shape_predictor_68_face_landmarks.dat";
|
||||
|
||||
public static string GetPosePredictorFivePointModelLocation() => "shape_predictor_5_face_landmarks.dat";
|
||||
|
||||
public static string GetFaceRecognitionModelLocation() => "dlib_face_recognition_resnet_model_v1.dat";
|
||||
|
||||
public static string GetCnnFaceDetectorModelLocation() => "mmod_human_face_detector.dat";
|
||||
|
||||
public static string GetPosePredictor194PointModelLocation() => "helen-dataset.dat";
|
||||
|
||||
public static string GetAgeNetworkModelLocation() => "adience-age-network.dat";
|
||||
|
||||
public static string GetGenderNetworkModelLocation() => "utkface-gender-network.dat";
|
||||
|
||||
public static string GetEmotionNetworkModelLocation() => "corrective-reannotation-of-fer-ck-kdef-emotion-network_test_best.dat";
|
||||
|
||||
}
|
129
FaceRecognitionDotNet/Image.cs
Normal file
129
FaceRecognitionDotNet/Image.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using DlibDotNet;
|
||||
using DlibDotNet.Extensions;
|
||||
using System.Drawing;
|
||||
using View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a image data. This class cannot be inherited.
|
||||
/// </summary>
|
||||
public sealed class Image : DisposableObject
|
||||
{
|
||||
|
||||
#region Fields
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal Image(MatrixBase matrix, Mode mode)
|
||||
{
|
||||
Matrix = matrix;
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the image.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException">This object is disposed.</exception>
|
||||
public int Height
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return Matrix.Rows;
|
||||
}
|
||||
}
|
||||
|
||||
internal MatrixBase Matrix { get; private set; }
|
||||
|
||||
internal Mode Mode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of the image.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException">This object is disposed.</exception>
|
||||
public int Width
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return Matrix.Columns;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Saves this <see cref="Image"/> to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">A string that contains the name of the file to which to save this <see cref="Image"/>.</param>
|
||||
/// <param name="format">The <see cref="ImageFormat"/> for this <see cref="Image"/>.</param>
|
||||
/// <exception cref="NullReferenceException"><paramref name="fileName"/> is null.</exception>
|
||||
/// <exception cref="ObjectDisposedException">This object is disposed.</exception>
|
||||
public void Save(string fileName, ImageFormat format)
|
||||
{
|
||||
if (fileName == null)
|
||||
throw new NullReferenceException(nameof(fileName));
|
||||
|
||||
ThrowIfDisposed();
|
||||
|
||||
string? directory = Path.GetDirectoryName(fileName);
|
||||
if (!Directory.Exists(directory) && !string.IsNullOrWhiteSpace(directory))
|
||||
_ = Directory.CreateDirectory(directory);
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case ImageFormat.Bmp:
|
||||
DlibDotNet.Dlib.SaveBmp(Matrix, fileName);
|
||||
break;
|
||||
case ImageFormat.Jpeg:
|
||||
DlibDotNet.Dlib.SaveJpeg(Matrix, fileName);
|
||||
break;
|
||||
case ImageFormat.Png:
|
||||
DlibDotNet.Dlib.SavePng(Matrix, fileName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this <see cref="Image"/> to a GDI+ <see cref="Bitmap"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Bitmap"/> that represents the converted <see cref="Image"/>.</returns>
|
||||
/// <exception cref="ObjectDisposedException">This object is disposed.</exception>
|
||||
/// <exception cref="NotSupportedException">A Greyscale image is not supported.</exception>
|
||||
public Bitmap ToBitmap()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (Mode == Mode.Greyscale)
|
||||
throw new NotSupportedException();
|
||||
|
||||
return ((Matrix<RgbPixel>)Matrix).ToBitmap();
|
||||
}
|
||||
|
||||
#region Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Releases all unmanaged resources.
|
||||
/// </summary>
|
||||
protected override void DisposeUnmanaged()
|
||||
{
|
||||
base.DisposeUnmanaged();
|
||||
Matrix?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
49
FaceRecognitionDotNet/ModelParameter.cs
Normal file
49
FaceRecognitionDotNet/ModelParameter.cs
Normal file
@ -0,0 +1,49 @@
|
||||
namespace View_by_Distance.FaceRecognitionDotNet;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the model binary datum. This class cannot be inherited.
|
||||
/// </summary>
|
||||
public sealed class ModelParameter
|
||||
{
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the binary data of model for 68 points face landmarks.
|
||||
/// </summary>
|
||||
public byte[]? PosePredictor68FaceLandmarksModel
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the binary data of model for 5 points face landmarks.
|
||||
/// </summary>
|
||||
public byte[]? PosePredictor5FaceLandmarksModel
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the binary data of model for face encoding.
|
||||
/// </summary>
|
||||
public byte[]? FaceRecognitionModel
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the binary data of model for face detector by using CNN.
|
||||
/// </summary>
|
||||
public byte[]? CnnFaceDetectorModel
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
107
FaceRecognitionDotNet/Point.cs
Normal file
107
FaceRecognitionDotNet/Point.cs
Normal file
@ -0,0 +1,107 @@
|
||||
namespace View_by_Distance.FaceRecognitionDotNet;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an ordered pair of integer x- and y-coordinates that defines a point in a two-dimensional plane.
|
||||
/// </summary>
|
||||
public readonly struct Point : IEquatable<Point>
|
||||
{
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Point"/> structure with the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x">The horizontal position of the point.</param>
|
||||
/// <param name="y">The vertical position of the point.</param>
|
||||
public Point(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
internal Point(DlibDotNet.Point point)
|
||||
{
|
||||
X = point.X;
|
||||
Y = point.Y;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the x-coordinate of this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
public int X
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the y-coordinate of this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
public int Y
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Point"/> structures for equality.
|
||||
/// </summary>
|
||||
/// <param name="other">The point to compare to this instance.</param>
|
||||
/// <returns><code>true</code> if both <see cref="Point"/> structures contain the same <see cref="X"/> and <see cref="Y"/> values; otherwise, <code>false</code>.</returns>
|
||||
public bool Equals(Point other)
|
||||
{
|
||||
return X == other.X &&
|
||||
Y == other.Y;
|
||||
}
|
||||
|
||||
#region overrides
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="object"/> is a <see cref="Point"/> and whether it contains the same coordinates as this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="object"/> to compare.</param>
|
||||
/// <returns><code>true</code> if <paramref name="obj"/> is a <see cref="Point"/> and contains the same <see cref="X"/> and <see cref="Y"/> values as this <see cref="Point"/>; otherwise, <code>false</code>.</returns>
|
||||
public override bool Equals(object? obj) => obj is Point point && Equals(point);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code for this <see cref="Point"/>.
|
||||
/// </summary>
|
||||
/// <returns>The hash code for this <see cref="Point"/> structure.</returns>
|
||||
#pragma warning disable IDE0070
|
||||
public override int GetHashCode()
|
||||
#pragma warning restore IDE0070
|
||||
{
|
||||
int hashCode = 1861411795;
|
||||
hashCode = hashCode * -1521134295 + X.GetHashCode();
|
||||
hashCode = hashCode * -1521134295 + Y.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Point"/> structures for equality.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/> structure to compare.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/> structure to compare.</param>
|
||||
/// <returns><code>true</code> if both the <see cref="X"/> and <see cref="Y"/> coordinates of <paramref name="point1"/> and <paramref name="point2"/> are equal; otherwise, <code>false</code>.</returns>
|
||||
public static bool operator ==(Point point1, Point point2) => point1.Equals(point2);
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="Point"/> structures for inequality.
|
||||
/// </summary>
|
||||
/// <param name="point1">The first <see cref="Point"/> structure to compare.</param>
|
||||
/// <param name="point2">The second <see cref="Point"/> structure to compare.</param>
|
||||
/// <returns><code>true</code> if <paramref name="point1"/> and <paramref name="point2"/> have different <see cref="X"/> or <see cref="Y"/> coordinates; <code>false</code> if <paramref name="point1"/> and <paramref name="point2"/> have the same <see cref="X"/> and <see cref="Y"/> coordinates.</returns>
|
||||
|
||||
public static bool operator !=(Point point1, Point point2) => !(point1 == point2);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
@ -9,41 +9,43 @@ namespace View_by_Distance.Metadata.Models;
|
||||
public class A_Metadata
|
||||
{
|
||||
|
||||
private readonly MetadataConfiguration _MetadataConfiguration;
|
||||
private readonly ResultSettings _ResultSettings;
|
||||
private readonly MetadataSettings _MetadataSettings;
|
||||
private readonly ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> _FileGroups;
|
||||
|
||||
public A_Metadata(MetadataConfiguration metadataConfiguration)
|
||||
public A_Metadata(ResultSettings resultSettings, MetadataSettings metadataSettings)
|
||||
{
|
||||
_MetadataConfiguration = metadataConfiguration;
|
||||
string aResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(metadataConfiguration.ResultConfiguration,
|
||||
_ResultSettings = resultSettings;
|
||||
_MetadataSettings = metadataSettings;
|
||||
string aResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
|
||||
nameof(A_Metadata),
|
||||
string.Empty,
|
||||
includeResizeGroup: false,
|
||||
includeModel: false,
|
||||
includePredictorModel: false);
|
||||
_FileGroups = IPath.GetKeyValuePairs(metadataConfiguration.ResultConfiguration, aResultsFullGroupDirectory, [metadataConfiguration.ResultConfiguration.ResultSingleton]);
|
||||
_FileGroups = IPath.GetKeyValuePairs(resultSettings, aResultsFullGroupDirectory, [resultSettings.ResultSingleton]);
|
||||
}
|
||||
|
||||
private (int, FileInfo) GetFileInfo(ResultConfiguration resultConfiguration, FilePath filePath)
|
||||
private (int, FileInfo) GetFileInfo(ResultSettings resultSettings, FilePath filePath)
|
||||
{
|
||||
FileInfo result;
|
||||
FileInfo fileInfo = new(filePath.FullName);
|
||||
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultConfiguration, filePath);
|
||||
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
|
||||
DateTime minimumDateTime = fileInfo.CreationTime < fileInfo.LastWriteTime ? fileInfo.CreationTime : fileInfo.LastWriteTime;
|
||||
int fileInfoMinimumYear = minimumDateTime.Year < resultConfiguration.EpicYear ? resultConfiguration.EpicYear : minimumDateTime.Year;
|
||||
result = new(Path.Combine(_FileGroups[fileInfoMinimumYear][_MetadataConfiguration.ResultConfiguration.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
|
||||
int fileInfoMinimumYear = minimumDateTime.Year < resultSettings.EpicYear ? resultSettings.EpicYear : minimumDateTime.Year;
|
||||
result = new(Path.Combine(_FileGroups[fileInfoMinimumYear][_ResultSettings.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
|
||||
return (fileInfoMinimumYear, result);
|
||||
}
|
||||
|
||||
private (int, string) GetJsonFile(ResultConfiguration resultConfiguration, FilePath filePath, ExifDirectory exifDirectory)
|
||||
private (int, string) GetJsonFile(ResultSettings resultSettings, FilePath filePath, ExifDirectory exifDirectory)
|
||||
{
|
||||
string? result;
|
||||
DateTime? dateTime;
|
||||
dateTime = IDate.GetDateTimeOriginal(exifDirectory);
|
||||
dateTime ??= IDate.GetMinimum(exifDirectory);
|
||||
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultConfiguration, filePath);
|
||||
int exifYear = dateTime.Value.Year < resultConfiguration.EpicYear ? resultConfiguration.EpicYear : dateTime.Value.Year;
|
||||
result = Path.Combine(_FileGroups[exifYear][_MetadataConfiguration.ResultConfiguration.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json");
|
||||
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
|
||||
int exifYear = dateTime.Value.Year < resultSettings.EpicYear ? resultSettings.EpicYear : dateTime.Value.Year;
|
||||
result = Path.Combine(_FileGroups[exifYear][_ResultSettings.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json");
|
||||
return (exifYear, result);
|
||||
}
|
||||
|
||||
@ -64,21 +66,21 @@ public class A_Metadata
|
||||
return (json, result);
|
||||
}
|
||||
|
||||
public (FileInfo, ExifDirectory) GetMetadataCollection(MetadataConfiguration metadataConfiguration, FilePath filePath, DeterministicHashCode deterministicHashCode)
|
||||
public (FileInfo, ExifDirectory) GetMetadataCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath)
|
||||
{
|
||||
ExifDirectory? result;
|
||||
(int fileInfoMinimumYear, FileInfo fileInfo) = GetFileInfo(metadataConfiguration.ResultConfiguration, filePath);
|
||||
if (_MetadataConfiguration.ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
|
||||
(int fileInfoMinimumYear, FileInfo fileInfo) = GetFileInfo(resultSettings, filePath);
|
||||
if (_MetadataSettings.ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
|
||||
{
|
||||
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
|
||||
fileInfo.Refresh();
|
||||
}
|
||||
if (_MetadataConfiguration.ForceMetadataLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
|
||||
if (_MetadataSettings.ForceMetadataLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
|
||||
{
|
||||
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
|
||||
fileInfo.Refresh();
|
||||
}
|
||||
if (_MetadataConfiguration.PropertiesChangedForMetadata)
|
||||
if (_MetadataSettings.PropertiesChangedForMetadata)
|
||||
result = null;
|
||||
else if (!fileInfo.Exists)
|
||||
result = null;
|
||||
@ -101,8 +103,9 @@ public class A_Metadata
|
||||
if (result is null)
|
||||
{
|
||||
string json;
|
||||
result = Exif.GetExifDirectory(filePath, deterministicHashCode);
|
||||
(int exifYear, string jsonFile) = GetJsonFile(metadataConfiguration.ResultConfiguration, filePath, result);
|
||||
const PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName = null;
|
||||
result = Exif.GetExifDirectory(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
|
||||
(int exifYear, string jsonFile) = GetJsonFile(_ResultSettings, filePath, result);
|
||||
if (exifYear == fileInfoMinimumYear)
|
||||
json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
|
||||
else
|
||||
@ -122,7 +125,7 @@ public class A_Metadata
|
||||
}
|
||||
}
|
||||
}
|
||||
if (IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null) && _MetadataConfiguration.ForceMetadataLastWriteTimeToCreationTime)
|
||||
if (IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null) && _MetadataSettings.ForceMetadataLastWriteTimeToCreationTime)
|
||||
{
|
||||
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
|
||||
fileInfo.Refresh();
|
||||
|
@ -62,7 +62,7 @@ internal static class Base
|
||||
results.Add(value);
|
||||
}
|
||||
}
|
||||
return new(results);
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
}
|
@ -28,22 +28,24 @@ internal abstract class Exif
|
||||
private static Shared.Models.AviDirectory[] GetAviDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.AviDirectory> results = [];
|
||||
Shared.Models.AviDirectory aviDirectory;
|
||||
IEnumerable<MetadataExtractor.Formats.Avi.AviDirectory> aviDirectories = directories.OfType<MetadataExtractor.Formats.Avi.AviDirectory>();
|
||||
foreach (MetadataExtractor.Formats.Avi.AviDirectory aviDirectory in aviDirectories)
|
||||
foreach (MetadataExtractor.Formats.Avi.AviDirectory a in aviDirectories)
|
||||
{
|
||||
if (aviDirectory.Tags.Count == 0)
|
||||
if (a.Tags.Count == 0)
|
||||
continue;
|
||||
DateTime? dateTimeOriginal;
|
||||
string? duration = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration);
|
||||
string? height = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight);
|
||||
string? width = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagWidth);
|
||||
if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal, out DateTime checkDateTime))
|
||||
string? duration = a.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration);
|
||||
string? height = a.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight);
|
||||
string? width = a.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagWidth);
|
||||
if (a.TryGetDateTime(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal, out DateTime checkDateTime))
|
||||
dateTimeOriginal = checkDateTime;
|
||||
else
|
||||
dateTimeOriginal = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal));
|
||||
dateTimeOriginal = GetDateTime(a.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal));
|
||||
if (dateTimeOriginal is null && duration is null && height is null && width is null)
|
||||
continue;
|
||||
results.Add(new(dateTimeOriginal, duration, height, width));
|
||||
aviDirectory = new(dateTimeOriginal, duration, height, width);
|
||||
results.Add(aviDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -51,70 +53,71 @@ internal abstract class Exif
|
||||
private static Shared.Models.ExifDirectoryBase[] GetExifBaseDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.ExifDirectoryBase> results = [];
|
||||
Shared.Models.ExifDirectoryBase exifDirectoryBase;
|
||||
IEnumerable<ExifDirectoryBase> exifBaseDirectories = directories.OfType<ExifDirectoryBase>();
|
||||
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
|
||||
foreach (ExifDirectoryBase e in exifBaseDirectories)
|
||||
{
|
||||
if (exifDirectoryBase.Tags.Count == 0)
|
||||
if (e.Tags.Count == 0)
|
||||
continue;
|
||||
DateTime? dateTime;
|
||||
DateTime checkDateTime;
|
||||
DateTime? dateTimeOriginal;
|
||||
DateTime? dateTimeDigitized;
|
||||
string? aperture = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagAperture);
|
||||
string? applicationNotes = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagApplicationNotes);
|
||||
string? artist = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagArtist);
|
||||
string? bitsPerSample = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBitsPerSample);
|
||||
string? bodySerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBodySerialNumber);
|
||||
string? cameraOwnerName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCameraOwnerName);
|
||||
string? compressedAverageBitsPerPixel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompressedAverageBitsPerPixel);
|
||||
string? compression = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompression);
|
||||
string? copyright = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCopyright);
|
||||
string? documentName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagDocumentName);
|
||||
string? exifVersion = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExifVersion);
|
||||
string? exposureTime = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExposureTime);
|
||||
string? fileSource = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagFileSource);
|
||||
string? imageDescription = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageDescription);
|
||||
string? imageHeight = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageHeight);
|
||||
string? imageNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageNumber);
|
||||
string? imageUniqueId = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageUniqueId);
|
||||
string? imageWidth = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageWidth);
|
||||
string? isoSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagIsoSpeed);
|
||||
string? lensMake = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensMake);
|
||||
string? lensModel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensModel);
|
||||
string? lensSerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensSerialNumber);
|
||||
string? make = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMake);
|
||||
string? makerNote = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMakernote);
|
||||
string? model = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagModel);
|
||||
string? orientation = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagOrientation);
|
||||
int? orientationValue = orientation is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagOrientation);
|
||||
string? rating = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRating);
|
||||
string? ratingPercent = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRatingPercent);
|
||||
string? securityClassification = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSecurityClassification);
|
||||
string? shutterSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagShutterSpeed);
|
||||
string? software = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSoftware);
|
||||
string? timeZone = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZone);
|
||||
string? timeZoneDigitized = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneDigitized);
|
||||
string? timeZoneOriginal = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneOriginal);
|
||||
string? userComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagUserComment);
|
||||
string? winAuthor = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinAuthor);
|
||||
string? winComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinComment);
|
||||
string? winKeywords = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinKeywords);
|
||||
string? winSubject = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinSubject);
|
||||
string? winTitle = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinTitle);
|
||||
string? xResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagXResolution);
|
||||
string? yResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagYResolution);
|
||||
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTime, out checkDateTime))
|
||||
string? aperture = e.GetDescription(ExifDirectoryBase.TagAperture);
|
||||
string? applicationNotes = e.GetDescription(ExifDirectoryBase.TagApplicationNotes);
|
||||
string? artist = e.GetDescription(ExifDirectoryBase.TagArtist);
|
||||
string? bitsPerSample = e.GetDescription(ExifDirectoryBase.TagBitsPerSample);
|
||||
string? bodySerialNumber = e.GetDescription(ExifDirectoryBase.TagBodySerialNumber);
|
||||
string? cameraOwnerName = e.GetDescription(ExifDirectoryBase.TagCameraOwnerName);
|
||||
string? compressedAverageBitsPerPixel = e.GetDescription(ExifDirectoryBase.TagCompressedAverageBitsPerPixel);
|
||||
string? compression = e.GetDescription(ExifDirectoryBase.TagCompression);
|
||||
string? copyright = e.GetDescription(ExifDirectoryBase.TagCopyright);
|
||||
string? documentName = e.GetDescription(ExifDirectoryBase.TagDocumentName);
|
||||
string? exifVersion = e.GetDescription(ExifDirectoryBase.TagExifVersion);
|
||||
string? exposureTime = e.GetDescription(ExifDirectoryBase.TagExposureTime);
|
||||
string? fileSource = e.GetDescription(ExifDirectoryBase.TagFileSource);
|
||||
string? imageDescription = e.GetDescription(ExifDirectoryBase.TagImageDescription);
|
||||
string? imageHeight = e.GetDescription(ExifDirectoryBase.TagImageHeight);
|
||||
string? imageNumber = e.GetDescription(ExifDirectoryBase.TagImageNumber);
|
||||
string? imageUniqueId = e.GetDescription(ExifDirectoryBase.TagImageUniqueId);
|
||||
string? imageWidth = e.GetDescription(ExifDirectoryBase.TagImageWidth);
|
||||
string? isoSpeed = e.GetDescription(ExifDirectoryBase.TagIsoSpeed);
|
||||
string? lensMake = e.GetDescription(ExifDirectoryBase.TagLensMake);
|
||||
string? lensModel = e.GetDescription(ExifDirectoryBase.TagLensModel);
|
||||
string? lensSerialNumber = e.GetDescription(ExifDirectoryBase.TagLensSerialNumber);
|
||||
string? make = e.GetDescription(ExifDirectoryBase.TagMake);
|
||||
string? makerNote = e.GetDescription(ExifDirectoryBase.TagMakernote);
|
||||
string? model = e.GetDescription(ExifDirectoryBase.TagModel);
|
||||
string? orientation = e.GetDescription(ExifDirectoryBase.TagOrientation);
|
||||
int? orientationValue = orientation is null ? null : e.GetInt32(ExifDirectoryBase.TagOrientation);
|
||||
string? rating = e.GetDescription(ExifDirectoryBase.TagRating);
|
||||
string? ratingPercent = e.GetDescription(ExifDirectoryBase.TagRatingPercent);
|
||||
string? securityClassification = e.GetDescription(ExifDirectoryBase.TagSecurityClassification);
|
||||
string? shutterSpeed = e.GetDescription(ExifDirectoryBase.TagShutterSpeed);
|
||||
string? software = e.GetDescription(ExifDirectoryBase.TagSoftware);
|
||||
string? timeZone = e.GetDescription(ExifDirectoryBase.TagTimeZone);
|
||||
string? timeZoneDigitized = e.GetDescription(ExifDirectoryBase.TagTimeZoneDigitized);
|
||||
string? timeZoneOriginal = e.GetDescription(ExifDirectoryBase.TagTimeZoneOriginal);
|
||||
string? userComment = e.GetDescription(ExifDirectoryBase.TagUserComment);
|
||||
string? winAuthor = e.GetDescription(ExifDirectoryBase.TagWinAuthor);
|
||||
string? winComment = e.GetDescription(ExifDirectoryBase.TagWinComment);
|
||||
string? winKeywords = e.GetDescription(ExifDirectoryBase.TagWinKeywords);
|
||||
string? winSubject = e.GetDescription(ExifDirectoryBase.TagWinSubject);
|
||||
string? winTitle = e.GetDescription(ExifDirectoryBase.TagWinTitle);
|
||||
string? xResolution = e.GetDescription(ExifDirectoryBase.TagXResolution);
|
||||
string? yResolution = e.GetDescription(ExifDirectoryBase.TagYResolution);
|
||||
if (e.TryGetDateTime(ExifDirectoryBase.TagDateTime, out checkDateTime))
|
||||
dateTime = checkDateTime;
|
||||
else
|
||||
dateTime = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTime));
|
||||
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out checkDateTime))
|
||||
dateTime = GetDateTime(e.GetString(ExifDirectoryBase.TagDateTime));
|
||||
if (e.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out checkDateTime))
|
||||
dateTimeOriginal = checkDateTime;
|
||||
else
|
||||
dateTimeOriginal = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeOriginal));
|
||||
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out checkDateTime))
|
||||
dateTimeOriginal = GetDateTime(e.GetString(ExifDirectoryBase.TagDateTimeOriginal));
|
||||
if (e.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out checkDateTime))
|
||||
dateTimeDigitized = checkDateTime;
|
||||
else
|
||||
dateTimeDigitized = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized));
|
||||
dateTimeDigitized = GetDateTime(e.GetString(ExifDirectoryBase.TagDateTimeDigitized));
|
||||
if (userComment is not null && userComment.Length > 255)
|
||||
userComment = "...";
|
||||
if (aperture is null
|
||||
@ -164,52 +167,53 @@ internal abstract class Exif
|
||||
&& xResolution is not null
|
||||
&& yResolution is null)
|
||||
continue;
|
||||
results.Add(new(aperture,
|
||||
applicationNotes,
|
||||
artist,
|
||||
bitsPerSample,
|
||||
bodySerialNumber,
|
||||
cameraOwnerName,
|
||||
compressedAverageBitsPerPixel,
|
||||
compression,
|
||||
copyright,
|
||||
dateTime,
|
||||
dateTimeDigitized,
|
||||
dateTimeOriginal,
|
||||
documentName,
|
||||
exifVersion,
|
||||
exposureTime,
|
||||
fileSource,
|
||||
imageDescription,
|
||||
imageHeight,
|
||||
imageNumber,
|
||||
imageUniqueId,
|
||||
imageWidth,
|
||||
isoSpeed,
|
||||
lensMake,
|
||||
lensModel,
|
||||
lensSerialNumber,
|
||||
make,
|
||||
makerNote,
|
||||
model,
|
||||
orientation,
|
||||
orientationValue,
|
||||
rating,
|
||||
ratingPercent,
|
||||
securityClassification,
|
||||
shutterSpeed,
|
||||
software,
|
||||
timeZone,
|
||||
timeZoneDigitized,
|
||||
timeZoneOriginal,
|
||||
userComment,
|
||||
winAuthor,
|
||||
winComment,
|
||||
winKeywords,
|
||||
winSubject,
|
||||
winTitle,
|
||||
xResolution,
|
||||
yResolution));
|
||||
exifDirectoryBase = new(aperture,
|
||||
applicationNotes,
|
||||
artist,
|
||||
bitsPerSample,
|
||||
bodySerialNumber,
|
||||
cameraOwnerName,
|
||||
compressedAverageBitsPerPixel,
|
||||
compression,
|
||||
copyright,
|
||||
dateTime,
|
||||
dateTimeDigitized,
|
||||
dateTimeOriginal,
|
||||
documentName,
|
||||
exifVersion,
|
||||
exposureTime,
|
||||
fileSource,
|
||||
imageDescription,
|
||||
imageHeight,
|
||||
imageNumber,
|
||||
imageUniqueId,
|
||||
imageWidth,
|
||||
isoSpeed,
|
||||
lensMake,
|
||||
lensModel,
|
||||
lensSerialNumber,
|
||||
make,
|
||||
makerNote,
|
||||
model,
|
||||
orientation,
|
||||
orientationValue,
|
||||
rating,
|
||||
ratingPercent,
|
||||
securityClassification,
|
||||
shutterSpeed,
|
||||
software,
|
||||
timeZone,
|
||||
timeZoneDigitized,
|
||||
timeZoneOriginal,
|
||||
userComment,
|
||||
winAuthor,
|
||||
winComment,
|
||||
winKeywords,
|
||||
winSubject,
|
||||
winTitle,
|
||||
xResolution,
|
||||
yResolution);
|
||||
results.Add(exifDirectoryBase);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -217,23 +221,25 @@ internal abstract class Exif
|
||||
private static Shared.Models.FileMetadataDirectory[] GetFileMetadataDirectories(string file, IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.FileMetadataDirectory> results = [];
|
||||
Shared.Models.FileMetadataDirectory fileMetadataDirectory;
|
||||
IEnumerable<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory> fileMetadataDirectories = directories.OfType<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory>();
|
||||
foreach (MetadataExtractor.Formats.FileSystem.FileMetadataDirectory fileMetadataDirectory in fileMetadataDirectories)
|
||||
foreach (MetadataExtractor.Formats.FileSystem.FileMetadataDirectory f in fileMetadataDirectories)
|
||||
{
|
||||
if (fileMetadataDirectory.Tags.Count == 0)
|
||||
if (f.Tags.Count == 0)
|
||||
continue;
|
||||
DateTime? fileModifiedDate;
|
||||
string? fileName = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName);
|
||||
string? fileSize = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize);
|
||||
if (fileMetadataDirectory.TryGetDateTime(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate, out DateTime checkDateTime))
|
||||
string? fileName = f.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName);
|
||||
string? fileSize = f.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize);
|
||||
if (f.TryGetDateTime(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate, out DateTime checkDateTime))
|
||||
fileModifiedDate = checkDateTime;
|
||||
else
|
||||
fileModifiedDate = GetDateTime(fileMetadataDirectory.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate));
|
||||
fileModifiedDate = GetDateTime(f.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate));
|
||||
if (fileName is null || !file.EndsWith(fileName))
|
||||
throw new NotSupportedException($"!{file}.EndsWith({fileName})");
|
||||
if (fileModifiedDate is null && fileName is null && fileSize is null)
|
||||
continue;
|
||||
results.Add(new(fileModifiedDate, fileName, fileSize));
|
||||
fileMetadataDirectory = new(fileModifiedDate, fileName, fileSize);
|
||||
results.Add(fileMetadataDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -241,16 +247,18 @@ internal abstract class Exif
|
||||
private static Shared.Models.GifHeaderDirectory[] GetGifHeaderDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.GifHeaderDirectory> results = [];
|
||||
Shared.Models.GifHeaderDirectory gifHeaderDirectory;
|
||||
IEnumerable<MetadataExtractor.Formats.Gif.GifHeaderDirectory> gifHeaderDirectories = directories.OfType<MetadataExtractor.Formats.Gif.GifHeaderDirectory>();
|
||||
foreach (MetadataExtractor.Formats.Gif.GifHeaderDirectory gifHeaderDirectory in gifHeaderDirectories)
|
||||
foreach (MetadataExtractor.Formats.Gif.GifHeaderDirectory g in gifHeaderDirectories)
|
||||
{
|
||||
if (gifHeaderDirectory.Tags.Count == 0)
|
||||
if (g.Tags.Count == 0)
|
||||
continue;
|
||||
string? imageHeight = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight);
|
||||
string? imageWidth = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth);
|
||||
string? imageHeight = g.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight);
|
||||
string? imageWidth = g.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth);
|
||||
if (imageHeight is null && imageWidth is null)
|
||||
continue;
|
||||
results.Add(new(imageHeight, imageWidth));
|
||||
gifHeaderDirectory = new(imageHeight, imageWidth);
|
||||
results.Add(gifHeaderDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -258,29 +266,31 @@ internal abstract class Exif
|
||||
private static Shared.Models.GpsDirectory[] GetGpsDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.GpsDirectory> results = [];
|
||||
Shared.Models.GpsDirectory gpsDirectory;
|
||||
IEnumerable<GpsDirectory> gpsDirectories = directories.OfType<GpsDirectory>();
|
||||
foreach (GpsDirectory gpsDirectory in gpsDirectories)
|
||||
foreach (GpsDirectory g in gpsDirectories)
|
||||
{
|
||||
if (gpsDirectory.Tags.Count == 0)
|
||||
if (g.Tags.Count == 0)
|
||||
continue;
|
||||
DateTime? timeStamp;
|
||||
string? altitude = gpsDirectory.GetDescription(GpsDirectory.TagAltitude);
|
||||
string? latitude = gpsDirectory.GetDescription(GpsDirectory.TagLatitude);
|
||||
string? latitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLatitudeRef);
|
||||
string? longitude = gpsDirectory.GetDescription(GpsDirectory.TagLongitude);
|
||||
string? longitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLongitudeRef);
|
||||
if (gpsDirectory.TryGetDateTime(GpsDirectory.TagTimeStamp, out DateTime checkDateTime))
|
||||
string? altitude = g.GetDescription(GpsDirectory.TagAltitude);
|
||||
string? latitude = g.GetDescription(GpsDirectory.TagLatitude);
|
||||
string? latitudeRef = g.GetDescription(GpsDirectory.TagLatitudeRef);
|
||||
string? longitude = g.GetDescription(GpsDirectory.TagLongitude);
|
||||
string? longitudeRef = g.GetDescription(GpsDirectory.TagLongitudeRef);
|
||||
if (g.TryGetDateTime(GpsDirectory.TagTimeStamp, out DateTime checkDateTime))
|
||||
timeStamp = checkDateTime;
|
||||
else
|
||||
timeStamp = GetDateTime(gpsDirectory.GetString(GpsDirectory.TagTimeStamp));
|
||||
timeStamp = GetDateTime(g.GetString(GpsDirectory.TagTimeStamp));
|
||||
if (altitude is null && latitude is null && latitudeRef is null && longitude is null && longitudeRef is null && timeStamp is null)
|
||||
continue;
|
||||
results.Add(new(altitude,
|
||||
latitude,
|
||||
latitudeRef,
|
||||
longitude,
|
||||
longitudeRef,
|
||||
timeStamp));
|
||||
gpsDirectory = new(altitude,
|
||||
latitude,
|
||||
latitudeRef,
|
||||
longitude,
|
||||
longitudeRef,
|
||||
timeStamp);
|
||||
results.Add(gpsDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -288,16 +298,18 @@ internal abstract class Exif
|
||||
private static Shared.Models.JpegDirectory[] GetJpegDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.JpegDirectory> results = [];
|
||||
Shared.Models.JpegDirectory jpegDirectory;
|
||||
IEnumerable<MetadataExtractor.Formats.Jpeg.JpegDirectory> jpegDirectories = directories.OfType<MetadataExtractor.Formats.Jpeg.JpegDirectory>();
|
||||
foreach (MetadataExtractor.Formats.Jpeg.JpegDirectory jpegDirectory in jpegDirectories)
|
||||
foreach (MetadataExtractor.Formats.Jpeg.JpegDirectory j in jpegDirectories)
|
||||
{
|
||||
if (jpegDirectory.Tags.Count == 0)
|
||||
if (j.Tags.Count == 0)
|
||||
continue;
|
||||
string? imageHeight = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight);
|
||||
string? imageWidth = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth);
|
||||
string? imageHeight = j.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight);
|
||||
string? imageWidth = j.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth);
|
||||
if (imageHeight is null && imageWidth is null)
|
||||
continue;
|
||||
results.Add(new(imageHeight, imageWidth));
|
||||
jpegDirectory = new(imageHeight, imageWidth);
|
||||
results.Add(jpegDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -305,6 +317,7 @@ internal abstract class Exif
|
||||
private static Shared.Models.MakernoteDirectory[] GetMakernoteDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.MakernoteDirectory> results = [];
|
||||
Shared.Models.MakernoteDirectory makernoteDirectory;
|
||||
IEnumerable<AppleMakernoteDirectory> appleMakernoteDirectories = directories.OfType<AppleMakernoteDirectory>();
|
||||
foreach (AppleMakernoteDirectory appleMakernoteDirectory in appleMakernoteDirectories)
|
||||
{
|
||||
@ -315,7 +328,8 @@ internal abstract class Exif
|
||||
string? qualityAndFileFormat = null;
|
||||
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
|
||||
continue;
|
||||
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
|
||||
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat);
|
||||
results.Add(makernoteDirectory);
|
||||
}
|
||||
IEnumerable<CanonMakernoteDirectory> canonMakernoteDirectories = directories.OfType<CanonMakernoteDirectory>();
|
||||
foreach (CanonMakernoteDirectory canonMakernoteDirectory in canonMakernoteDirectories)
|
||||
@ -327,7 +341,8 @@ internal abstract class Exif
|
||||
string? qualityAndFileFormat = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.CameraSettings.TagQuality);
|
||||
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
|
||||
continue;
|
||||
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
|
||||
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat);
|
||||
results.Add(makernoteDirectory);
|
||||
}
|
||||
IEnumerable<NikonType2MakernoteDirectory> nikonType2MakernoteDirectories = directories.OfType<NikonType2MakernoteDirectory>();
|
||||
foreach (NikonType2MakernoteDirectory nikonType2MakernoteDirectory in nikonType2MakernoteDirectories)
|
||||
@ -339,7 +354,8 @@ internal abstract class Exif
|
||||
string? qualityAndFileFormat = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagQualityAndFileFormat);
|
||||
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
|
||||
continue;
|
||||
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
|
||||
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat);
|
||||
results.Add(makernoteDirectory);
|
||||
}
|
||||
IEnumerable<OlympusMakernoteDirectory> olympusMakernoteDirectories = directories.OfType<OlympusMakernoteDirectory>();
|
||||
foreach (OlympusMakernoteDirectory olympusMakernoteDirectory in olympusMakernoteDirectories)
|
||||
@ -351,7 +367,8 @@ internal abstract class Exif
|
||||
string? qualityAndFileFormat = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagJpegQuality);
|
||||
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
|
||||
continue;
|
||||
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
|
||||
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat);
|
||||
results.Add(makernoteDirectory);
|
||||
}
|
||||
IEnumerable<PanasonicMakernoteDirectory> panasonicMakernoteDirectories = directories.OfType<PanasonicMakernoteDirectory>();
|
||||
foreach (PanasonicMakernoteDirectory panasonicMakernoteDirectory in panasonicMakernoteDirectories)
|
||||
@ -363,7 +380,8 @@ internal abstract class Exif
|
||||
string? qualityAndFileFormat = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagQualityMode);
|
||||
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
|
||||
continue;
|
||||
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
|
||||
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat);
|
||||
results.Add(makernoteDirectory);
|
||||
}
|
||||
IEnumerable<SamsungType2MakernoteDirectory> samsungType2MakernoteDirectories = directories.OfType<SamsungType2MakernoteDirectory>();
|
||||
foreach (SamsungType2MakernoteDirectory samsungType2MakernoteDirectory in samsungType2MakernoteDirectories)
|
||||
@ -375,7 +393,8 @@ internal abstract class Exif
|
||||
string? qualityAndFileFormat = null;
|
||||
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
|
||||
continue;
|
||||
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
|
||||
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat);
|
||||
results.Add(makernoteDirectory);
|
||||
}
|
||||
IEnumerable<SonyType6MakernoteDirectory> sonyType6MakernoteDirectories = directories.OfType<SonyType6MakernoteDirectory>();
|
||||
foreach (SonyType6MakernoteDirectory sonyType6MakernoteDirectory in sonyType6MakernoteDirectories)
|
||||
@ -387,7 +406,8 @@ internal abstract class Exif
|
||||
string? qualityAndFileFormat = null;
|
||||
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
|
||||
continue;
|
||||
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
|
||||
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat);
|
||||
results.Add(makernoteDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -395,16 +415,18 @@ internal abstract class Exif
|
||||
private static Shared.Models.PhotoshopDirectory[] GetPhotoshopDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.PhotoshopDirectory> results = [];
|
||||
Shared.Models.PhotoshopDirectory photoshopDirectory;
|
||||
IEnumerable<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory> photoshopDirectories = directories.OfType<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory>();
|
||||
foreach (MetadataExtractor.Formats.Photoshop.PhotoshopDirectory photoshopDirectory in photoshopDirectories)
|
||||
foreach (MetadataExtractor.Formats.Photoshop.PhotoshopDirectory p in photoshopDirectories)
|
||||
{
|
||||
if (photoshopDirectory.Tags.Count == 0)
|
||||
if (p.Tags.Count == 0)
|
||||
continue;
|
||||
string? jpegQuality = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality);
|
||||
string? url = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl);
|
||||
string? jpegQuality = p.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality);
|
||||
string? url = p.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl);
|
||||
if (jpegQuality is null && url is null)
|
||||
continue;
|
||||
results.Add(new(jpegQuality, url));
|
||||
photoshopDirectory = new(jpegQuality, url);
|
||||
results.Add(photoshopDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -412,17 +434,19 @@ internal abstract class Exif
|
||||
private static Shared.Models.PngDirectory[] GetPngDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.PngDirectory> results = [];
|
||||
Shared.Models.PngDirectory pngDirectory;
|
||||
IEnumerable<MetadataExtractor.Formats.Png.PngDirectory> pngDirectories = directories.OfType<MetadataExtractor.Formats.Png.PngDirectory>();
|
||||
foreach (MetadataExtractor.Formats.Png.PngDirectory pngDirectory in pngDirectories)
|
||||
foreach (MetadataExtractor.Formats.Png.PngDirectory p in pngDirectories)
|
||||
{
|
||||
if (pngDirectory.Tags.Count == 0)
|
||||
if (p.Tags.Count == 0)
|
||||
continue;
|
||||
string? imageHeight = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageHeight);
|
||||
string? imageWidth = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth);
|
||||
string? textualData = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagTextualData);
|
||||
string? imageHeight = p.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageHeight);
|
||||
string? imageWidth = p.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth);
|
||||
string? textualData = p.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagTextualData);
|
||||
if (imageHeight is null && imageWidth is null && textualData is null)
|
||||
continue;
|
||||
results.Add(new(imageHeight, imageWidth, textualData));
|
||||
pngDirectory = new(imageHeight, imageWidth, textualData);
|
||||
results.Add(pngDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -430,19 +454,21 @@ internal abstract class Exif
|
||||
private static Shared.Models.QuickTimeMovieHeaderDirectory[] GetQuickTimeMovieHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.QuickTimeMovieHeaderDirectory> results = [];
|
||||
Shared.Models.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory;
|
||||
IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory> quickTimeMovieHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory>();
|
||||
foreach (MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in quickTimeMovieHeaderDirectories)
|
||||
foreach (MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory q in quickTimeMovieHeaderDirectories)
|
||||
{
|
||||
if (quickTimeMovieHeaderDirectory.Tags.Count == 0)
|
||||
if (q.Tags.Count == 0)
|
||||
continue;
|
||||
DateTime? created;
|
||||
if (quickTimeMovieHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime))
|
||||
if (q.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime))
|
||||
created = checkDateTime;
|
||||
else
|
||||
created = GetDateTime(quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated));
|
||||
created = GetDateTime(q.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated));
|
||||
if (created is null)
|
||||
continue;
|
||||
results.Add(new(created));
|
||||
quickTimeMovieHeaderDirectory = new(created);
|
||||
results.Add(quickTimeMovieHeaderDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -450,19 +476,21 @@ internal abstract class Exif
|
||||
private static Shared.Models.QuickTimeTrackHeaderDirectory[] GetQuickTimeTrackHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.QuickTimeTrackHeaderDirectory> results = [];
|
||||
Shared.Models.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory;
|
||||
IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory> quickTimeTrackHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory>();
|
||||
foreach (MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in quickTimeTrackHeaderDirectories)
|
||||
foreach (MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory q in quickTimeTrackHeaderDirectories)
|
||||
{
|
||||
if (quickTimeTrackHeaderDirectory.Tags.Count == 0)
|
||||
if (q.Tags.Count == 0)
|
||||
continue;
|
||||
DateTime? created;
|
||||
if (quickTimeTrackHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime))
|
||||
if (q.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime))
|
||||
created = checkDateTime;
|
||||
else
|
||||
created = GetDateTime(quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated));
|
||||
created = GetDateTime(q.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated));
|
||||
if (created is null)
|
||||
continue;
|
||||
results.Add(new(created));
|
||||
quickTimeTrackHeaderDirectory = new(created);
|
||||
results.Add(quickTimeTrackHeaderDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
@ -470,21 +498,23 @@ internal abstract class Exif
|
||||
private static Shared.Models.WebPDirectory[] GetWebPDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
List<Shared.Models.WebPDirectory> results = [];
|
||||
Shared.Models.WebPDirectory webPDirectory;
|
||||
IEnumerable<MetadataExtractor.Formats.WebP.WebPDirectory> webPDirectories = directories.OfType<MetadataExtractor.Formats.WebP.WebPDirectory>();
|
||||
foreach (MetadataExtractor.Formats.WebP.WebPDirectory webPDirectory in webPDirectories)
|
||||
foreach (MetadataExtractor.Formats.WebP.WebPDirectory w in webPDirectories)
|
||||
{
|
||||
if (webPDirectory.Tags.Count == 0)
|
||||
if (w.Tags.Count == 0)
|
||||
continue;
|
||||
string? imageHeight = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight);
|
||||
string? imageWidth = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth);
|
||||
string? imageHeight = w.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight);
|
||||
string? imageWidth = w.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth);
|
||||
if (imageHeight is null && imageWidth is null)
|
||||
continue;
|
||||
results.Add(new(imageHeight, imageWidth));
|
||||
webPDirectory = new(imageHeight, imageWidth);
|
||||
results.Add(webPDirectory);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
private static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, Shared.Models.DeterministicHashCode deterministicHashCode, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
private static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, Shared.Models.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories)
|
||||
{
|
||||
Shared.Models.ExifDirectory result;
|
||||
Shared.Models.AviDirectory[] aviDirectories = GetAviDirectories(directories);
|
||||
@ -500,15 +530,16 @@ internal abstract class Exif
|
||||
Shared.Models.QuickTimeMovieHeaderDirectory[] quickTimeMovieHeaderDirectories = GetQuickTimeMovieHeaderDirectoryDirectories(directories);
|
||||
Shared.Models.QuickTimeTrackHeaderDirectory[] quickTimeTrackHeaderDirectories = GetQuickTimeTrackHeaderDirectoryDirectories(directories);
|
||||
result = new(aviDirectories,
|
||||
null,
|
||||
exifBaseDirectories,
|
||||
fileMetadataDirectories,
|
||||
filePath,
|
||||
gifHeaderDirectories,
|
||||
gpsDirectories,
|
||||
size?.Height,
|
||||
deterministicHashCode.Id ?? filePath.Id,
|
||||
jpegDirectories,
|
||||
MakernoteDirectories,
|
||||
filePath.Name,
|
||||
personKeyFormattedAndKeyTicksAndDisplayDirectoryName,
|
||||
photoshopDirectories,
|
||||
pngDirectories,
|
||||
quickTimeMovieHeaderDirectories,
|
||||
@ -518,7 +549,7 @@ internal abstract class Exif
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath, Shared.Models.DeterministicHashCode deterministicHashCode)
|
||||
internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath, Shared.Models.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName)
|
||||
{
|
||||
Shared.Models.ExifDirectory result;
|
||||
System.Drawing.Size? size;
|
||||
@ -527,7 +558,7 @@ internal abstract class Exif
|
||||
catch (Exception)
|
||||
{ size = null; }
|
||||
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName);
|
||||
result = Covert(filePath, deterministicHashCode, size, directories);
|
||||
result = Covert(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName, size, directories);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,17 @@ internal static class Face
|
||||
result = pngDirectory.TextualData[artist.Length..];
|
||||
break;
|
||||
}
|
||||
if (result is null)
|
||||
{
|
||||
const string author = "Author:";
|
||||
foreach (PngDirectory pngDirectory in pngDirectories)
|
||||
{
|
||||
if (pngDirectory.TextualData is null || !pngDirectory.TextualData.StartsWith(author))
|
||||
continue;
|
||||
result = pngDirectory.TextualData[author.Length..];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -28,42 +28,39 @@ internal static class Get
|
||||
}
|
||||
fileHolders.Add(fileHolder);
|
||||
}
|
||||
return new(results);
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
internal static Action<string> SetExifDirectoryCollection(IRename rename, IRenameConfiguration renameConfiguration, A_Metadata metadata, List<string> distinct, List<(bool, FilePath, FileInfo, ExifDirectory, ReadOnlyCollection<FileHolder>)> collection)
|
||||
internal static Action<string> SetExifDirectoryCollection(IRename rename, ResultSettings resultSettings, MetadataSettings metadataSettings, IRenameSettings renameSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups)
|
||||
{
|
||||
return file =>
|
||||
{
|
||||
rename.Tick();
|
||||
FileInfo fileInfo;
|
||||
ExifDirectory exifDirectory;
|
||||
DeterministicHashCode deterministicHashCode;
|
||||
MetadataGroup metadataGroup;
|
||||
FileHolder fileHolder = FileHolder.Get(file);
|
||||
bool fastForwardMovingPictureExpertsGroupUsed;
|
||||
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
|
||||
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
|
||||
FilePath filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index: null);
|
||||
FilePath filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
string key = $"{Path.Combine(fileHolder.DirectoryFullPath ?? throw new NotSupportedException(), fileHolder.NameWithoutExtension)}";
|
||||
if (distinct.Contains(key))
|
||||
throw new NotSupportedException("Turn off parallelism when sidecar files are present!");
|
||||
if (!renameConfiguration.SkipIdFiles || filePath.Id is null || (!filePath.IsIntelligentIdFormat && filePath.SortOrder is not null))
|
||||
if (!renameSettings.SkipIdFiles || filePath.Id is null || (!filePath.IsIntelligentIdFormat && filePath.SortOrder is not null))
|
||||
{
|
||||
if (filePath.Id is not null)
|
||||
{
|
||||
fastForwardMovingPictureExpertsGroupFiles = null;
|
||||
deterministicHashCode = new(null, filePath.Id, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(renameConfiguration, filePath);
|
||||
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(renameConfiguration.MetadataConfiguration, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index: null);
|
||||
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(fastForwardMovingPictureExpertsGroupFilePath);
|
||||
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(renameSettings, filePath);
|
||||
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(resultSettings, metadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index: null);
|
||||
}
|
||||
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
|
||||
(fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode);
|
||||
lock (collection)
|
||||
collection.Add(new(fastForwardMovingPictureExpertsGroupUsed, filePath, fileInfo, exifDirectory, new([])));
|
||||
(fileInfo, exifDirectory) = metadata.GetMetadataCollection(resultSettings, metadataSettings, filePath);
|
||||
metadataGroup = new(fastForwardMovingPictureExpertsGroupUsed, filePath, fileInfo, exifDirectory, new([]));
|
||||
lock (metadataGroups)
|
||||
metadataGroups.Add(metadataGroup);
|
||||
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
|
||||
{
|
||||
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
|
||||
|
@ -17,10 +17,10 @@ public interface IMetadata
|
||||
Meters
|
||||
}
|
||||
|
||||
ExifDirectory TestStatic_GetExifDirectory(FilePath filePath, DeterministicHashCode deterministicHashCode) =>
|
||||
GetExifDirectory(filePath, deterministicHashCode);
|
||||
static ExifDirectory GetExifDirectory(FilePath filePath, DeterministicHashCode deterministicHashCode) =>
|
||||
Exif.GetExifDirectory(filePath, deterministicHashCode);
|
||||
ExifDirectory TestStatic_GetExifDirectory(FilePath filePath, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) =>
|
||||
GetExifDirectory(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
|
||||
static ExifDirectory GetExifDirectory(FilePath filePath, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) =>
|
||||
Exif.GetExifDirectory(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
|
||||
|
||||
string? TestStatic_GetMaker(ExifDirectory? exifDirectory) =>
|
||||
GetMaker(exifDirectory);
|
||||
@ -57,10 +57,10 @@ public interface IMetadata
|
||||
static double? GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) =>
|
||||
GPS.GetDistance(originLatitude, originLongitude, destinationLatitude, destinationLongitude, decimalPlaces, distanceUnit);
|
||||
|
||||
Action<string> TestStatic_SetExifDirectoryCollection(IRename rename, IRenameConfiguration renameConfiguration, A_Metadata metadata, List<string> distinct, List<(bool, FilePath, FileInfo, ExifDirectory, ReadOnlyCollection<FileHolder>)> collection) =>
|
||||
SetExifDirectoryCollection(rename, renameConfiguration, metadata, distinct, collection);
|
||||
static Action<string> SetExifDirectoryCollection(IRename rename, IRenameConfiguration renameConfiguration, A_Metadata metadata, List<string> distinct, List<(bool, FilePath, FileInfo, ExifDirectory, ReadOnlyCollection<FileHolder>)> collection) =>
|
||||
Get.SetExifDirectoryCollection(rename, renameConfiguration, metadata, distinct, collection);
|
||||
Action<string> TestStatic_SetExifDirectoryCollection(IRename rename, ResultSettings resultSettings, MetadataSettings metadataSettings, IRenameSettings renameSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
|
||||
SetExifDirectoryCollection(rename, resultSettings, metadataSettings, renameSettings, metadata, distinct, metadataGroups);
|
||||
static Action<string> SetExifDirectoryCollection(IRename rename, ResultSettings resultSettings, MetadataSettings metadataSettings, IRenameSettings renameSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
|
||||
Get.SetExifDirectoryCollection(rename, resultSettings, metadataSettings, renameSettings, metadata, distinct, metadataGroups);
|
||||
|
||||
ReadOnlyDictionary<string, List<FileHolder>> TestStatic_GetKeyValuePairs(IEnumerable<string> files) =>
|
||||
GetKeyValuePairs(files);
|
||||
|
44
People/AA.People.csproj
Normal file
44
People/AA.People.csproj
Normal file
@ -0,0 +1,44 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>library</OutputType>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PackageId>Phares.AA.People</PackageId>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<Version>9.0.100.0</Version>
|
||||
<Authors>Mike Phares</Authors>
|
||||
<Company>Phares</Company>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
|
||||
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
|
||||
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsWindows)'=='true'">
|
||||
<DefineConstants>Windows</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsOSX)'=='true'">
|
||||
<DefineConstants>OSX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(IsLinux)'=='true'">
|
||||
<DefineConstants>Linux</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
|
||||
<SupportedPlatform Include="browser" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Metadata\AA.Metadata.csproj" />
|
||||
<ProjectReference Include="..\Shared\AA.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
3
People/Models/A2_People.cs
Normal file
3
People/Models/A2_People.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace View_by_Distance.Metadata.Models;
|
||||
|
||||
public class A2_People() { }
|
40
People/Models/Stateless/Methods/IPeople.cs
Normal file
40
People/Models/Stateless/Methods/IPeople.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
|
||||
namespace View_by_Distance.People.Models.Stateless.Methods;
|
||||
|
||||
public interface IPeople
|
||||
{
|
||||
|
||||
ReadOnlyCollection<long> TestStatic_GetPersonKeys(ReadOnlyCollection<PersonContainer> personContainers) =>
|
||||
GetPersonKeys(personContainers);
|
||||
static ReadOnlyCollection<long> GetPersonKeys(ReadOnlyCollection<PersonContainer> personContainers) =>
|
||||
People.GetPersonKeys(personContainers);
|
||||
|
||||
ReadOnlyCollection<PersonContainer> TestStatic_GetPersonContainers(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings) =>
|
||||
GetPersonContainers(resultSettings, metadataSettings, peopleSettings, compareSettings);
|
||||
static ReadOnlyCollection<PersonContainer> GetPersonContainers(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings) =>
|
||||
People.GetPersonContainers(resultSettings, metadataSettings, peopleSettings, compareSettings);
|
||||
|
||||
string? TestStatic_VerifyAge(char numberSign, string personDisplayDirectory, string? minusOne, string personDisplayDirectoryName, int? approximateYears, List<PersonKeyFormattedAndPersonBirthday> personKeyFormattedAndPersonBirthdayCollection) =>
|
||||
VerifyAge(numberSign, personDisplayDirectory, minusOne, personDisplayDirectoryName, approximateYears, personKeyFormattedAndPersonBirthdayCollection);
|
||||
static string? VerifyAge(char numberSign, string personDisplayDirectory, string? minusOne, string personDisplayDirectoryName, int? approximateYears, List<PersonKeyFormattedAndPersonBirthday> personKeyFormattedAndPersonBirthdayCollection) =>
|
||||
People.VerifyAge(numberSign, personDisplayDirectory, minusOne, personDisplayDirectoryName, approximateYears, personKeyFormattedAndPersonBirthdayCollection);
|
||||
|
||||
static ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> TestStatic_GetJLinkDirectories(ResultSettings resultSettings, PeopleSettings peopleSettings) =>
|
||||
GetJLinkDirectories(resultSettings, peopleSettings);
|
||||
static ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> GetJLinkDirectories(ResultSettings resultSettings, PeopleSettings peopleSettings) =>
|
||||
People.GetJLinkDirectories(resultSettings, peopleSettings);
|
||||
|
||||
static ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> TestStatic_GetJLinkResolvedDirectories(ResultSettings resultSettings, PeopleSettings peopleSettings) =>
|
||||
GetJLinkResolvedDirectories(resultSettings, peopleSettings);
|
||||
static ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> GetJLinkResolvedDirectories(ResultSettings resultSettings, PeopleSettings peopleSettings) =>
|
||||
People.GetJLinkResolvedDirectories(resultSettings, peopleSettings);
|
||||
|
||||
static ReadOnlyCollections TestStatic_GetReadOnlyCollections(ResultSettings resultSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ReadOnlyCollection<PersonContainer> personContainers, ReadOnlyCollection<long> personKeys) =>
|
||||
GetReadOnlyCollections(resultSettings, peopleSettings, distanceSettings, compareSettings, personContainers, personKeys);
|
||||
static ReadOnlyCollections GetReadOnlyCollections(ResultSettings resultSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ReadOnlyCollection<PersonContainer> personContainers, ReadOnlyCollection<long> personKeys) =>
|
||||
ReadOnlyCollectionsLogic.GetReadOnlyCollections(resultSettings, peopleSettings, distanceSettings, compareSettings, personContainers, personKeys);
|
||||
|
||||
}
|
558
People/Models/Stateless/People.cs
Normal file
558
People/Models/Stateless/People.cs
Normal file
@ -0,0 +1,558 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using View_by_Distance.Metadata.Models;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
using WindowsShortcutFactory;
|
||||
|
||||
namespace View_by_Distance.People.Models.Stateless.Methods;
|
||||
|
||||
internal static class People
|
||||
{
|
||||
|
||||
private record RecordA(List<string?> Changes, List<PersonContainer> PersonContainers);
|
||||
private record RecordB(string Directory, string DirectoryName, string DirectoryNameSplitFirst);
|
||||
|
||||
internal static List<PersonKeyFormattedAndPersonBirthday> GetPersonBirthdays(string personBirthdayFormat, string[] personKeyDirectories, string personDisplayDirectoryName)
|
||||
{
|
||||
List<PersonKeyFormattedAndPersonBirthday> results = [];
|
||||
string personKeyFormatted;
|
||||
PersonBirthday? personBirthday;
|
||||
PersonKeyFormattedAndPersonBirthday personKeyFormattedAndPersonBirthday;
|
||||
foreach (string personKeyDirectory in personKeyDirectories)
|
||||
{
|
||||
personKeyFormatted = Path.GetFileName(personKeyDirectory);
|
||||
if (DateTime.TryParseExact(personKeyFormatted, "MM.dd.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime birthday))
|
||||
continue;
|
||||
personBirthday = IPersonBirthday.GetPersonBirthday(personBirthdayFormat, personKeyFormatted);
|
||||
if (personBirthday is null)
|
||||
continue;
|
||||
if (!IPersonBirthday.IsCounterPersonBirthday(personBirthday) && ((!personKeyDirectory.Contains('#') && (personDisplayDirectoryName.Contains('~') || personDisplayDirectoryName.Contains('#'))) || (personKeyDirectory.Contains('#') && !personDisplayDirectoryName.Contains('#'))))
|
||||
throw new NotSupportedException();
|
||||
personKeyFormattedAndPersonBirthday = new(personKeyFormatted, personBirthday);
|
||||
results.Add(personKeyFormattedAndPersonBirthday);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static List<PersonContainer> GetPersonContainersCollections(ResultSettings resultSettings, MetadataSettings metadataSettings, PersonDirectory personDirectory, char numberSign, string personDisplayDirectory, string personDisplayDirectoryName, int? approximateYears, List<PersonKeyFormattedAndPersonBirthday> personKeyFormattedAndPersonBirthdayCollection)
|
||||
{
|
||||
List<PersonContainer> results = [];
|
||||
long personKey;
|
||||
string[] files;
|
||||
FilePath filePath;
|
||||
const int zero = 0;
|
||||
string personKeyDirectory;
|
||||
FileHolder fileHolder;
|
||||
PersonContainer personContainer;
|
||||
PersonBirthday[] orderedPersonBirthdays;
|
||||
List<FilePath> personDisplayDirectoryAllFilePaths = GetFilePaths(resultSettings, metadataSettings, personDisplayDirectory);
|
||||
foreach (PersonKeyFormattedAndPersonBirthday personKeyFormattedAndPersonBirthday in personKeyFormattedAndPersonBirthdayCollection)
|
||||
{
|
||||
orderedPersonBirthdays = (from l in personKeyFormattedAndPersonBirthdayCollection where !l.PersonKeyFormatted.Contains(numberSign) orderby l.PersonBirthday.Value.Ticks descending select l.PersonBirthday).ToArray();
|
||||
if (orderedPersonBirthdays.Length == 0)
|
||||
personKey = personKeyFormattedAndPersonBirthdayCollection[zero].PersonBirthday.Value.Ticks;
|
||||
else
|
||||
{
|
||||
if (personKeyFormattedAndPersonBirthday.PersonKeyFormatted.Contains(numberSign))
|
||||
continue;
|
||||
personKey = orderedPersonBirthdays[zero].Value.Ticks;
|
||||
}
|
||||
personKeyDirectory = Path.Combine(personDisplayDirectory, personKeyFormattedAndPersonBirthday.PersonKeyFormatted);
|
||||
files = Directory.GetFiles(personKeyDirectory, "*", SearchOption.AllDirectories);
|
||||
if (files.Length == 0)
|
||||
continue;
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (!file.EndsWith(".rel"))
|
||||
continue;
|
||||
fileHolder = FileHolder.Get(file);
|
||||
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
personDisplayDirectoryAllFilePaths.Add(filePath);
|
||||
}
|
||||
personContainer = new(approximateYears, orderedPersonBirthdays, new(personDisplayDirectoryAllFilePaths), personDisplayDirectoryName, personKey, personDirectory);
|
||||
results.Add(personContainer);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static List<FilePath> GetFilePaths(ResultSettings resultSettings, MetadataSettings metadataSettings, string personDisplayDirectory)
|
||||
{
|
||||
List<FilePath> results = [];
|
||||
string[] files;
|
||||
string checkFile;
|
||||
FilePath filePath;
|
||||
string directoryName;
|
||||
FileHolder fileHolder;
|
||||
List<string> distinct = [];
|
||||
string fileNameWithoutExtension;
|
||||
string personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory);
|
||||
files = Directory.GetFiles(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string file in files)
|
||||
{
|
||||
fileHolder = FileHolder.Get(file);
|
||||
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
results.Add(filePath);
|
||||
}
|
||||
string[] directories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string directory in directories)
|
||||
{
|
||||
directoryName = Path.GetFileName(directory);
|
||||
files = Directory.GetFiles(directory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string file in files)
|
||||
{
|
||||
fileHolder = FileHolder.Get(file);
|
||||
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
if (filePath.ExtensionLowered is not ".json" and not ".pged")
|
||||
{
|
||||
results.Add(filePath);
|
||||
continue;
|
||||
}
|
||||
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
|
||||
if (string.IsNullOrEmpty(fileNameWithoutExtension) || string.IsNullOrEmpty(personDisplayDirectoryName))
|
||||
continue;
|
||||
else if (fileNameWithoutExtension.Length == 1 && fileNameWithoutExtension[0] == personDisplayDirectoryName[0])
|
||||
{
|
||||
if (distinct.Contains(file))
|
||||
throw new NotSupportedException($"Move / Delete <{file}>");
|
||||
distinct.Add(file);
|
||||
}
|
||||
else if (fileNameWithoutExtension != directoryName)
|
||||
{
|
||||
checkFile = Path.Combine(directory, $"{fileNameWithoutExtension}{filePath.ExtensionLowered}");
|
||||
if (!File.Exists(checkFile))
|
||||
{
|
||||
File.Move(file, checkFile);
|
||||
fileHolder = FileHolder.Get(checkFile);
|
||||
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
results.Add(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkFile = Path.Combine(directory, $"{fileNameWithoutExtension}.txt");
|
||||
if (File.Exists(checkFile))
|
||||
File.Delete(checkFile);
|
||||
File.Move(file, checkFile);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
results.Add(filePath);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static List<FilePath> GetFilePaths(ResultSettings resultSettings, MetadataSettings metadataSettings, ICompareSettings compareSettings, string personDisplayDirectory)
|
||||
{
|
||||
List<FilePath> results;
|
||||
string checkFile;
|
||||
FilePath filePath;
|
||||
FileHolder fileHolder;
|
||||
int? wholePercentages;
|
||||
string? checkDirectory;
|
||||
string[] files = Directory.GetFiles(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (file.EndsWith(".lnk"))
|
||||
continue;
|
||||
fileHolder = FileHolder.Get(file);
|
||||
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
|
||||
wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath);
|
||||
if (filePath.Id is not null && wholePercentages is not null)
|
||||
continue;
|
||||
checkDirectory = Path.GetDirectoryName(file);
|
||||
if (string.IsNullOrEmpty(checkDirectory))
|
||||
continue;
|
||||
checkDirectory = Path.Combine(checkDirectory, "_ Invalid");
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
_ = Directory.CreateDirectory(checkDirectory);
|
||||
checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
|
||||
if (File.Exists(checkFile))
|
||||
File.Delete(file);
|
||||
else
|
||||
File.Move(file, checkFile);
|
||||
}
|
||||
results = GetFilePaths(resultSettings, metadataSettings, personDisplayDirectory);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static RecordA GetPersonContainersGroup(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, PersonDirectory personDirectory, string[] personDisplayDirectories)
|
||||
{
|
||||
RecordA result;
|
||||
string? minusOne;
|
||||
char numberSign = '#';
|
||||
int? approximateYears;
|
||||
List<string?> changes = [];
|
||||
string[] personKeyDirectories;
|
||||
PersonContainer personContainer;
|
||||
List<PersonContainer> results = [];
|
||||
string? personDisplayDirectoryName;
|
||||
List<PersonContainer> personContainers;
|
||||
List<FilePath> personDisplayDirectoryAllFilePaths;
|
||||
char[] personCharacters = peopleSettings.PersonCharacters.ToArray();
|
||||
List<PersonKeyFormattedAndPersonBirthday> personKeyFormattedAndPersonBirthdayCollection;
|
||||
foreach (string personDisplayDirectory in personDisplayDirectories)
|
||||
{
|
||||
personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory);
|
||||
if (string.IsNullOrEmpty(personDisplayDirectoryName))
|
||||
continue;
|
||||
approximateYears = IAge.GetApproximateYears(personCharacters, personDisplayDirectoryName);
|
||||
personKeyDirectories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
personKeyFormattedAndPersonBirthdayCollection = GetPersonBirthdays(peopleSettings.PersonBirthdayFormat, personKeyDirectories, personDisplayDirectoryName);
|
||||
if (personDisplayDirectoryName.Contains('^'))
|
||||
{
|
||||
minusOne = Path.GetDirectoryName(personDisplayDirectory);
|
||||
if (minusOne is null)
|
||||
continue;
|
||||
changes.Add(VerifyAge(numberSign, personDisplayDirectory, minusOne, personDisplayDirectoryName, approximateYears, personKeyFormattedAndPersonBirthdayCollection));
|
||||
}
|
||||
if (changes.Any(l => l is not null))
|
||||
continue;
|
||||
if (personKeyFormattedAndPersonBirthdayCollection.Count > 0)
|
||||
{
|
||||
personContainers = GetPersonContainersCollections(resultSettings, metadataSettings, personDirectory, numberSign, personDisplayDirectory, personDisplayDirectoryName, approximateYears, personKeyFormattedAndPersonBirthdayCollection);
|
||||
results.AddRange(personContainers);
|
||||
}
|
||||
else
|
||||
{
|
||||
personDisplayDirectoryAllFilePaths = GetFilePaths(resultSettings, metadataSettings, compareSettings, personDisplayDirectory);
|
||||
personContainer = PersonContainer.Get(approximateYears, new(personDisplayDirectoryAllFilePaths), personDisplayDirectoryName, personDirectory);
|
||||
results.Add(personContainer);
|
||||
}
|
||||
}
|
||||
result = new(changes, results);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static RecordA GetPersonContainersInnerGroups(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, string groupDirectory, string groupDirectoryName)
|
||||
{
|
||||
RecordA result;
|
||||
string[] segments;
|
||||
const int zero = 0;
|
||||
List<string?> changes;
|
||||
List<string?> allChanges = [];
|
||||
string innerGroupDirectoryName;
|
||||
PersonDirectory personDirectory;
|
||||
List<PersonContainer> collection;
|
||||
const char exclamationPoint = '!';
|
||||
string[] personDisplayDirectories;
|
||||
List<PersonContainer> results = [];
|
||||
char @char = groupDirectoryName[zero];
|
||||
string[] innerGroupDirectories = Directory.GetDirectories(groupDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
if (@char == exclamationPoint)
|
||||
{
|
||||
personDirectory = new(@char, "Ignore", 'U', 'U', 'U');
|
||||
(changes, collection) = GetPersonContainersGroup(resultSettings, metadataSettings, peopleSettings, compareSettings, personDirectory, innerGroupDirectories);
|
||||
allChanges.AddRange(changes);
|
||||
results.AddRange(collection);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string innerGroupDirectory in innerGroupDirectories)
|
||||
{
|
||||
innerGroupDirectoryName = Path.GetFileName(innerGroupDirectory);
|
||||
segments = innerGroupDirectoryName.Split('-');
|
||||
if (segments.Length == 0)
|
||||
throw new NotSupportedException("Misplaced directory!");
|
||||
if (segments.Length != 3)
|
||||
continue;
|
||||
if (segments[zero] is not "Alive" and not "Dead" and not "Unknown")
|
||||
continue;
|
||||
if (segments[1] is not "Male" and not "Female" and not "Unknown")
|
||||
continue;
|
||||
if (segments[2] is not "Yes" and not "No" and not "Unknown")
|
||||
continue;
|
||||
personDirectory = new(@char, innerGroupDirectoryName, segments[zero][zero], segments[1][zero], segments[2][zero]);
|
||||
personDisplayDirectories = Directory.GetDirectories(innerGroupDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
(changes, collection) = GetPersonContainersGroup(resultSettings, metadataSettings, peopleSettings, compareSettings, personDirectory, personDisplayDirectories);
|
||||
allChanges.AddRange(changes);
|
||||
results.AddRange(collection);
|
||||
}
|
||||
}
|
||||
result = new(allChanges, results);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<PersonContainer> GetPersonContainersGroups(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, string[] groupDirectories)
|
||||
{
|
||||
List<PersonContainer> results;
|
||||
List<string?> changes;
|
||||
string groupDirectoryName;
|
||||
List<string?> allChanges = [];
|
||||
List<PersonContainer> collection;
|
||||
List<PersonContainer> personContainers = [];
|
||||
for (int i = 0; i < peopleSettings.PersonCharacters.Length; i++)
|
||||
{
|
||||
foreach (string groupDirectory in groupDirectories)
|
||||
{
|
||||
groupDirectoryName = Path.GetFileName(groupDirectory);
|
||||
if (peopleSettings.PersonCharacters[i] != groupDirectoryName.First())
|
||||
continue;
|
||||
(changes, collection) = GetPersonContainersInnerGroups(resultSettings, metadataSettings, peopleSettings, compareSettings, groupDirectory, groupDirectoryName);
|
||||
allChanges.AddRange(changes);
|
||||
personContainers.AddRange(collection);
|
||||
}
|
||||
}
|
||||
if (allChanges.Any(l => l is not null))
|
||||
throw new NotImplementedException($"A directory was changed restart to look for more! {string.Join(Environment.NewLine, (from l in allChanges where l is not null select l).ToArray())}");
|
||||
results = (from l in personContainers orderby l.Key is not null, l.Key select l).ToList();
|
||||
return results;
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<PersonContainer> GetPersonContainers(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings)
|
||||
{
|
||||
List<PersonContainer> results;
|
||||
string a2PeopleSingletonDirectoryChar;
|
||||
string a2PeopleSingletonDirectory = Path.GetFullPath(IResult.GetResultsDateGroupDirectory(resultSettings, nameof(A2_People), resultSettings.ResultSingleton));
|
||||
if (!Directory.Exists(a2PeopleSingletonDirectory))
|
||||
_ = Directory.CreateDirectory(a2PeopleSingletonDirectory);
|
||||
foreach (char personCharacter in peopleSettings.PersonCharacters)
|
||||
{
|
||||
a2PeopleSingletonDirectoryChar = Path.Combine(a2PeopleSingletonDirectory, personCharacter.ToString());
|
||||
if (!Directory.Exists(a2PeopleSingletonDirectoryChar))
|
||||
_ = Directory.CreateDirectory(a2PeopleSingletonDirectoryChar);
|
||||
}
|
||||
string[] groupDirectories = Directory.GetDirectories(a2PeopleSingletonDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
if (groupDirectories.Length == 0)
|
||||
results = [];
|
||||
else
|
||||
results = GetPersonContainersGroups(resultSettings, metadataSettings, peopleSettings, compareSettings, groupDirectories);
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<long> GetPersonKeys(ReadOnlyCollection<PersonContainer> personContainers)
|
||||
{
|
||||
List<long> results = [];
|
||||
long personKey;
|
||||
foreach (PersonContainer personContainer in personContainers)
|
||||
{
|
||||
if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0)
|
||||
continue;
|
||||
foreach (PersonBirthday personBirthday in personContainer.Birthdays)
|
||||
{
|
||||
personKey = personBirthday.Value.Ticks;
|
||||
results.Add(personKey);
|
||||
}
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
internal static string? VerifyAge(char numberSign, string personDisplayDirectory, string? minusOne, string personDisplayDirectoryName, int? approximateYears, List<PersonKeyFormattedAndPersonBirthday> personKeyFormattedAndPersonBirthdayCollection)
|
||||
{
|
||||
string? result;
|
||||
if (approximateYears is null)
|
||||
throw new NotSupportedException();
|
||||
if (personKeyFormattedAndPersonBirthdayCollection.Count == 0)
|
||||
throw new NotSupportedException();
|
||||
const int zero = 0;
|
||||
int? updateApproximateYears;
|
||||
DateTime dateTime = DateTime.Now;
|
||||
PersonBirthday[] orderedPersonBirthdays = (from l in personKeyFormattedAndPersonBirthdayCollection where !l.PersonKeyFormatted.Contains(numberSign) orderby l.PersonBirthday.Value.Ticks descending select l.PersonBirthday).ToArray();
|
||||
TimeSpan timeSpan = new(orderedPersonBirthdays[zero].Value.Ticks - dateTime.AddYears(-approximateYears.Value).Ticks);
|
||||
if (timeSpan.TotalDays < -366)
|
||||
updateApproximateYears = approximateYears.Value + 1;
|
||||
else if (timeSpan.TotalDays > 1)
|
||||
updateApproximateYears = approximateYears.Value - 1;
|
||||
else
|
||||
updateApproximateYears = null;
|
||||
if (minusOne is null || updateApproximateYears is null)
|
||||
result = null;
|
||||
else
|
||||
{
|
||||
result = Path.Combine(minusOne, $"{personDisplayDirectoryName.Split('^')[0]}^{updateApproximateYears}");
|
||||
if (Directory.Exists(result))
|
||||
result = null;
|
||||
else
|
||||
Directory.Move(personDisplayDirectory, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> GetDirectoryAndTicksCollection(PeopleSettings peopleSettings, string? rootDirectory)
|
||||
{
|
||||
List<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> results = [];
|
||||
string directory;
|
||||
DateTime dateTime;
|
||||
string[] personKeyDirectories;
|
||||
string[] personDisplayDirectoryNames;
|
||||
string personKeyFormattedDirectoryName;
|
||||
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
|
||||
foreach (string jLink in peopleSettings.JLinks)
|
||||
{
|
||||
if (rootDirectory is null)
|
||||
continue;
|
||||
directory = Path.Combine(rootDirectory, jLink);
|
||||
if (!Directory.Exists(directory))
|
||||
continue;
|
||||
personDisplayDirectoryNames = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string personDisplayDirectoryName in personDisplayDirectoryNames)
|
||||
{
|
||||
personKeyDirectories = Directory.GetDirectories(personDisplayDirectoryName, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string personKeyFormattedDirectory in personKeyDirectories)
|
||||
{
|
||||
personKeyFormattedDirectoryName = Path.GetFileName(personKeyFormattedDirectory);
|
||||
if (personKeyFormattedDirectoryName.Length != peopleSettings.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormattedDirectoryName, peopleSettings.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
|
||||
continue;
|
||||
personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(directory, dateTime.Ticks, personKeyFormattedDirectoryName);
|
||||
results.Add(personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> GetGenealogicalDataCommunicationDirectories(PeopleSettings peopleSettings)
|
||||
{
|
||||
ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> results;
|
||||
string? genealogicalDataCommunicationDirectory = Path.GetDirectoryName(peopleSettings.GenealogicalDataCommunicationFile);
|
||||
results = GetDirectoryAndTicksCollection(peopleSettings, genealogicalDataCommunicationDirectory);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static string? TryToFind(PeopleSettings peopleSettings, string a2PeopleSingletonDirectory, List<RecordB> a2PeopleSingletonDirectories, string file, string path)
|
||||
{
|
||||
string? result;
|
||||
string? pathName = Path.GetFileName(path);
|
||||
string? group = Path.GetDirectoryName(path);
|
||||
string? groupName = Path.GetFileName(group);
|
||||
if (pathName is null || group is null || groupName is null)
|
||||
result = null;
|
||||
else
|
||||
{
|
||||
string[] matches;
|
||||
matches = (from l in a2PeopleSingletonDirectories where l.DirectoryName == pathName select l.Directory).ToArray();
|
||||
if (matches.Length == 1)
|
||||
result = matches.First();
|
||||
else
|
||||
{
|
||||
string pathNameSplitFirst = pathName.Split(peopleSettings.PersonCharacters).First();
|
||||
matches = (from l in a2PeopleSingletonDirectories where l.DirectoryNameSplitFirst == pathNameSplitFirst select l.Directory).ToArray();
|
||||
if (matches.Length == 1)
|
||||
result = matches.First();
|
||||
else
|
||||
{
|
||||
string checkDirectory = Path.Combine(a2PeopleSingletonDirectory, groupName, pathName);
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
result = null;
|
||||
else
|
||||
result = checkDirectory;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
try
|
||||
{
|
||||
WindowsShortcut windowsShortcut;
|
||||
windowsShortcut = new() { Path = result };
|
||||
windowsShortcut.Save(file);
|
||||
windowsShortcut.Dispose();
|
||||
}
|
||||
catch (Exception)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> GetJLinkResolvedDirectories(PeopleSettings peopleSettings, List<string> resolvedDirectories)
|
||||
{
|
||||
List<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> results = [];
|
||||
DateTime dateTime;
|
||||
string directoryName;
|
||||
string[] directories;
|
||||
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
|
||||
foreach (string resolvedDirectory in resolvedDirectories)
|
||||
{
|
||||
directories = Directory.GetDirectories(resolvedDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string directory in directories)
|
||||
{
|
||||
directoryName = Path.GetFileName(directory);
|
||||
if (directoryName.Length != peopleSettings.PersonBirthdayFormat.Length || !DateTime.TryParseExact(directoryName, peopleSettings.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
|
||||
continue;
|
||||
personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(resolvedDirectory, dateTime.Ticks, directoryName);
|
||||
results.Add(personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
|
||||
}
|
||||
}
|
||||
if (results.Count != resolvedDirectories.Count)
|
||||
results.Clear();
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> GetJLinkDirectories(ResultSettings resultSettings, PeopleSettings peopleSettings)
|
||||
{
|
||||
ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> results;
|
||||
string[] files;
|
||||
string? foundPath;
|
||||
int totalFiles = 0;
|
||||
string checkDirectory;
|
||||
WindowsShortcut windowsShortcut;
|
||||
List<string> resolvedDirectories = [];
|
||||
string a2PeopleContentDirectory = Path.GetFullPath(IResult.GetResultsDateGroupDirectory(resultSettings, nameof(A2_People), resultSettings.ResultContent));
|
||||
if (!Directory.Exists(a2PeopleContentDirectory))
|
||||
_ = Directory.CreateDirectory(a2PeopleContentDirectory);
|
||||
string a2PeopleSingletonDirectory = Path.GetFullPath(IResult.GetResultsDateGroupDirectory(resultSettings, nameof(A2_People), resultSettings.ResultSingleton));
|
||||
if (!Directory.Exists(a2PeopleSingletonDirectory))
|
||||
_ = Directory.CreateDirectory(a2PeopleSingletonDirectory);
|
||||
if (string.IsNullOrEmpty(peopleSettings.GenealogicalDataCommunicationFile))
|
||||
results = GetDirectoryAndTicksCollection(peopleSettings, a2PeopleContentDirectory);
|
||||
else
|
||||
results = GetGenealogicalDataCommunicationDirectories(peopleSettings);
|
||||
if (results.Count == 0 || results.Count < peopleSettings.JLinks.Length)
|
||||
{
|
||||
RecordB recordB;
|
||||
List<RecordB> a2PeopleSingletonDirectories = [];
|
||||
foreach (string directory in Directory.GetDirectories(a2PeopleSingletonDirectory, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
recordB = new(directory, Path.GetFileName(directory), Path.GetFileName(directory).Split(peopleSettings.PersonCharacters).First());
|
||||
a2PeopleSingletonDirectories.Add(recordB);
|
||||
}
|
||||
foreach (string directoryName in peopleSettings.JLinks)
|
||||
{
|
||||
checkDirectory = Path.Combine(a2PeopleContentDirectory, directoryName);
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
continue;
|
||||
files = Directory.GetFiles(checkDirectory, "*.lnk", SearchOption.TopDirectoryOnly);
|
||||
totalFiles += files.Length;
|
||||
foreach (string file in files)
|
||||
{
|
||||
windowsShortcut = WindowsShortcut.Load(file);
|
||||
if (windowsShortcut.Path is null)
|
||||
continue;
|
||||
if (Directory.Exists(windowsShortcut.Path))
|
||||
resolvedDirectories.Add(windowsShortcut.Path);
|
||||
else
|
||||
{
|
||||
foundPath = TryToFind(peopleSettings, a2PeopleSingletonDirectory, a2PeopleSingletonDirectories, file, windowsShortcut.Path);
|
||||
if (string.IsNullOrEmpty(foundPath))
|
||||
continue;
|
||||
resolvedDirectories.Add(foundPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (totalFiles == resolvedDirectories.Count)
|
||||
results = GetJLinkResolvedDirectories(peopleSettings, resolvedDirectories);
|
||||
else
|
||||
{
|
||||
resolvedDirectories.Clear();
|
||||
results = new([]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> GetJLinkResolvedDirectories(ResultSettings resultSettings, PeopleSettings peopleSettings)
|
||||
{
|
||||
ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> jLinkResolvedDirectories;
|
||||
if (!peopleSettings.JLinks.Where(l => !string.IsNullOrEmpty(l)).Any())
|
||||
jLinkResolvedDirectories = new([]);
|
||||
else
|
||||
{
|
||||
jLinkResolvedDirectories = GetJLinkDirectories(resultSettings, peopleSettings);
|
||||
if (jLinkResolvedDirectories.Count == 0)
|
||||
throw new Exception(nameof(GetJLinkDirectories));
|
||||
}
|
||||
return jLinkResolvedDirectories;
|
||||
}
|
||||
|
||||
}
|
110
People/Models/Stateless/ReadOnlyCollectionsLogic.cs
Normal file
110
People/Models/Stateless/ReadOnlyCollectionsLogic.cs
Normal file
@ -0,0 +1,110 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Metadata.Models;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.People.Models.Stateless.Methods;
|
||||
|
||||
internal static class ReadOnlyCollectionsLogic
|
||||
{
|
||||
|
||||
private static void SetPersonCollectionsAfterSetSkipCollections(PeopleSettings peopleSettings, ReadOnlyCollection<PersonContainer> personContainers, Dictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted, List<string> personKeyFormattedCollection)
|
||||
{
|
||||
string personKeyFormatted;
|
||||
string newestPersonKeyFormatted;
|
||||
foreach (PersonContainer personContainer in personContainers)
|
||||
{
|
||||
if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0)
|
||||
continue;
|
||||
foreach (PersonBirthday personBirthday in personContainer.Birthdays)
|
||||
{
|
||||
personKeyFormatted = IPersonBirthday.GetFormatted(peopleSettings.PersonBirthdayFormat, personBirthday);
|
||||
personKeyFormattedCollection.Add(personKeyFormatted);
|
||||
if (personContainer.Birthdays.Length < 1)
|
||||
continue;
|
||||
newestPersonKeyFormatted = IPersonBirthday.GetFormatted(peopleSettings.PersonBirthdayFormat, personContainer.Key.Value);
|
||||
_ = personKeyFormattedToNewestPersonKeyFormatted.TryAdd(personKeyFormatted, newestPersonKeyFormatted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetSkipCollections(ResultSettings resultSettings, ICompareSettings compareSettings, ReadOnlyCollection<PersonContainer> personContainers, Dictionary<int, List<FilePathAndWholePercentages>> skipCollection, Dictionary<int, List<FilePathAndWholePercentages>> skipNotSkipCollection, ReadOnlyCollection<string> skipNotSkipDirectories)
|
||||
{
|
||||
string checkFile;
|
||||
int? wholePercentages;
|
||||
List<FilePath> distinct = [];
|
||||
List<string> distinctFiles = [];
|
||||
List<string> distinctFileName = [];
|
||||
FilePathAndWholePercentages filePathAndWholePercentages;
|
||||
bool skipNotSkipDirectoriesAny = skipNotSkipDirectories.Count > 0;
|
||||
string a2PeopleSingletonDirectory = Path.GetFullPath(IResult.GetResultsDateGroupDirectory(resultSettings, nameof(A2_People), resultSettings.ResultSingleton));
|
||||
string[] checkDirectories = string.IsNullOrEmpty(a2PeopleSingletonDirectory) ? [] : (from l in skipNotSkipDirectories select Path.GetFullPath($"{a2PeopleSingletonDirectory}{l}")).ToArray();
|
||||
foreach (PersonContainer personContainer in personContainers)
|
||||
{
|
||||
foreach (FilePath personDisplayDirectoryAllFilePath in personContainer.DisplayDirectoryAllFilePaths)
|
||||
{
|
||||
if (personDisplayDirectoryAllFilePath.ExtensionLowered != compareSettings.FacesFileNameExtension)
|
||||
continue;
|
||||
if (distinctFiles.Contains(personDisplayDirectoryAllFilePath.FullName))
|
||||
continue;
|
||||
distinctFiles.Add(personDisplayDirectoryAllFilePath.FullName);
|
||||
distinct.Add(personDisplayDirectoryAllFilePath);
|
||||
}
|
||||
}
|
||||
foreach (FilePath filePath in distinct)
|
||||
{
|
||||
if (distinctFileName.Contains(filePath.Name))
|
||||
{
|
||||
checkFile = $"{filePath.FullName}.dup";
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Move(filePath.FullName, checkFile);
|
||||
continue;
|
||||
}
|
||||
if (filePath.Id is null)
|
||||
continue;
|
||||
wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath);
|
||||
if (wholePercentages is null)
|
||||
continue;
|
||||
if (!skipNotSkipDirectoriesAny || !checkDirectories.Any(filePath.FullName.StartsWith))
|
||||
{
|
||||
if (!skipCollection.ContainsKey(filePath.Id.Value))
|
||||
skipCollection.Add(filePath.Id.Value, []);
|
||||
filePathAndWholePercentages = new(filePath, wholePercentages.Value);
|
||||
skipCollection[filePath.Id.Value].Add(filePathAndWholePercentages);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!skipNotSkipCollection.ContainsKey(filePath.Id.Value))
|
||||
skipNotSkipCollection.Add(filePath.Id.Value, []);
|
||||
filePathAndWholePercentages = new(filePath, wholePercentages.Value);
|
||||
skipNotSkipCollection[filePath.Id.Value].Add(filePathAndWholePercentages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static ReadOnlyCollections GetReadOnlyCollections(ResultSettings resultSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ReadOnlyCollection<PersonContainer> personContainers, ReadOnlyCollection<long> personKeys)
|
||||
{
|
||||
ReadOnlyCollections result;
|
||||
List<string> personKeyFormattedCollection = [];
|
||||
Dictionary<int, List<FilePathAndWholePercentages>> skipCollection = [];
|
||||
Dictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted = [];
|
||||
Dictionary<int, List<FilePathAndWholePercentages>> skipNotSkipCollection = [];
|
||||
ReadOnlyCollection<string> skipNotSkipDirectories = distanceSettings.SkipNotSkipDirectories.Where(l => !string.IsNullOrEmpty(l)).ToArray().AsReadOnly();
|
||||
SetSkipCollections(resultSettings, compareSettings, personContainers, skipCollection, skipNotSkipCollection, skipNotSkipDirectories);
|
||||
SetPersonCollectionsAfterSetSkipCollections(peopleSettings, personContainers, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection);
|
||||
ReadOnlyCollection<PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName> jLinkResolvedDirectories = IPeople.GetJLinkResolvedDirectories(resultSettings, peopleSettings);
|
||||
ReadOnlyCollection<long> jLinkResolvedPersonKeys = jLinkResolvedDirectories.Select(l => l.KeyTicks).ToArray().AsReadOnly();
|
||||
result = new(jLinkResolvedPersonKeys,
|
||||
personContainers,
|
||||
personKeyFormattedCollection.AsReadOnly(),
|
||||
personKeyFormattedToNewestPersonKeyFormatted.AsReadOnly(),
|
||||
personKeys,
|
||||
skipCollection.AsReadOnly(),
|
||||
skipNotSkipCollection.AsReadOnly(),
|
||||
skipNotSkipDirectories);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using View_by_Distance.Shared.Models;
|
||||
|
||||
namespace View_by_Distance.Rename.Models;
|
||||
|
||||
public record AppSettings(RenameConfiguration RenameConfiguration,
|
||||
string Company,
|
||||
int MaxDegreeOfParallelism,
|
||||
bool RequireRootDirectoryExists)
|
||||
public record AppSettings(ResultSettings ResultSettings,
|
||||
MetadataSettings MetadataSettings,
|
||||
RenameSettings RenameSettings)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
@ -15,6 +16,42 @@ public record AppSettings(RenameConfiguration RenameConfiguration,
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void Verify(AppSettings appSettings)
|
||||
{
|
||||
if (appSettings.RenameSettings.MaxDegreeOfParallelism > Environment.ProcessorCount)
|
||||
throw new Exception("MaxDegreeOfParallelism must be =< Environment.ProcessorCount!");
|
||||
if (appSettings.RenameSettings.MaxDegreeOfParallelism > 1 && (appSettings.RenameSettings.InPlace || appSettings.RenameSettings.InPlaceMoveDirectory || appSettings.RenameSettings.InPlaceWithOriginalName))
|
||||
throw new NotSupportedException($"Change Settings: {nameof(appSettings.RenameSettings.InPlace)} or {nameof(appSettings.RenameSettings.InPlaceMoveDirectory)} or {nameof(appSettings.RenameSettings.MaxDegreeOfParallelism)}");
|
||||
if (appSettings.RenameSettings.InPlace && appSettings.RenameSettings.InPlaceMoveDirectory && appSettings.RenameSettings.InPlaceWithOriginalName)
|
||||
throw new NotSupportedException($"Change Settings: {nameof(appSettings.RenameSettings.InPlace)} or {nameof(appSettings.RenameSettings.InPlaceMoveDirectory)} or {nameof(appSettings.RenameSettings.InPlaceWithOriginalName)}");
|
||||
}
|
||||
|
||||
public static AppSettings Get(IConfigurationRoot configurationRoot)
|
||||
{
|
||||
AppSettings result;
|
||||
#pragma warning disable IL3050, IL2026
|
||||
ResultSettings? resultSettings = configurationRoot.GetSection(nameof(ResultSettings)).Get<ResultSettings>();
|
||||
MetadataSettings? metadataSettings = configurationRoot.GetSection(nameof(MetadataSettings)).Get<MetadataSettings>();
|
||||
RenameSettings? renameSettings = configurationRoot.GetSection(nameof(RenameSettings)).Get<RenameSettings>();
|
||||
#pragma warning restore IL3050, IL2026
|
||||
if (resultSettings is null || metadataSettings is null || renameSettings?.Company is null)
|
||||
{
|
||||
List<string> paths = [];
|
||||
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
|
||||
{
|
||||
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
|
||||
continue;
|
||||
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
|
||||
continue;
|
||||
paths.Add(physicalFileProvider.Root);
|
||||
}
|
||||
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
|
||||
}
|
||||
result = new(resultSettings, metadataSettings, renameSettings);
|
||||
Verify(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
|
@ -1,2 +0,0 @@
|
||||
[*.cs]
|
||||
csharp_preserve_single_line_statements = true
|
@ -1,108 +0,0 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Rename.Models.Binder;
|
||||
|
||||
public class AppSettings
|
||||
{
|
||||
|
||||
public string? Company { get; set; }
|
||||
public string[]? ConfigurationDirectoryNames { get; set; }
|
||||
public string? ConfigurationFileName { get; set; }
|
||||
public int? ConfigurationSpecialFolder { get; set; }
|
||||
public int? MaxDegreeOfParallelism { get; set; }
|
||||
public bool? RequireRootDirectoryExists { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void PreVerify(IConfigurationRoot configurationRoot, AppSettings? appSettings)
|
||||
{
|
||||
if (appSettings?.Company is null)
|
||||
{
|
||||
List<string> paths = [];
|
||||
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
|
||||
{
|
||||
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
|
||||
continue;
|
||||
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
|
||||
continue;
|
||||
paths.Add(physicalFileProvider.Root);
|
||||
}
|
||||
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void Verify(Models.AppSettings appSettings)
|
||||
{
|
||||
if (appSettings.MaxDegreeOfParallelism > 1 && (appSettings.RenameConfiguration.InPlace || appSettings.RenameConfiguration.InPlaceMoveDirectory || appSettings.RenameConfiguration.InPlaceWithOriginalName))
|
||||
throw new NotSupportedException($"Change configuration: {nameof(appSettings.RenameConfiguration.InPlace)} or {nameof(appSettings.RenameConfiguration.InPlaceMoveDirectory)} or {nameof(appSettings.MaxDegreeOfParallelism)}");
|
||||
if (appSettings.RenameConfiguration.InPlace && appSettings.RenameConfiguration.InPlaceMoveDirectory && appSettings.RenameConfiguration.InPlaceWithOriginalName)
|
||||
throw new NotSupportedException($"Change configuration: {nameof(appSettings.RenameConfiguration.InPlace)} or {nameof(appSettings.RenameConfiguration.InPlaceMoveDirectory)} or {nameof(appSettings.RenameConfiguration.InPlaceWithOriginalName)}");
|
||||
}
|
||||
|
||||
private static Models.AppSettings Get(AppSettings? appSettings, RenameConfiguration renameConfiguration)
|
||||
{
|
||||
Models.AppSettings result;
|
||||
if (appSettings is null) throw new NullReferenceException(nameof(appSettings));
|
||||
if (appSettings.Company is null) throw new NullReferenceException(nameof(appSettings.Company));
|
||||
if (appSettings.MaxDegreeOfParallelism is null) throw new NullReferenceException(nameof(appSettings.MaxDegreeOfParallelism));
|
||||
if (appSettings.RequireRootDirectoryExists is null) throw new NullReferenceException(nameof(appSettings.RequireRootDirectoryExists));
|
||||
result = new(renameConfiguration,
|
||||
appSettings.Company,
|
||||
appSettings.MaxDegreeOfParallelism.Value,
|
||||
appSettings.RequireRootDirectoryExists.Value);
|
||||
Verify(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Models.AppSettings Get(AppSettings? appSettings)
|
||||
{
|
||||
Models.AppSettings? result;
|
||||
string? json;
|
||||
if (appSettings is null || appSettings.ConfigurationFileName is null)
|
||||
throw new NotSupportedException($"{nameof(appSettings.ConfigurationFileName)} must be set!");
|
||||
string jsonFile = Path.Combine(AppContext.BaseDirectory, appSettings.ConfigurationFileName);
|
||||
if (File.Exists(jsonFile))
|
||||
json = File.ReadAllText(jsonFile);
|
||||
else
|
||||
{
|
||||
json = null;
|
||||
string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
List<string> collection = [applicationData];
|
||||
if (appSettings?.ConfigurationDirectoryNames is not null)
|
||||
collection.AddRange(appSettings.ConfigurationDirectoryNames);
|
||||
if (appSettings?.ConfigurationFileName is not null)
|
||||
collection.Add(appSettings.ConfigurationFileName);
|
||||
jsonFile = Path.Combine(collection.ToArray());
|
||||
}
|
||||
if (string.IsNullOrEmpty(json) && File.Exists(jsonFile))
|
||||
json = File.ReadAllText(jsonFile);
|
||||
result = (string.IsNullOrEmpty(json) ? null : result = JsonSerializer.Deserialize(json, AppSettingsSourceGenerationContext.Default.AppSettings)) ??
|
||||
throw new NullReferenceException(nameof(Models.AppSettings));
|
||||
result = Get(appSettings, result.RenameConfiguration);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Models.AppSettings Get(IConfigurationRoot configurationRoot)
|
||||
{
|
||||
Models.AppSettings result;
|
||||
#pragma warning disable IL3050, IL2026
|
||||
AppSettings? appSettings = configurationRoot.Get<AppSettings>();
|
||||
#pragma warning restore IL3050, IL2026
|
||||
PreVerify(configurationRoot, appSettings);
|
||||
result = Get(appSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(AppSettings))]
|
||||
internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Rename.Models;
|
||||
|
||||
public record RenameConfiguration(Shared.Models.MetadataConfiguration MetadataConfiguration,
|
||||
string DefaultMaker,
|
||||
bool ForceNewId,
|
||||
string[] IgnoreExtensions,
|
||||
bool InPlace,
|
||||
bool InPlaceMoveDirectory,
|
||||
bool InPlaceWithOriginalName,
|
||||
bool OnlySaveIdentifiersToDisk,
|
||||
string RelativePropertyCollectionFile,
|
||||
string[] SidecarExtensions,
|
||||
bool SkipIdFiles,
|
||||
string[] ValidImageFormatExtensions,
|
||||
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.IRenameConfiguration
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, RenameConfigurationSourceGenerationContext.Default.RenameConfiguration);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(RenameConfiguration))]
|
||||
internal partial class RenameConfigurationSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
36
Rename/Models/RenameSettings.cs
Normal file
36
Rename/Models/RenameSettings.cs
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Rename.Models;
|
||||
|
||||
public record RenameSettings(string Company,
|
||||
string DefaultMaker,
|
||||
bool ForceNewId,
|
||||
string[] IgnoreExtensions,
|
||||
bool InPlace,
|
||||
bool InPlaceMoveDirectory,
|
||||
bool InPlaceWithOriginalName,
|
||||
int MaxDegreeOfParallelism,
|
||||
bool OnlySaveIdentifiersToDisk,
|
||||
string RelativePropertyCollectionFile,
|
||||
bool RequireRootDirectoryExists,
|
||||
string[] SidecarExtensions,
|
||||
bool SkipIdFiles,
|
||||
string[] ValidImageFormatExtensions,
|
||||
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.IRenameSettings
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, RenameSettingsSourceGenerationContext.Default.RenameSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(RenameSettings))]
|
||||
internal partial class RenameSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
@ -15,11 +15,7 @@ public class Program
|
||||
.AddEnvironmentVariables()
|
||||
.AddUserSecrets<Program>();
|
||||
IConfigurationRoot configurationRoot = configurationBuilder.Build();
|
||||
AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot);
|
||||
if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount)
|
||||
throw new Exception("MaxDegreeOfParallelism must be =< Environment.ProcessorCount!");
|
||||
if (string.IsNullOrEmpty(appSettings.Company))
|
||||
throw new Exception("Company must have a value!");
|
||||
AppSettings appSettings = AppSettings.Get(configurationRoot);
|
||||
int silentIndex = args.IndexOf("s");
|
||||
if (silentIndex > -1)
|
||||
args.RemoveAt(silentIndex);
|
||||
|
210
Rename/Rename.cs
210
Rename/Rename.cs
@ -49,10 +49,10 @@ public partial class Rename : IRename, IDisposable
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
ReadOnlyCollection<string> IRename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IRenameConfiguration renameConfiguration, FilePath filePath)
|
||||
ReadOnlyCollection<string> IRename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IRenameSettings renameSettings, FilePath filePath)
|
||||
{
|
||||
List<string> results = [];
|
||||
bool isValidVideoFormatExtensions = renameConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
|
||||
bool isValidVideoFormatExtensions = renameSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
|
||||
if (isValidVideoFormatExtensions)
|
||||
{
|
||||
bool check;
|
||||
@ -79,7 +79,7 @@ public partial class Rename : IRename, IDisposable
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
return new(results);
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416
|
||||
@ -117,22 +117,22 @@ public partial class Rename : IRename, IDisposable
|
||||
|
||||
#pragma warning restore CA1416
|
||||
|
||||
private void NonParallelismAndInPlace(RenameConfiguration renameConfiguration, ReadOnlyCollection<int> ids, ExifDirectory exifDirectory, FileInfo fileInfo, FilePath filePath, bool fastForwardMovingPictureExpertsGroupUsed, ReadOnlyCollection<FileHolder> sidecarFiles)
|
||||
private void NonParallelismAndInPlace(AppSettings appSettings, ReadOnlyCollection<int> ids, ExifDirectory exifDirectory, FileInfo fileInfo, FilePath filePath, bool fastForwardMovingPictureExpertsGroupUsed, ReadOnlyCollection<FileHolder> sidecarFiles)
|
||||
{
|
||||
if (exifDirectory.Id is null)
|
||||
if (exifDirectory.FilePath.Id is null)
|
||||
throw new NotImplementedException();
|
||||
int i = 0;
|
||||
ToDo toDo;
|
||||
const string jpg = ".jpg";
|
||||
const string jpeg = ".jpeg";
|
||||
List<ToDo> toDoCollection = [];
|
||||
DateTime? dateTime = IDate.GetDateTimeOriginal(exifDirectory);
|
||||
ReadOnlyCollection<string> keywords = IMetadata.GetKeywords(exifDirectory);
|
||||
MetadataConfiguration metadataConfiguration = renameConfiguration.MetadataConfiguration;
|
||||
bool hasIgnoreKeyword = metadataConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains);
|
||||
bool hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(keywords.Contains);
|
||||
string checkFileExtension = filePath.ExtensionLowered == jpeg ? jpg : filePath.ExtensionLowered;
|
||||
bool hasDateTimeOriginal = dateTime is not null;
|
||||
string paddedId = IId.GetPaddedId(metadataConfiguration, exifDirectory.Id.Value, hasIgnoreKeyword, hasDateTimeOriginal, i);
|
||||
string checkDirectory = renameConfiguration.InPlaceWithOriginalName ? Path.Combine(filePath.DirectoryFullPath, filePath.FileNameFirstSegment) : filePath.DirectoryFullPath;
|
||||
string paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, exifDirectory.FilePath.Id.Value, hasIgnoreKeyword, hasDateTimeOriginal, i);
|
||||
string checkDirectory = appSettings.RenameSettings.InPlaceWithOriginalName ? Path.Combine(filePath.DirectoryFullPath, filePath.FileNameFirstSegment) : filePath.DirectoryFullPath;
|
||||
string checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
|
||||
if (checkFile != filePath.FullName)
|
||||
{
|
||||
@ -142,21 +142,22 @@ public partial class Rename : IRename, IDisposable
|
||||
if (File.Exists(checkFile))
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
toDoCollection.Add(new(checkDirectory, filePath, checkFile, JsonFile: false));
|
||||
toDo = new(checkDirectory, filePath, checkFile, JsonFile: false);
|
||||
toDoCollection.Add(toDo);
|
||||
if (sidecarFiles.Count != 0)
|
||||
{
|
||||
if (renameConfiguration.InPlace)
|
||||
throw new NotSupportedException($"Must use {nameof(renameConfiguration.InPlaceWithOriginalName)} when sidecar file(s) are present!");
|
||||
if (appSettings.RenameSettings.InPlace)
|
||||
throw new NotSupportedException($"Must use {nameof(appSettings.RenameSettings.InPlaceWithOriginalName)} when sidecar file(s) are present!");
|
||||
dateTime ??= IDate.GetMinimum(exifDirectory);
|
||||
RecordB recordB = new(dateTime.Value, exifDirectory, fastForwardMovingPictureExpertsGroupUsed, filePath, sidecarFiles, hasDateTimeOriginal, hasIgnoreKeyword, fileInfo.FullName);
|
||||
toDoCollection.AddRange(GetSidecarFiles(metadataConfiguration, recordB, [], checkDirectory, paddedId));
|
||||
toDoCollection.AddRange(GetSidecarFiles(appSettings, recordB, [], checkDirectory, paddedId));
|
||||
}
|
||||
_ = RenameFilesInDirectories(renameConfiguration, new(toDoCollection));
|
||||
_ = RenameFilesInDirectories(appSettings.RenameSettings, new(toDoCollection));
|
||||
string jsonFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}.json");
|
||||
File.Move(fileInfo.FullName, jsonFile, overwrite: true);
|
||||
if (renameConfiguration.InPlaceWithOriginalName && ids.Count > 0)
|
||||
if (appSettings.RenameSettings.InPlaceWithOriginalName && ids.Count > 0)
|
||||
{
|
||||
string contains = ids.Contains(exifDirectory.Id.Value) ? "_ Exists _" : "_ New _";
|
||||
string contains = ids.Contains(exifDirectory.FilePath.Id.Value) ? "_ Exists _" : "_ New _";
|
||||
string idCheck = Path.Combine(checkDirectory, contains, fastForwardMovingPictureExpertsGroupUsed ? "Video" : "Image");
|
||||
if (!Directory.Exists(idCheck))
|
||||
_ = Directory.CreateDirectory(idCheck);
|
||||
@ -164,16 +165,16 @@ public partial class Rename : IRename, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private List<RecordA> GetRecordACollection(ILogger<Program>? logger, RenameConfiguration renameConfiguration, IRename rename, ReadOnlyCollection<int> ids, IEnumerable<string> files, A_Metadata metadata)
|
||||
private List<RecordA> GetRecordACollection(ILogger<Program>? logger, AppSettings appSettings, IRename rename, ReadOnlyCollection<int> ids, IEnumerable<string> files, A_Metadata metadata)
|
||||
{
|
||||
List<RecordA> results = [];
|
||||
int index = -1;
|
||||
RecordA recordA;
|
||||
FileInfo fileInfo;
|
||||
FilePath filePath;
|
||||
string directoryName;
|
||||
ExifDirectory exifDirectory;
|
||||
List<FileHolder> sidecarFiles;
|
||||
DeterministicHashCode deterministicHashCode;
|
||||
bool fastForwardMovingPictureExpertsGroupUsed;
|
||||
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
|
||||
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
|
||||
@ -182,36 +183,34 @@ public partial class Rename : IRename, IDisposable
|
||||
{
|
||||
index += 1;
|
||||
rename.Tick();
|
||||
if (keyValuePair.Value.Count > 1 && !renameConfiguration.ForceNewId)
|
||||
if (keyValuePair.Value.Count > 1 && !appSettings.RenameSettings.ForceNewId)
|
||||
{
|
||||
if (renameConfiguration.InPlaceMoveDirectory)
|
||||
if (appSettings.RenameSettings.InPlaceMoveDirectory)
|
||||
continue;
|
||||
throw new NotSupportedException($"When sidecar files are present {nameof(renameConfiguration.ForceNewId)} must be true!");
|
||||
throw new NotSupportedException($"When sidecar files are present {nameof(appSettings.RenameSettings.ForceNewId)} must be true!");
|
||||
}
|
||||
if (keyValuePair.Value.Count > 2)
|
||||
throw new NotSupportedException("Too many sidecar files!");
|
||||
foreach (FileHolder fileHolder in keyValuePair.Value)
|
||||
{
|
||||
if (renameConfiguration.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
|
||||
if (appSettings.RenameSettings.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
|
||||
continue;
|
||||
if (renameConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
|
||||
if (appSettings.RenameSettings.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
|
||||
continue;
|
||||
filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index);
|
||||
if (renameConfiguration.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
|
||||
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index);
|
||||
if (appSettings.RenameSettings.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
|
||||
continue;
|
||||
if (!renameConfiguration.ForceNewId && filePath.Id is not null)
|
||||
if (!appSettings.RenameSettings.ForceNewId && filePath.Id is not null)
|
||||
{
|
||||
fastForwardMovingPictureExpertsGroupFiles = null;
|
||||
deterministicHashCode = new(null, filePath.Id, null);
|
||||
directoryName = Path.GetFileName(filePath.DirectoryFullPath);
|
||||
if (renameConfiguration.InPlaceWithOriginalName || (renameConfiguration.InPlace && directoryName.EndsWith(filePath.Id.Value.ToString())))
|
||||
if (appSettings.RenameSettings.InPlaceWithOriginalName || (appSettings.RenameSettings.InPlace && directoryName.EndsWith(filePath.Id.Value.ToString())))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(renameConfiguration, filePath);
|
||||
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(renameConfiguration.MetadataConfiguration, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index);
|
||||
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(fastForwardMovingPictureExpertsGroupFilePath);
|
||||
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(appSettings.RenameSettings, filePath);
|
||||
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index);
|
||||
}
|
||||
sidecarFiles = [];
|
||||
for (int i = 0; i < keyValuePair.Value.Count; i++)
|
||||
@ -221,7 +220,7 @@ public partial class Rename : IRename, IDisposable
|
||||
sidecarFiles.Add(keyValuePair.Value[i]);
|
||||
}
|
||||
try
|
||||
{ (fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode); }
|
||||
{ (fileInfo, exifDirectory) = metadata.GetMetadataCollection(appSettings.ResultSettings, appSettings.MetadataSettings, filePath); }
|
||||
catch (Exception)
|
||||
{
|
||||
logger?.LogWarning("<{filePath}>", filePath.FullName);
|
||||
@ -233,19 +232,21 @@ public partial class Rename : IRename, IDisposable
|
||||
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
|
||||
File.Delete(fastForwardMovingPictureExpertsGroupFile);
|
||||
}
|
||||
if (renameConfiguration.InPlace || renameConfiguration.InPlaceWithOriginalName)
|
||||
NonParallelismAndInPlace(renameConfiguration, ids, exifDirectory, fileInfo, filePath, fastForwardMovingPictureExpertsGroupUsed, new(sidecarFiles));
|
||||
if (!fastForwardMovingPictureExpertsGroupUsed && renameConfiguration.InPlaceMoveDirectory && renameConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
|
||||
if (appSettings.RenameSettings.InPlace || appSettings.RenameSettings.InPlaceWithOriginalName)
|
||||
NonParallelismAndInPlace(appSettings, ids, exifDirectory, fileInfo, filePath, fastForwardMovingPictureExpertsGroupUsed, new(sidecarFiles));
|
||||
if (!fastForwardMovingPictureExpertsGroupUsed && appSettings.RenameSettings.InPlaceMoveDirectory && appSettings.RenameSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
|
||||
fastForwardMovingPictureExpertsGroupUsed = true;
|
||||
results.Add(new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, fileInfo, filePath, new(sidecarFiles)));
|
||||
recordA = new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, fileInfo, filePath, new(sidecarFiles));
|
||||
results.Add(recordA);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<RecordB> GetRecordBCollection(MetadataConfiguration metadataConfiguration, List<RecordA> recordACollection)
|
||||
private static ReadOnlyCollection<RecordB> GetRecordBCollection(AppSettings appSettings, List<RecordA> recordACollection)
|
||||
{
|
||||
List<RecordB> results = [];
|
||||
RecordB recordB;
|
||||
DateTime? dateTime;
|
||||
bool hasIgnoreKeyword;
|
||||
bool hasDateTimeOriginal;
|
||||
@ -256,52 +257,54 @@ public partial class Rename : IRename, IDisposable
|
||||
hasDateTimeOriginal = dateTime is not null;
|
||||
dateTime ??= IDate.GetMinimum(recordA.ExifDirectory);
|
||||
keywords = IMetadata.GetKeywords(recordA.ExifDirectory);
|
||||
hasIgnoreKeyword = metadataConfiguration.IgnoreRulesKeyWords.Any(l => keywords.Contains(l));
|
||||
results.Add(new(dateTime.Value, recordA.ExifDirectory, recordA.FastForwardMovingPictureExpertsGroupUsed, recordA.FilePath, recordA.SidecarFiles, hasDateTimeOriginal, hasIgnoreKeyword, recordA.FileInfo.FullName));
|
||||
hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(l => keywords.Contains(l));
|
||||
recordB = new(dateTime.Value, recordA.ExifDirectory, recordA.FastForwardMovingPictureExpertsGroupUsed, recordA.FilePath, recordA.SidecarFiles, hasDateTimeOriginal, hasIgnoreKeyword, recordA.FileInfo.FullName);
|
||||
results.Add(recordB);
|
||||
}
|
||||
return new(results);
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private ReadOnlyCollection<RecordB> GetRecordBCollection(ILogger<Program>? logger, AppSettings appSettings, IRename rename, ReadOnlyCollection<int> ids, DirectoryInfo directoryInfo)
|
||||
{
|
||||
ReadOnlyCollection<RecordB> results;
|
||||
RecordA recordA;
|
||||
List<RecordA> recordACollection = [];
|
||||
RenameConfiguration renameConfiguration = appSettings.RenameConfiguration;
|
||||
A_Metadata metadata = new(renameConfiguration.MetadataConfiguration);
|
||||
int appSettingsMaxDegreeOfParallelism = appSettings.MaxDegreeOfParallelism;
|
||||
A_Metadata metadata = new(appSettings.ResultSettings, appSettings.MetadataSettings);
|
||||
int appSettingsMaxDegreeOfParallelism = appSettings.RenameSettings.MaxDegreeOfParallelism;
|
||||
IEnumerable<string> files = appSettingsMaxDegreeOfParallelism == 1 ? Directory.GetFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories);
|
||||
int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count() : 123000;
|
||||
_ProgressBar = new(filesCount, "EnumerateFiles load", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
|
||||
if (appSettingsMaxDegreeOfParallelism == 1)
|
||||
recordACollection.AddRange(GetRecordACollection(logger, renameConfiguration, rename, ids, files, metadata));
|
||||
recordACollection.AddRange(GetRecordACollection(logger, appSettings, rename, ids, files, metadata));
|
||||
else
|
||||
{
|
||||
List<string> distinct = [];
|
||||
List<(bool, FilePath, FileInfo, ExifDirectory, ReadOnlyCollection<FileHolder>)> collection = [];
|
||||
List<MetadataGroup> metadataGroups = [];
|
||||
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
|
||||
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(rename, renameConfiguration, metadata, distinct, collection));
|
||||
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(rename, appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.RenameSettings, metadata, distinct, metadataGroups));
|
||||
if (_ProgressBar.CurrentTick != recordACollection.Count)
|
||||
throw new NotSupportedException();
|
||||
foreach ((bool fastForwardMovingPictureExpertsGroupUsed, FilePath filePath, FileInfo fileInfo, ExifDirectory exifDirectory, ReadOnlyCollection<FileHolder> sidecarFiles) in collection)
|
||||
foreach (MetadataGroup metadataGroup in metadataGroups)
|
||||
{
|
||||
if (fastForwardMovingPictureExpertsGroupUsed || !renameConfiguration.InPlaceMoveDirectory || !renameConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
|
||||
recordACollection.Add(new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, fileInfo, filePath, sidecarFiles));
|
||||
if (metadataGroup.FastForwardMovingPictureExpertsGroupUsed || !appSettings.RenameSettings.InPlaceMoveDirectory || !appSettings.RenameSettings.ValidVideoFormatExtensions.Contains(metadataGroup.FilePath.ExtensionLowered))
|
||||
recordA = new(metadataGroup.ExifDirectory, metadataGroup.FastForwardMovingPictureExpertsGroupUsed, metadataGroup.FileInfo, metadataGroup.FilePath, metadataGroup.SidecarFiles);
|
||||
else
|
||||
recordACollection.Add(new(exifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, fileInfo, filePath, sidecarFiles));
|
||||
recordA = new(metadataGroup.ExifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, metadataGroup.FileInfo, metadataGroup.FilePath, metadataGroup.SidecarFiles);
|
||||
recordACollection.Add(recordA);
|
||||
}
|
||||
}
|
||||
_ProgressBar.Dispose();
|
||||
results = GetRecordBCollection(renameConfiguration.MetadataConfiguration, recordACollection);
|
||||
results = GetRecordBCollection(appSettings, recordACollection);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void VerifyIntMinValueLength(MetadataConfiguration metadataConfiguration, ReadOnlyCollection<RecordB> recordBCollection)
|
||||
private static void VerifyIntMinValueLength(MetadataSettings metadataSettings, ReadOnlyCollection<RecordB> recordBCollection)
|
||||
{
|
||||
foreach (RecordB recordB in recordBCollection)
|
||||
{
|
||||
if (recordB.ExifDirectory.Id is null)
|
||||
if (recordB.ExifDirectory.FilePath.Id is null)
|
||||
continue;
|
||||
if (metadataConfiguration.IntMinValueLength < recordB.ExifDirectory.Id.Value.ToString().Length)
|
||||
if (metadataSettings.IntMinValueLength < recordB.ExifDirectory.FilePath.Id.Value.ToString().Length)
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
@ -312,7 +315,7 @@ public partial class Rename : IRename, IDisposable
|
||||
private static string GetDirectoryName(string year, string tfw, string prefix, string? splat, int seasonValue, string seasonName, string makerSplit) =>
|
||||
splat is null ? $"{prefix}{year} {tfw}{year}.{seasonValue} {seasonName}{makerSplit}" : $"{prefix}{year} {tfw}{year}{splat}";
|
||||
|
||||
private static string? GetCheckDirectory(RenameConfiguration renameConfiguration, RecordB record, ReadOnlyCollection<int> ids, bool multipleDirectoriesWithFiles, string paddedId)
|
||||
private static string? GetCheckDirectory(AppSettings appSettings, RecordB record, ReadOnlyCollection<int> ids, bool multipleDirectoriesWithFiles, string paddedId)
|
||||
{
|
||||
string? result;
|
||||
string year = record.DateTime.Year.ToString();
|
||||
@ -322,7 +325,7 @@ public partial class Rename : IRename, IDisposable
|
||||
else
|
||||
{
|
||||
(bool? isWrongYear, string[] years) = IDate.IsWrongYear(record.FilePath, record.ExifDirectory);
|
||||
if (renameConfiguration.InPlaceMoveDirectory && !record.FilePath.FileNameFirstSegment.Contains(paddedId))
|
||||
if (appSettings.RenameSettings.InPlaceMoveDirectory && !record.FilePath.FileNameFirstSegment.Contains(paddedId))
|
||||
result = null;
|
||||
else
|
||||
{
|
||||
@ -331,9 +334,9 @@ public partial class Rename : IRename, IDisposable
|
||||
string[] segments = checkDirectoryName.Split(years, StringSplitOptions.None);
|
||||
string? splat = checkDirectoryName[^3..][1] == '!' ? checkDirectoryName[^3..] : null;
|
||||
(int seasonValue, string seasonName) = IDate.GetSeason(record.DateTime.DayOfYear);
|
||||
string rootDirectory = renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory;
|
||||
string contains = record.ExifDirectory.Id is null || ids.Contains(record.ExifDirectory.Id.Value) ? "_ Exists _" : "_ New-Destination _";
|
||||
string makerSplit = string.IsNullOrEmpty(maker) ? string.IsNullOrEmpty(renameConfiguration.DefaultMaker) ? string.Empty : renameConfiguration.DefaultMaker : $" {maker.Split(' ')[0]}";
|
||||
string rootDirectory = appSettings.ResultSettings.RootDirectory;
|
||||
string contains = record.ExifDirectory.FilePath.Id is null || ids.Contains(record.ExifDirectory.FilePath.Id.Value) ? "_ Exists _" : "_ New-Destination _";
|
||||
string makerSplit = string.IsNullOrEmpty(maker) ? string.IsNullOrEmpty(appSettings.RenameSettings.DefaultMaker) ? string.Empty : appSettings.RenameSettings.DefaultMaker : $" {maker.Split(' ')[0]}";
|
||||
string directoryName = GetDirectoryName(year, tfw, segments[0], splat, seasonValue, seasonName, makerSplit);
|
||||
result = Path.GetFullPath(Path.Combine(rootDirectory, contains, directoryName));
|
||||
}
|
||||
@ -341,16 +344,17 @@ public partial class Rename : IRename, IDisposable
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<ToDo> GetSidecarFiles(MetadataConfiguration metadataConfiguration, RecordB record, List<string> distinct, string checkDirectory, string paddedId)
|
||||
private static List<ToDo> GetSidecarFiles(AppSettings appSettings, RecordB record, List<string> distinct, string checkDirectory, string paddedId)
|
||||
{
|
||||
List<ToDo> results = [];
|
||||
ToDo toDo;
|
||||
string checkFile;
|
||||
FilePath filePath;
|
||||
string checkFileExtension;
|
||||
foreach (FileHolder fileHolder in record.SidecarFiles)
|
||||
{
|
||||
checkFileExtension = fileHolder.ExtensionLowered;
|
||||
filePath = FilePath.Get(metadataConfiguration, fileHolder, index: null);
|
||||
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index: null);
|
||||
checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
|
||||
if (checkFile == filePath.FullName)
|
||||
continue;
|
||||
@ -363,16 +367,17 @@ public partial class Rename : IRename, IDisposable
|
||||
if (distinct.Contains(checkFile))
|
||||
continue;
|
||||
distinct.Add(checkFile);
|
||||
results.Add(new(checkDirectory, filePath, checkFile, JsonFile: false));
|
||||
toDo = new(checkDirectory, filePath, checkFile, JsonFile: false);
|
||||
results.Add(toDo);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static bool? GetDirectoryCheck(RenameConfiguration renameConfiguration)
|
||||
private static bool? GetDirectoryCheck(ResultSettings resultSettings)
|
||||
{
|
||||
bool? result = null;
|
||||
IEnumerable<string> files;
|
||||
string[] directories = Directory.GetDirectories(renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
string[] directories = Directory.GetDirectories(resultSettings.RootDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string directory in directories)
|
||||
{
|
||||
files = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories);
|
||||
@ -390,9 +395,10 @@ public partial class Rename : IRename, IDisposable
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<ToDo> GetToDoCollection(RenameConfiguration renameConfiguration, ReadOnlyCollection<int> ids, ReadOnlyCollection<RecordB> recordBCollection)
|
||||
private static ReadOnlyCollection<ToDo> GetToDoCollection(AppSettings appSettings, ReadOnlyCollection<int> ids, ReadOnlyCollection<RecordB> recordBCollection)
|
||||
{
|
||||
List<ToDo> results = [];
|
||||
ToDo toDo;
|
||||
RecordB record;
|
||||
string jsonFile;
|
||||
string paddedId;
|
||||
@ -406,18 +412,17 @@ public partial class Rename : IRename, IDisposable
|
||||
List<string> distinct = [];
|
||||
const string jpeg = ".jpeg";
|
||||
string jsonFileSubDirectory;
|
||||
bool? directoryCheck = GetDirectoryCheck(renameConfiguration);
|
||||
MetadataConfiguration metadataConfiguration = renameConfiguration.MetadataConfiguration;
|
||||
VerifyIntMinValueLength(metadataConfiguration, recordBCollection);
|
||||
bool? directoryCheck = GetDirectoryCheck(appSettings.ResultSettings);
|
||||
VerifyIntMinValueLength(appSettings.MetadataSettings, recordBCollection);
|
||||
bool multipleDirectoriesWithFiles = directoryCheck is not null && directoryCheck.Value;
|
||||
ReadOnlyCollection<RecordB> sorted = new((from l in recordBCollection orderby l.DateTime select l).ToArray());
|
||||
ReadOnlyCollection<RecordB> sorted = (from l in recordBCollection orderby l.DateTime select l).ToArray().AsReadOnly();
|
||||
for (int i = 0; i < sorted.Count; i++)
|
||||
{
|
||||
record = sorted[i];
|
||||
if (record.ExifDirectory.Id is null)
|
||||
if (record.ExifDirectory.FilePath.Id is null)
|
||||
continue;
|
||||
paddedId = IId.GetPaddedId(metadataConfiguration, record.ExifDirectory.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i);
|
||||
checkDirectory = GetCheckDirectory(renameConfiguration, record, ids, multipleDirectoriesWithFiles, paddedId);
|
||||
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i);
|
||||
checkDirectory = GetCheckDirectory(appSettings, record, ids, multipleDirectoriesWithFiles, paddedId);
|
||||
if (string.IsNullOrEmpty(checkDirectory))
|
||||
continue;
|
||||
checkFileExtension = record.FilePath.ExtensionLowered == jpeg ? jpg : record.FilePath.ExtensionLowered;
|
||||
@ -431,23 +436,25 @@ public partial class Rename : IRename, IDisposable
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
}
|
||||
(directoryName, _) = IPath.GetDirectoryNameAndIndex(metadataConfiguration.ResultConfiguration, record.ExifDirectory.Id.Value);
|
||||
jsonFile = Path.Combine(jsonFileSubDirectory, directoryName, $"{record.ExifDirectory.Id.Value}{checkFileExtension}.json");
|
||||
(directoryName, _) = IPath.GetDirectoryNameAndIndex(appSettings.ResultSettings, record.ExifDirectory.FilePath.Id.Value);
|
||||
jsonFile = Path.Combine(jsonFileSubDirectory, directoryName, $"{record.ExifDirectory.FilePath.Id.Value}{checkFileExtension}.json");
|
||||
if (record.JsonFile != jsonFile)
|
||||
{
|
||||
fileHolder = FileHolder.Get(record.JsonFile);
|
||||
filePath = FilePath.Get(metadataConfiguration, fileHolder, index: null);
|
||||
results.Add(new(null, filePath, jsonFile, JsonFile: true));
|
||||
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index: null);
|
||||
toDo = new(null, filePath, jsonFile, JsonFile: true);
|
||||
results.Add(toDo);
|
||||
}
|
||||
if (distinct.Contains(checkFile))
|
||||
continue;
|
||||
distinct.Add(checkFile);
|
||||
results.Add(new(checkDirectory, record.FilePath, checkFile, JsonFile: false));
|
||||
toDo = new(checkDirectory, record.FilePath, checkFile, JsonFile: false);
|
||||
results.Add(toDo);
|
||||
if (record.SidecarFiles.Count == 0)
|
||||
continue;
|
||||
results.AddRange(GetSidecarFiles(metadataConfiguration, record, distinct, checkDirectory, paddedId));
|
||||
results.AddRange(GetSidecarFiles(appSettings, record, distinct, checkDirectory, paddedId));
|
||||
}
|
||||
return new(results);
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static void VerifyDirectories(ReadOnlyCollection<ToDo> toDoCollection)
|
||||
@ -463,11 +470,11 @@ public partial class Rename : IRename, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private ReadOnlyCollection<string> RenameFilesInDirectories(RenameConfiguration renameConfiguration, ReadOnlyCollection<ToDo> toDoCollection)
|
||||
private ReadOnlyCollection<string> RenameFilesInDirectories(RenameSettings renameSettings, ReadOnlyCollection<ToDo> toDoCollection)
|
||||
{
|
||||
List<string> results = [];
|
||||
VerifyDirectories(toDoCollection);
|
||||
bool useProgressBar = !renameConfiguration.InPlace && !renameConfiguration.InPlaceWithOriginalName;
|
||||
bool useProgressBar = !renameSettings.InPlace && !renameSettings.InPlaceWithOriginalName;
|
||||
if (useProgressBar)
|
||||
_ProgressBar = new(toDoCollection.Count, "Move Files", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
|
||||
foreach (ToDo toDo in toDoCollection)
|
||||
@ -498,61 +505,58 @@ public partial class Rename : IRename, IDisposable
|
||||
}
|
||||
if (useProgressBar)
|
||||
_ProgressBar?.Dispose();
|
||||
return new(results);
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static void SaveIdentifiersToDisk(long ticks, RenameConfiguration renameConfiguration, ReadOnlyCollection<RecordB> recordBCollection)
|
||||
private static void SaveIdentifiersToDisk(long ticks, AppSettings appSettings, ReadOnlyCollection<RecordB> recordBCollection)
|
||||
{
|
||||
string paddedId;
|
||||
Identifier identifier;
|
||||
List<Identifier> identifiers = [];
|
||||
MetadataConfiguration metadataConfiguration = renameConfiguration.MetadataConfiguration;
|
||||
string aMetadataCollectionDirectory = IResult.GetResultsDateGroupDirectory(metadataConfiguration.ResultConfiguration, nameof(A_Metadata), metadataConfiguration.ResultConfiguration.ResultCollection);
|
||||
string aMetadataCollectionDirectory = IResult.GetResultsDateGroupDirectory(appSettings.ResultSettings, nameof(A_Metadata), appSettings.ResultSettings.ResultCollection);
|
||||
foreach (RecordB record in recordBCollection)
|
||||
{
|
||||
if (record.ExifDirectory.Id is null)
|
||||
if (record.ExifDirectory.FilePath.Id is null)
|
||||
continue;
|
||||
paddedId = IId.GetPaddedId(renameConfiguration.MetadataConfiguration, record.ExifDirectory.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, index: null);
|
||||
identifier = new([], record.HasDateTimeOriginal, record.ExifDirectory.Id.Value, record.FilePath.Length, paddedId, record.DateTime.Ticks);
|
||||
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, index: null);
|
||||
identifier = new([], record.HasDateTimeOriginal, record.ExifDirectory.FilePath.Id.Value, record.FilePath.Length, paddedId, record.DateTime.Ticks);
|
||||
identifiers.Add(identifier);
|
||||
}
|
||||
string json = JsonSerializer.Serialize(identifiers.OrderBy(l => l.PaddedId).ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray);
|
||||
_ = IPath.WriteAllText(Path.Combine(aMetadataCollectionDirectory, $"{ticks}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<int> GetIds(RenameConfiguration renameConfiguration)
|
||||
private static ReadOnlyCollection<int> GetIds(RenameSettings renameSettings)
|
||||
{
|
||||
ReadOnlyCollection<int> results;
|
||||
string? propertyCollectionFile = string.IsNullOrEmpty(renameConfiguration.RelativePropertyCollectionFile) ? null : renameConfiguration.RelativePropertyCollectionFile;
|
||||
string? propertyCollectionFile = string.IsNullOrEmpty(renameSettings.RelativePropertyCollectionFile) ? null : renameSettings.RelativePropertyCollectionFile;
|
||||
string? json = !File.Exists(propertyCollectionFile) ? null : File.ReadAllText(propertyCollectionFile);
|
||||
Identifier[]? identifiers = json is null ? null : JsonSerializer.Deserialize(json, IdentifierCollectionSourceGenerationContext.Default.IdentifierArray);
|
||||
if (identifiers is null && !string.IsNullOrEmpty(renameConfiguration.RelativePropertyCollectionFile))
|
||||
throw new Exception($"Invalid {nameof(renameConfiguration.RelativePropertyCollectionFile)}");
|
||||
if (identifiers is null && !string.IsNullOrEmpty(renameSettings.RelativePropertyCollectionFile))
|
||||
throw new Exception($"Invalid {nameof(renameSettings.RelativePropertyCollectionFile)}");
|
||||
results = identifiers is null ? new([]) : new((from l in identifiers select l.Id).ToArray());
|
||||
return results;
|
||||
}
|
||||
|
||||
private void RenameWork(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks)
|
||||
{
|
||||
ReadOnlyCollection<int> ids = GetIds(appSettings.RenameConfiguration);
|
||||
RenameConfiguration renameConfiguration = appSettings.RenameConfiguration;
|
||||
MetadataConfiguration metadataConfiguration = renameConfiguration.MetadataConfiguration;
|
||||
_ = IPath.DeleteEmptyDirectories(metadataConfiguration.ResultConfiguration.RootDirectory);
|
||||
DirectoryInfo directoryInfo = new(Path.GetFullPath(metadataConfiguration.ResultConfiguration.RootDirectory));
|
||||
ReadOnlyCollection<int> ids = GetIds(appSettings.RenameSettings);
|
||||
_ = IPath.DeleteEmptyDirectories(appSettings.ResultSettings.RootDirectory);
|
||||
DirectoryInfo directoryInfo = new(Path.GetFullPath(appSettings.ResultSettings.RootDirectory));
|
||||
logger?.LogInformation("{Ticks} {RootDirectory}", ticks, directoryInfo.FullName);
|
||||
ReadOnlyCollection<RecordB> recordBCollection = GetRecordBCollection(logger, appSettings, rename, ids, directoryInfo);
|
||||
SaveIdentifiersToDisk(ticks, renameConfiguration, recordBCollection);
|
||||
if (renameConfiguration.InPlace || renameConfiguration.InPlaceWithOriginalName)
|
||||
SaveIdentifiersToDisk(ticks, appSettings, recordBCollection);
|
||||
if (appSettings.RenameSettings.InPlace || appSettings.RenameSettings.InPlaceWithOriginalName)
|
||||
{
|
||||
if (recordBCollection.Count > 0)
|
||||
recordBCollection = new([]);
|
||||
string aMetadataSingletonDirectory = IResult.GetResultsGroupDirectory(metadataConfiguration.ResultConfiguration, nameof(A_Metadata));
|
||||
string aMetadataSingletonDirectory = IResult.GetResultsGroupDirectory(appSettings.ResultSettings, nameof(A_Metadata));
|
||||
_ = IPath.DeleteEmptyDirectories(aMetadataSingletonDirectory);
|
||||
}
|
||||
if (!renameConfiguration.OnlySaveIdentifiersToDisk)
|
||||
if (!appSettings.RenameSettings.OnlySaveIdentifiersToDisk)
|
||||
{
|
||||
ReadOnlyCollection<ToDo> toDoCollection = GetToDoCollection(renameConfiguration, ids, recordBCollection);
|
||||
ReadOnlyCollection<string> lines = RenameFilesInDirectories(renameConfiguration, toDoCollection);
|
||||
ReadOnlyCollection<ToDo> toDoCollection = GetToDoCollection(appSettings, ids, recordBCollection);
|
||||
ReadOnlyCollection<string> lines = RenameFilesInDirectories(appSettings.RenameSettings, toDoCollection);
|
||||
if (lines.Count != 0)
|
||||
{
|
||||
File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines);
|
||||
|
44
Shared/Models/DistanceSettings.cs
Normal file
44
Shared/Models/DistanceSettings.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record DistanceSettings(bool DistanceMoveUnableToMatch,
|
||||
int FaceAreaPermyriad,
|
||||
int FaceConfidencePercent,
|
||||
int FaceDistancePermyriad,
|
||||
string FocusDirectory,
|
||||
string FocusModel,
|
||||
string LinkedAlpha,
|
||||
string LocationContainerDebugDirectory,
|
||||
string LocationContainerDirectoryPattern,
|
||||
int LocationDigits,
|
||||
int LocationFactor,
|
||||
string MappingDefaultName,
|
||||
int[] RangeDaysDeltaTolerance,
|
||||
float[] RangeDistanceTolerance,
|
||||
float[] RangeFaceAreaTolerance,
|
||||
float[] RangeFaceConfidence,
|
||||
bool ReMap,
|
||||
bool SaveIndividually,
|
||||
bool SaveSortingWithoutPerson,
|
||||
string[] SkipNotSkipDirectories,
|
||||
int SkipOlderThanDays,
|
||||
int SkipPersonWithMoreThen,
|
||||
int SortingMaximumPerFaceShouldBeHigh,
|
||||
bool UseExtraPersonKeyCheck)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, DistanceSettingsSourceGenerationContext.Default.DistanceSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(DistanceSettings))]
|
||||
internal partial class DistanceSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
@ -4,15 +4,16 @@ using System.Text.Json.Serialization;
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record ExifDirectory(AviDirectory[] AviDirectories,
|
||||
object? Encoding,
|
||||
ExifDirectoryBase[] ExifBaseDirectories,
|
||||
FileMetadataDirectory[] FileMetadataDirectories,
|
||||
FilePath FilePath,
|
||||
GifHeaderDirectory[] GifHeaderDirectories,
|
||||
GpsDirectory[] GpsDirectories,
|
||||
int? Height,
|
||||
int? Id,
|
||||
JpegDirectory[] JpegDirectories,
|
||||
MakernoteDirectory[] MakernoteDirectories,
|
||||
string OriginalFileName,
|
||||
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
|
||||
PhotoshopDirectory[] PhotoshopDirectories,
|
||||
PngDirectory[] PngDirectories,
|
||||
QuickTimeMovieHeaderDirectory[] QuickTimeMovieHeaderDirectories,
|
||||
@ -27,6 +28,25 @@ public record ExifDirectory(AviDirectory[] AviDirectories,
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ExifDirectory Get(object encoding, ExifDirectory e) =>
|
||||
new(e.AviDirectories,
|
||||
encoding,
|
||||
e.ExifBaseDirectories,
|
||||
e.FileMetadataDirectories,
|
||||
e.FilePath,
|
||||
e.GifHeaderDirectories,
|
||||
e.GpsDirectories,
|
||||
e.Height,
|
||||
e.JpegDirectories,
|
||||
e.MakernoteDirectories,
|
||||
e.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
|
||||
e.PhotoshopDirectories,
|
||||
e.PngDirectories,
|
||||
e.QuickTimeMovieHeaderDirectories,
|
||||
e.QuickTimeTrackHeaderDirectories,
|
||||
e.WebPDirectories,
|
||||
e.Width);
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
|
53
Shared/Models/FaceDistance.cs
Normal file
53
Shared/Models/FaceDistance.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record class FaceDistance : Properties.IFaceDistance
|
||||
{
|
||||
|
||||
public int? ConfidencePercent { init; get; }
|
||||
public DateTime DateTimeOriginalThenMinimumDateTime { init; get; }
|
||||
public object? Encoding { init; get; }
|
||||
public MappingFromFilterPost? MappingFromFilterPost { init; get; }
|
||||
public int? Id { init; get; }
|
||||
public bool? IsWrongYear { init; get; }
|
||||
public double? Length { init; get; }
|
||||
public int? WholePercentages { init; get; }
|
||||
|
||||
[JsonConstructor]
|
||||
public FaceDistance(int? confidencePercent, DateTime dateTimeOriginalThenMinimumDateTime, object? encoding, MappingFromFilterPost? mappingFromFilterPost, int? id, bool? isWrongYear, double? length, int? wholePercentages)
|
||||
{
|
||||
ConfidencePercent = confidencePercent;
|
||||
DateTimeOriginalThenMinimumDateTime = dateTimeOriginalThenMinimumDateTime;
|
||||
Encoding = encoding;
|
||||
MappingFromFilterPost = mappingFromFilterPost;
|
||||
Id = id;
|
||||
IsWrongYear = isWrongYear;
|
||||
Length = length;
|
||||
WholePercentages = wholePercentages;
|
||||
}
|
||||
|
||||
public FaceDistance(int? confidencePercent, DateTime dateTimeOriginalThenMinimumDateTime, object? encoding, MappingFromFilterPost? mappingFromFilterPost, int id, bool? isWrongYear, int? wholePercentages) :
|
||||
this(confidencePercent, dateTimeOriginalThenMinimumDateTime, encoding, mappingFromFilterPost, id, isWrongYear, null, wholePercentages)
|
||||
{ }
|
||||
|
||||
public FaceDistance(FaceDistance faceDistance, double length) :
|
||||
this(faceDistance.ConfidencePercent, faceDistance.DateTimeOriginalThenMinimumDateTime, null, faceDistance.MappingFromFilterPost, faceDistance.Id, faceDistance.IsWrongYear, length, faceDistance.WholePercentages)
|
||||
{ }
|
||||
|
||||
public FaceDistance(object encoding) => Encoding = encoding;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, FaceDistanceSourceGenerationContext.Default.FaceDistance);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(FaceDistance))]
|
||||
internal partial class FaceDistanceSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
22
Shared/Models/FacePartAndFacePointArray.cs
Normal file
22
Shared/Models/FacePartAndFacePointArray.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record FacePartAndFacePointArray(FacePart FacePart, FacePoint[] FacePoints)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, FacePartAndFacePointArraySourceGenerationContext.Default.FacePartAndFacePointArray);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(FacePartAndFacePointArray))]
|
||||
public partial class FacePartAndFacePointArraySourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
22
Shared/Models/FaceRecognitionGroup.cs
Normal file
22
Shared/Models/FaceRecognitionGroup.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record FaceRecognitionGroup(Location Location, ReadOnlyCollection<FaceEncoding?> FaceEncodings, ReadOnlyCollection<ReadOnlyCollection<FacePartAndFacePointArray>> FaceParts)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, FaceRecognitionGroupSourceGenerationContext.Default.FaceRecognitionGroup);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(FaceRecognitionGroup))]
|
||||
public partial class FaceRecognitionGroupSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
@ -51,7 +51,8 @@ public record FileHolder(DateTime? CreationTime,
|
||||
public static FileHolder Get(FilePath filePath, int? id)
|
||||
{
|
||||
FileHolder result;
|
||||
result = new(new(filePath.CreationTicks),
|
||||
DateTime dateTime = new(filePath.CreationTicks);
|
||||
result = new(dateTime,
|
||||
filePath.DirectoryFullPath,
|
||||
true,
|
||||
filePath.ExtensionLowered,
|
||||
|
@ -26,7 +26,7 @@ public record FilePath(long CreationTicks,
|
||||
return result;
|
||||
}
|
||||
|
||||
public static FilePath Get(MetadataConfiguration metadataConfiguration, FileHolder fileHolder, int? index)
|
||||
public static FilePath Get(ResultSettings resultSettings, MetadataSettings metadataSettings, FileHolder fileHolder, int? index)
|
||||
{
|
||||
if (fileHolder.CreationTime is null)
|
||||
fileHolder = FileHolder.Get(fileHolder);
|
||||
@ -40,23 +40,23 @@ public record FilePath(long CreationTicks,
|
||||
int? id;
|
||||
int? sortOder;
|
||||
string fileNameFirstSegment = fileHolder.Name.Split('.')[0];
|
||||
int sortOrderOnlyLengthIndex = metadataConfiguration.Offset.ToString().Length;
|
||||
int sortOrderOnlyLengthIndex = metadataSettings.Offset.ToString().Length;
|
||||
string fileDirectoryFullPath = fileHolder.DirectoryFullPath ?? throw new NullReferenceException();
|
||||
bool isIntelligentIdFormat = IId.NameWithoutExtensionIsIntelligentIdFormat(metadataConfiguration, fileNameFirstSegment);
|
||||
bool isPaddedIntelligentIdFormat = IId.NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment);
|
||||
bool fileNameFirstSegmentIsIdFormat = !isPaddedIntelligentIdFormat && !isIntelligentIdFormat && IId.NameWithoutExtensionIsIdFormat(metadataConfiguration, fileHolder);
|
||||
bool isIntelligentIdFormat = IId.NameWithoutExtensionIsIntelligentIdFormat(metadataSettings, fileNameFirstSegment);
|
||||
bool isPaddedIntelligentIdFormat = IId.NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataSettings, sortOrderOnlyLengthIndex, fileNameFirstSegment);
|
||||
bool fileNameFirstSegmentIsIdFormat = !isPaddedIntelligentIdFormat && !isIntelligentIdFormat && IId.NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder);
|
||||
bool? hasIgnoreKeyword = !isIntelligentIdFormat && !isPaddedIntelligentIdFormat ? null : fileNameFirstSegment[^1] is '2' or '8';
|
||||
bool? hasDateTimeOriginal = !isIntelligentIdFormat && !isPaddedIntelligentIdFormat ? null : fileNameFirstSegment[^1] is '1' or '9';
|
||||
if (!fileNameFirstSegmentIsIdFormat && !isIntelligentIdFormat && !isPaddedIntelligentIdFormat)
|
||||
(id, sortOder) = (null, null);
|
||||
else if (isIntelligentIdFormat)
|
||||
(id, sortOder) = (IId.GetId(metadataConfiguration, fileNameFirstSegment), null);
|
||||
(id, sortOder) = (IId.GetId(resultSettings, metadataSettings, fileNameFirstSegment), null);
|
||||
else if (isPaddedIntelligentIdFormat)
|
||||
{
|
||||
if (!int.TryParse(fileNameFirstSegment[..sortOrderOnlyLengthIndex], out int absoluteValueOfSortOrder))
|
||||
(id, sortOder) = (null, null);
|
||||
else
|
||||
(id, sortOder) = (IId.GetId(metadataConfiguration, fileNameFirstSegment[sortOrderOnlyLengthIndex..]), absoluteValueOfSortOrder);
|
||||
(id, sortOder) = (IId.GetId(resultSettings, metadataSettings, fileNameFirstSegment[sortOrderOnlyLengthIndex..]), absoluteValueOfSortOrder);
|
||||
}
|
||||
else if (fileNameFirstSegmentIsIdFormat)
|
||||
{
|
||||
@ -64,7 +64,7 @@ public record FilePath(long CreationTicks,
|
||||
throw new NullReferenceException(nameof(index));
|
||||
if (!int.TryParse(fileNameFirstSegment, out int valueOfFileNameFirstSegment))
|
||||
throw new NotSupportedException();
|
||||
(id, sortOder) = (valueOfFileNameFirstSegment, metadataConfiguration.Offset + index);
|
||||
(id, sortOder) = (valueOfFileNameFirstSegment, metadataSettings.Offset + index);
|
||||
}
|
||||
else
|
||||
throw new NotSupportedException();
|
||||
|
21
Shared/Models/FilePathAndWholePercentages.cs
Normal file
21
Shared/Models/FilePathAndWholePercentages.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record FilePathAndWholePercentages(FilePath FilePath, int WholePercentages)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, FilePathAndWholePercentagesSourceGenerationContext.Default.FilePathAndWholePercentages);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(FilePathAndWholePercentages))]
|
||||
public partial class FilePathAndWholePercentagesSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
@ -39,4 +39,11 @@ public class Location(int bottom, double confidence, int left, int right, int to
|
||||
|
||||
public static bool operator !=(Location location1, Location location2) => !(location1 == location2);
|
||||
|
||||
public static Location Get(int bottom, double confidence, int height, int left, int right, int top, int width, int zCount)
|
||||
{
|
||||
Location result = new(bottom, confidence, left, right, top);
|
||||
_ = Stateless.Methods.Location.Check(bottom, height, left, right, top, width, zCount, throwException: true);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
49
Shared/Models/LocationContainer.cs
Normal file
49
Shared/Models/LocationContainer.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record LocationContainer(DateOnly? CreationDateOnly,
|
||||
ExifDirectory? ExifDirectory,
|
||||
object? Encoding,
|
||||
FaceFile? FaceFile,
|
||||
FilePath FilePath,
|
||||
int? LengthPermyriad,
|
||||
FilePath? LengthSource,
|
||||
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
|
||||
RectangleF? Rectangle,
|
||||
int? WholePercentages)
|
||||
{
|
||||
|
||||
public static LocationContainer Get(LocationContainer locationContainer, object? encoding, bool keepExifDirectory)
|
||||
{
|
||||
LocationContainer result;
|
||||
result = new(locationContainer.CreationDateOnly,
|
||||
keepExifDirectory ? locationContainer.ExifDirectory : null,
|
||||
encoding,
|
||||
locationContainer.FaceFile,
|
||||
locationContainer.FilePath,
|
||||
locationContainer.LengthPermyriad,
|
||||
locationContainer.LengthSource,
|
||||
locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
|
||||
locationContainer.Rectangle,
|
||||
locationContainer.WholePercentages);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static LocationContainer Get(LocationContainer source, LocationContainer locationContainer, int lengthPermyriad, bool keepExifDirectory, bool keepEncoding)
|
||||
{
|
||||
LocationContainer result;
|
||||
result = new(locationContainer.CreationDateOnly,
|
||||
keepExifDirectory ? locationContainer.ExifDirectory : null,
|
||||
keepEncoding ? locationContainer.Encoding : null,
|
||||
locationContainer.FaceFile,
|
||||
locationContainer.FilePath,
|
||||
lengthPermyriad,
|
||||
source.FilePath,
|
||||
locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
|
||||
locationContainer.Rectangle,
|
||||
locationContainer.WholePercentages);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
23
Shared/Models/MappingFromFilterPost.cs
Normal file
23
Shared/Models/MappingFromFilterPost.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record MappingFromFilterPost(bool? CanReMap,
|
||||
bool? InSkipCollection,
|
||||
bool? IsFocusPerson)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, MappingFromFilterPostGenerationContext.Default.MappingFromFilterPost);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(MappingFromFilterPost))]
|
||||
public partial class MappingFromFilterPostGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record MetadataConfiguration(ResultConfiguration ResultConfiguration,
|
||||
bool ForceMetadataLastWriteTimeToCreationTime,
|
||||
string[] IgnoreRulesKeyWords,
|
||||
int IntMinValueLength,
|
||||
int Offset,
|
||||
bool PropertiesChangedForMetadata)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, MetadataConfigurationSourceGenerationContext.Default.MetadataConfiguration);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(MetadataConfiguration))]
|
||||
internal partial class MetadataConfigurationSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
22
Shared/Models/MetadataGroup.cs
Normal file
22
Shared/Models/MetadataGroup.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record MetadataGroup(bool FastForwardMovingPictureExpertsGroupUsed, FilePath FilePath, FileInfo FileInfo, ExifDirectory ExifDirectory, ReadOnlyCollection<FileHolder> SidecarFiles)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, MetadataGroupSourceGenerationContext.Default.MetadataGroup);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(MetadataGroup))]
|
||||
internal partial class MetadataGroupSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
25
Shared/Models/MetadataSettings.cs
Normal file
25
Shared/Models/MetadataSettings.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record MetadataSettings(bool ForceMetadataLastWriteTimeToCreationTime,
|
||||
string[] IgnoreRulesKeyWords,
|
||||
int IntMinValueLength,
|
||||
int Offset,
|
||||
bool PropertiesChangedForMetadata)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, MetadataSettingsSourceGenerationContext.Default.MetadataSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(MetadataSettings))]
|
||||
internal partial class MetadataSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
24
Shared/Models/PeopleSettings.cs
Normal file
24
Shared/Models/PeopleSettings.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record PeopleSettings(string GenealogicalDataCommunicationFile,
|
||||
string[] JLinks,
|
||||
string PersonBirthdayFormat,
|
||||
string PersonCharacters)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, PeopleSettingsSourceGenerationContext.Default.PeopleSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(PeopleSettings))]
|
||||
internal partial class PeopleSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
21
Shared/Models/PersonBirthday.cs
Normal file
21
Shared/Models/PersonBirthday.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record PersonBirthday(DateTime Value)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, PersonBirthdaySourceGenerationContext.Default.PersonBirthday);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(PersonBirthday))]
|
||||
public partial class PersonBirthdaySourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
48
Shared/Models/PersonContainer.cs
Normal file
48
Shared/Models/PersonContainer.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record PersonContainer(int? ApproximateYears,
|
||||
PersonBirthday[]? Birthdays,
|
||||
ReadOnlyCollection<FilePath> DisplayDirectoryAllFilePaths,
|
||||
string DisplayDirectoryName,
|
||||
long? Key,
|
||||
PersonDirectory? PersonDirectory)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, PersonContainerSourceGenerationContext.Default.PersonContainer);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static PersonContainer Get(int? approximateYears, PersonBirthday[]? birthdays, ReadOnlyCollection<FilePath> displayDirectoryAllFilePaths, string displayDirectoryName, long? key, PersonDirectory? personDirectory) =>
|
||||
new(approximateYears, birthdays, displayDirectoryAllFilePaths, displayDirectoryName, key, personDirectory);
|
||||
|
||||
public static PersonContainer Get(char[] personCharacters, PersonBirthday birthday, string displayDirectoryName, PersonDirectory personDirectory) =>
|
||||
new(Stateless.Methods.IAge.GetApproximateYears(personCharacters, displayDirectoryName), [birthday], new([]), displayDirectoryName, birthday.Value.Ticks, personDirectory);
|
||||
|
||||
public static PersonContainer Get(int? approximateYears, PersonBirthday birthdays, string displayDirectoryName, long key) =>
|
||||
new(approximateYears, [birthdays], new([]), displayDirectoryName, key, null);
|
||||
|
||||
public static PersonContainer Get(int? approximateYears, PersonBirthday birthdays, PersonDirectory? personDirectory, string displayDirectoryName, long key) =>
|
||||
new(approximateYears, [birthdays], new([]), displayDirectoryName, key, personDirectory);
|
||||
|
||||
public static PersonContainer Get(int? approximateYears, ReadOnlyCollection<FilePath> displayDirectoryAllFilePaths, string displayDirectoryName, PersonDirectory? personDirectory) =>
|
||||
new(approximateYears, null, displayDirectoryAllFilePaths, displayDirectoryName, null, personDirectory);
|
||||
|
||||
public static PersonContainer Get(int? approximateYears, PersonBirthday[]? birthdays, ReadOnlyCollection<FilePath> displayDirectoryAllFilePaths, string displayDirectoryName, long? key) =>
|
||||
new(approximateYears, birthdays, displayDirectoryAllFilePaths, displayDirectoryName, key, null);
|
||||
|
||||
public static bool? IsKeyIsMaxBirthday(PersonContainer personContainer) =>
|
||||
personContainer.Birthdays is null || personContainer.Key is null ? null : personContainer.Key.Value == personContainer.Birthdays.First().Value.Ticks;
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(PersonContainer))]
|
||||
public partial class PersonContainerSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
21
Shared/Models/PersonDirectory.cs
Normal file
21
Shared/Models/PersonDirectory.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record PersonDirectory(char Char, string Group, char Status, char Sex, char First)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, PersonDirectorySourceGenerationContext.Default.PersonDirectory);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(PersonDirectory))]
|
||||
public partial class PersonDirectorySourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
21
Shared/Models/PersonKeyFormattedAndPersonBirthday.cs
Normal file
21
Shared/Models/PersonKeyFormattedAndPersonBirthday.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record PersonKeyFormattedAndPersonBirthday(string PersonKeyFormatted, PersonBirthday PersonBirthday)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, PersonKeyFormattedAndPersonBirthdaySourceGenerationContext.Default.PersonKeyFormattedAndPersonBirthday);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(PersonKeyFormattedAndPersonBirthday))]
|
||||
public partial class PersonKeyFormattedAndPersonBirthdaySourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
21
Shared/Models/PersonKeyFormattedAndPersonKeyTicks.cs
Normal file
21
Shared/Models/PersonKeyFormattedAndPersonKeyTicks.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName(string KeyFormatted, long KeyTicks, string? DisplayDirectoryName)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryNameSourceGenerationContext.Default.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName))]
|
||||
public partial class PersonKeyFormattedAndKeyTicksAndDisplayDirectoryNameSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
14
Shared/Models/Properties/ICompareSettings.cs
Normal file
14
Shared/Models/Properties/ICompareSettings.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace View_by_Distance.Shared.Models.Properties;
|
||||
|
||||
public interface ICompareSettings
|
||||
{
|
||||
|
||||
public string FacesFileNameExtension { init; get; }
|
||||
public string FacesHiddenFileNameExtension { init; get; }
|
||||
public string FacesPartsFileNameExtension { init; get; }
|
||||
public string[] IgnoreExtensions { init; get; }
|
||||
public int MaxDegreeOfParallelism { init; get; }
|
||||
public string[] ValidImageFormatExtensions { init; get; }
|
||||
public string[] ValidVideoFormatExtensions { init; get; }
|
||||
|
||||
}
|
15
Shared/Models/Properties/IFaceDistance.cs
Normal file
15
Shared/Models/Properties/IFaceDistance.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace View_by_Distance.Shared.Models.Properties;
|
||||
|
||||
public interface IFaceDistance
|
||||
{
|
||||
|
||||
public int? ConfidencePercent { init; get; }
|
||||
public DateTime DateTimeOriginalThenMinimumDateTime { init; get; }
|
||||
public object? Encoding { init; get; }
|
||||
public MappingFromFilterPost? MappingFromFilterPost { init; get; }
|
||||
public int? Id { init; get; }
|
||||
public bool? IsWrongYear { init; get; }
|
||||
public double? Length { init; get; }
|
||||
public int? WholePercentages { init; get; }
|
||||
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
namespace View_by_Distance.Shared.Models.Properties;
|
||||
|
||||
public interface IRenameConfiguration
|
||||
public interface IRenameSettings
|
||||
{
|
||||
|
||||
public MetadataConfiguration MetadataConfiguration { init; get; }
|
||||
public string[] IgnoreExtensions { init; get; }
|
||||
public bool SkipIdFiles { init; get; }
|
||||
public string[] ValidImageFormatExtensions { init; get; }
|
29
Shared/Models/ReadOnlyCollections.cs
Normal file
29
Shared/Models/ReadOnlyCollections.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record ReadOnlyCollections(ReadOnlyCollection<long> JLinkResolvedPersonKeys,
|
||||
ReadOnlyCollection<PersonContainer> PersonContainers,
|
||||
ReadOnlyCollection<string> PersonKeyFormattedCollection,
|
||||
ReadOnlyDictionary<string, string> PersonKeyFormattedToNewestPersonKeyFormatted,
|
||||
ReadOnlyCollection<long> PersonKeys,
|
||||
ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> SkipCollection,
|
||||
ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> SkipNotSkipCollection,
|
||||
ReadOnlyCollection<string> SkipNotSkipDirectories)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, ReadOnlyCollectionsSourceGenerationContext.Default.ReadOnlyCollections);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonSerializable(typeof(ReadOnlyCollections))]
|
||||
public partial class ReadOnlyCollectionsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record ResultConfiguration(string DateGroup,
|
||||
int EpicYear,
|
||||
string? ModelName,
|
||||
int? NumberOfJitters,
|
||||
int? NumberOfTimesToUpsample,
|
||||
string? PredictorModelName,
|
||||
int ResultAllInOneSubdirectoryLength,
|
||||
string ResultCollection,
|
||||
string ResultContent,
|
||||
string ResultSingleton,
|
||||
string RootDirectory)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, ResultConfigurationSourceGenerationContext.Default.ResultConfiguration);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(ResultConfiguration))]
|
||||
internal partial class ResultConfigurationSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
31
Shared/Models/ResultSettings.cs
Normal file
31
Shared/Models/ResultSettings.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record ResultSettings(string DateGroup,
|
||||
int EpicYear,
|
||||
string ModelName,
|
||||
int NumberOfJitters,
|
||||
int NumberOfTimesToUpsample,
|
||||
string PredictorModelName,
|
||||
int ResultAllInOneSubdirectoryLength,
|
||||
string ResultCollection,
|
||||
string ResultContent,
|
||||
string ResultSingleton,
|
||||
string RootDirectory)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, ResultSettingsSourceGenerationContext.Default.ResultSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(ResultSettings))]
|
||||
internal partial class ResultSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
45
Shared/Models/SaveContainer.cs
Normal file
45
Shared/Models/SaveContainer.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public record SaveContainer(string CheckFile,
|
||||
string Directory,
|
||||
FileHolder? FaceFileHolder,
|
||||
FileHolder? HiddenFaceFileHolder,
|
||||
FileHolder? FacePartsFileHolder,
|
||||
bool MakeAllHidden,
|
||||
FileHolder? ResizedFileHolder,
|
||||
string ShortcutFile)
|
||||
{
|
||||
public static SaveContainer Get(string directory) =>
|
||||
new(string.Empty, directory, null, null, null, false, null, string.Empty);
|
||||
|
||||
public static SaveContainer Get(string directory, string locationContainersFile) =>
|
||||
new(string.Empty, directory, null, null, null, false, FileHolder.Get(locationContainersFile), Path.Combine(directory, $"{Path.GetFileName(locationContainersFile)}.lnk"));
|
||||
|
||||
public static SaveContainer Get(string directory, FileHolder? faceFileHolder, FileHolder? resizedFileHolder, string shortcutFile) =>
|
||||
new(string.Empty, directory, faceFileHolder, null, null, true, resizedFileHolder, shortcutFile);
|
||||
|
||||
public static SaveContainer Get(string checkFile, string directory, FileHolder faceFileHolder) =>
|
||||
new(checkFile, directory, faceFileHolder, null, null, false, null, string.Empty);
|
||||
|
||||
public static SaveContainer Get(FileHolder resizedFileHolder, string checkFile, string directory) =>
|
||||
new(checkFile, directory, null, null, null, false, resizedFileHolder, string.Empty);
|
||||
|
||||
public static SaveContainer Get(string checkFile, string directory, FileHolder? faceFileHolder, FileHolder? hiddenFaceFileHolder, FileHolder? facePartsFileHolder, FileHolder? resizedFileHolder, string shortcutFile) =>
|
||||
new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, false, resizedFileHolder, shortcutFile);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, ResultSettingsSourceGenerationContext.Default.ResultSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(SaveContainer))]
|
||||
internal partial class SaveContainerSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
14
Shared/Models/Stateless/IMapLogic.cs
Normal file
14
Shared/Models/Stateless/IMapLogic.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
public interface IMapLogic
|
||||
{ // ...
|
||||
|
||||
const int CopyNotMappedFaces = 5;
|
||||
const int ForceSingleImage = 3;
|
||||
const int Individually = 6;
|
||||
const int ManualCopy = 4;
|
||||
const int Mapping = 1;
|
||||
const int Sigma = 3;
|
||||
const int Sorting = 2;
|
||||
|
||||
}
|
@ -6,10 +6,10 @@ namespace View_by_Distance.Shared.Models.Stateless;
|
||||
internal abstract class Id
|
||||
{
|
||||
|
||||
internal static bool NameWithoutExtensionIsIdFormat(MetadataConfiguration metadataConfiguration, string fileNameFirstSegment)
|
||||
internal static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment)
|
||||
{
|
||||
bool result;
|
||||
if (fileNameFirstSegment.Length < 5 || fileNameFirstSegment.Length > metadataConfiguration.IntMinValueLength)
|
||||
if (fileNameFirstSegment.Length < 5 || fileNameFirstSegment.Length > metadataSettings.IntMinValueLength)
|
||||
result = false;
|
||||
else
|
||||
{
|
||||
@ -19,13 +19,13 @@ internal abstract class Id
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static int GetId(MetadataConfiguration metadataConfiguration, string intelligentId)
|
||||
internal static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId)
|
||||
{
|
||||
int result;
|
||||
StringBuilder results = new();
|
||||
if (metadataConfiguration.IntMinValueLength < (metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength + 2))
|
||||
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
|
||||
throw new NotSupportedException();
|
||||
for (int i = intelligentId.Length - (metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength + 2); i > -1; i--)
|
||||
for (int i = intelligentId.Length - (resultSettings.ResultAllInOneSubdirectoryLength + 2); i > -1; i--)
|
||||
_ = results.Append(intelligentId[i]);
|
||||
_ = results.Append(intelligentId[^3]).Append(intelligentId[^2]);
|
||||
result = int.Parse(results.ToString());
|
||||
@ -37,12 +37,12 @@ internal abstract class Id
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0060
|
||||
internal static string GetIntelligentId(MetadataConfiguration metadataConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
|
||||
internal static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
|
||||
#pragma warning restore IDE0060
|
||||
{
|
||||
string result;
|
||||
StringBuilder stringBuilder = new();
|
||||
if (metadataConfiguration.IntMinValueLength < (metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength + 2))
|
||||
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
|
||||
throw new NotSupportedException();
|
||||
int key;
|
||||
string value;
|
||||
@ -50,33 +50,33 @@ internal abstract class Id
|
||||
if (id > -1)
|
||||
{
|
||||
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : 9;
|
||||
value = id.ToString().PadLeft(metadataConfiguration.IntMinValueLength, '0');
|
||||
value = id.ToString().PadLeft(metadataSettings.IntMinValueLength, '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : 1;
|
||||
value = id.ToString()[1..].PadLeft(metadataConfiguration.IntMinValueLength, '0');
|
||||
value = id.ToString()[1..].PadLeft(metadataSettings.IntMinValueLength, '0');
|
||||
}
|
||||
for (int i = value.Length - metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength - 1; i > -1; i--)
|
||||
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength - 1; i > -1; i--)
|
||||
_ = stringBuilder.Append(value[i]);
|
||||
for (int i = value.Length - metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength; i < value.Length; i++)
|
||||
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength; i < value.Length; i++)
|
||||
resultAllInOneSubdirectoryChars.Add(value[i]);
|
||||
result = $"{stringBuilder}{string.Join(string.Empty, resultAllInOneSubdirectoryChars)}{key}";
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static string GetPaddedId(MetadataConfiguration metadataConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
|
||||
internal static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
|
||||
{
|
||||
string result;
|
||||
if (metadataConfiguration.Offset < 0)
|
||||
if (metadataSettings.Offset < 0)
|
||||
result = Guid.NewGuid().ToString();
|
||||
else
|
||||
{
|
||||
string intelligentId = GetIntelligentId(metadataConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal);
|
||||
int check = GetId(metadataConfiguration, intelligentId);
|
||||
string intelligentId = GetIntelligentId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal);
|
||||
int check = GetId(resultSettings, metadataSettings, intelligentId);
|
||||
if (check != id)
|
||||
throw new NotSupportedException();
|
||||
result = index is null || metadataConfiguration.Offset == IId.DeterministicHashCode ? intelligentId : $"{metadataConfiguration.Offset + index}{intelligentId}";
|
||||
result = index is null || metadataSettings.Offset == IId.DeterministicHashCode ? intelligentId : $"{metadataSettings.Offset + index}{intelligentId}";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
24
Shared/Models/Stateless/ImageFormat.cs
Normal file
24
Shared/Models/Stateless/ImageFormat.cs
Normal file
@ -0,0 +1,24 @@
|
||||
namespace View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the file format of the image.
|
||||
/// </summary>
|
||||
public enum ImageFormat
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that the bitmap (BMP) image format.
|
||||
/// </summary>
|
||||
Bmp,
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that the Joint Photographic Experts Group (JPEG) image format.
|
||||
/// </summary>
|
||||
Jpeg,
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that the W3C Portable Network Graphics (PNG) image format.
|
||||
/// </summary>
|
||||
Png,
|
||||
|
||||
}
|
46
Shared/Models/Stateless/Methods/Age.cs
Normal file
46
Shared/Models/Stateless/Methods/Age.cs
Normal file
@ -0,0 +1,46 @@
|
||||
namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
internal abstract class Age
|
||||
{
|
||||
|
||||
internal static (int, TimeSpan) GetAge(long minuendTicks, long subtrahendTicks)
|
||||
{
|
||||
TimeSpan result;
|
||||
int years = 0;
|
||||
DateTime check = new(subtrahendTicks);
|
||||
for (int i = 0; i < int.MaxValue; i++)
|
||||
{
|
||||
check = check.AddYears(1);
|
||||
if (check.Ticks > minuendTicks)
|
||||
break;
|
||||
years += 1;
|
||||
}
|
||||
result = new(minuendTicks - check.AddYears(-1).Ticks);
|
||||
return (years, result);
|
||||
}
|
||||
|
||||
internal static (int, TimeSpan) GetAge(long minuendTicks, DateTime subtrahend)
|
||||
{
|
||||
(int years, TimeSpan result) = GetAge(minuendTicks, subtrahend.Ticks);
|
||||
return (years, result);
|
||||
}
|
||||
|
||||
internal static (int, TimeSpan) GetAge(DateTime minuend, DateTime subtrahend)
|
||||
{
|
||||
(int years, TimeSpan result) = GetAge(minuend.Ticks, subtrahend.Ticks);
|
||||
return (years, result);
|
||||
}
|
||||
|
||||
internal static int? GetApproximateYears(char[] personCharacters, string personDisplayDirectoryName)
|
||||
{
|
||||
int? result;
|
||||
const int zero = 0;
|
||||
string[] segments = personDisplayDirectoryName.Split(personCharacters);
|
||||
if (segments.Length == 1 || !int.TryParse(segments[1].Split('-')[zero], out int years))
|
||||
result = null;
|
||||
else
|
||||
result = years;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
26
Shared/Models/Stateless/Methods/IAge.cs
Normal file
26
Shared/Models/Stateless/Methods/IAge.cs
Normal file
@ -0,0 +1,26 @@
|
||||
namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
public interface IAge
|
||||
{ // ...
|
||||
|
||||
(int, TimeSpan) TestStatic_GetAge(long minuendTicks, long subtrahendTicks) =>
|
||||
GetAge(minuendTicks, subtrahendTicks);
|
||||
static (int, TimeSpan) GetAge(long minuendTicks, long subtrahendTicks) =>
|
||||
Age.GetAge(minuendTicks, subtrahendTicks);
|
||||
|
||||
(int, TimeSpan) TestStatic_GetAge(long minuendTicks, DateTime subtrahend) =>
|
||||
GetAge(minuendTicks, subtrahend);
|
||||
static (int, TimeSpan) GetAge(long minuendTicks, DateTime subtrahend) =>
|
||||
Age.GetAge(minuendTicks, subtrahend);
|
||||
|
||||
(int, TimeSpan) TestStatic_GetAge(DateTime minuend, DateTime subtrahend) =>
|
||||
GetAge(minuend, subtrahend);
|
||||
static (int, TimeSpan) GetAge(DateTime minuend, DateTime subtrahend) =>
|
||||
Age.GetAge(minuend, subtrahend);
|
||||
|
||||
int? TestStatic_GetApproximateYears(char[] personCharacters, string personDisplayDirectoryName) =>
|
||||
GetApproximateYears(personCharacters, personDisplayDirectoryName);
|
||||
static int? GetApproximateYears(char[] personCharacters, string personDisplayDirectoryName) =>
|
||||
Age.GetApproximateYears(personCharacters, personDisplayDirectoryName);
|
||||
|
||||
}
|
9
Shared/Models/Stateless/Methods/ICompare.cs
Normal file
9
Shared/Models/Stateless/Methods/ICompare.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
public interface ICompare
|
||||
{
|
||||
|
||||
void Tick();
|
||||
void ConstructProgressBar(int maxTicks, string message);
|
||||
|
||||
}
|
@ -5,23 +5,23 @@ public interface IId
|
||||
|
||||
const int DeterministicHashCode = 9876543;
|
||||
|
||||
static bool IsOffsetDeterministicHashCode(MetadataConfiguration metadataConfiguration) =>
|
||||
metadataConfiguration.Offset == DeterministicHashCode;
|
||||
static bool IsOffsetDeterministicHashCode(MetadataSettings metadataSettings) =>
|
||||
metadataSettings.Offset == DeterministicHashCode;
|
||||
|
||||
string TestStatic_GetIntelligentId(MetadataConfiguration metadataConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
|
||||
GetIntelligentId(metadataConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal);
|
||||
static string GetIntelligentId(MetadataConfiguration metadataConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
|
||||
Id.GetIntelligentId(metadataConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal);
|
||||
string TestStatic_GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
|
||||
GetIntelligentId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal);
|
||||
static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
|
||||
Id.GetIntelligentId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal);
|
||||
|
||||
int TestStatic_GetId(MetadataConfiguration metadataConfiguration, string intelligentId) =>
|
||||
GetId(metadataConfiguration, intelligentId);
|
||||
static int GetId(MetadataConfiguration metadataConfiguration, string intelligentId) =>
|
||||
Id.GetId(metadataConfiguration, intelligentId);
|
||||
int TestStatic_GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
|
||||
GetId(resultSettings, metadataSettings, intelligentId);
|
||||
static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
|
||||
Id.GetId(resultSettings, metadataSettings, intelligentId);
|
||||
|
||||
string TestStatic_GetPaddedId(MetadataConfiguration metadataConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
||||
GetPaddedId(metadataConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
||||
static string GetPaddedId(MetadataConfiguration metadataConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
||||
Id.GetPaddedId(metadataConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
||||
string TestStatic_GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
||||
GetPaddedId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
||||
static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
|
||||
Id.GetPaddedId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
|
||||
|
||||
string TestStatic_GetIgnoreFullPath(FilePath filePath, FileHolder fileHolder) =>
|
||||
GetIgnoreFullPath(filePath, fileHolder);
|
||||
@ -36,22 +36,22 @@ public interface IId
|
||||
Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}2{fileHolder.ExtensionLowered}") :
|
||||
throw new NotSupportedException("Low");
|
||||
|
||||
bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataConfiguration metadataConfiguration, string fileNameFirstSegment) =>
|
||||
NameWithoutExtensionIsIntelligentIdFormat(metadataConfiguration, fileNameFirstSegment);
|
||||
static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataConfiguration metadataConfiguration, string fileNameFirstSegment) =>
|
||||
fileNameFirstSegment.Length - 1 == metadataConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
|
||||
bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
|
||||
NameWithoutExtensionIsIntelligentIdFormat(metadataSettings, fileNameFirstSegment);
|
||||
static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
|
||||
fileNameFirstSegment.Length - 1 == metadataSettings.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
|
||||
|
||||
bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataConfiguration metadataConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
|
||||
NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment);
|
||||
static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataConfiguration metadataConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
|
||||
fileNameFirstSegment.Length == metadataConfiguration.IntMinValueLength + sortOrderOnlyLengthIndex + 1
|
||||
bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
|
||||
NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataSettings, sortOrderOnlyLengthIndex, fileNameFirstSegment);
|
||||
static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
|
||||
fileNameFirstSegment.Length == metadataSettings.IntMinValueLength + sortOrderOnlyLengthIndex + 1
|
||||
&& fileNameFirstSegment[^1] is '1' or '2' or '8' or '9'
|
||||
&& fileNameFirstSegment.All(char.IsNumber);
|
||||
|
||||
bool TestStatic_NameWithoutExtensionIsIdFormat(MetadataConfiguration metadataConfiguration, FileHolder fileHolder) =>
|
||||
NameWithoutExtensionIsIdFormat(metadataConfiguration, fileHolder);
|
||||
static bool NameWithoutExtensionIsIdFormat(MetadataConfiguration metadataConfiguration, FileHolder fileHolder) =>
|
||||
Id.NameWithoutExtensionIsIdFormat(metadataConfiguration, fileHolder.NameWithoutExtension.Split('.')[0]);
|
||||
bool TestStatic_NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
|
||||
NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder);
|
||||
static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
|
||||
Id.NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder.NameWithoutExtension.Split('.')[0]);
|
||||
|
||||
int TestStatic_GetDeterministicHashCode(byte[] value) =>
|
||||
GetDeterministicHashCode(value);
|
||||
|
25
Shared/Models/Stateless/Methods/ILocation.cs
Normal file
25
Shared/Models/Stateless/Methods/ILocation.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
public interface ILocation
|
||||
{
|
||||
|
||||
RectangleF? TestStatic_GetPercentagesRectangle(DistanceSettings distanceSettings, int wholePercentages) =>
|
||||
GetPercentagesRectangle(distanceSettings, wholePercentages);
|
||||
static RectangleF? GetPercentagesRectangle(DistanceSettings distanceSettings, int wholePercentages) =>
|
||||
Location.GetPercentagesRectangle(distanceSettings, wholePercentages);
|
||||
|
||||
Models.Location TestStatic_GetTrimBound(double detectionConfidence, Rectangle rectangle, int width, int height, int facesCount) =>
|
||||
TrimBound(detectionConfidence, rectangle, width, height, facesCount);
|
||||
static Models.Location TrimBound(double detectionConfidence, Rectangle rectangle, int width, int height, int facesCount) =>
|
||||
Models.Location.Get(Math.Min(rectangle.Bottom, height),
|
||||
detectionConfidence,
|
||||
height,
|
||||
Math.Max(rectangle.Left, 0),
|
||||
Math.Min(rectangle.Right, width),
|
||||
Math.Max(rectangle.Top, 0),
|
||||
width,
|
||||
facesCount);
|
||||
|
||||
}
|
28
Shared/Models/Stateless/Methods/IMapping.cs
Normal file
28
Shared/Models/Stateless/Methods/IMapping.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
|
||||
namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
public interface IMapping
|
||||
{ // ...
|
||||
|
||||
int TestStatic_GetAreaPermyriad(int faceAreaPermyriad, int height, Models.Location location, int width)
|
||||
=> GetAreaPermyriad(faceAreaPermyriad, height, location, width);
|
||||
static int GetAreaPermyriad(int faceAreaPermyriad, int height, Models.Location location, int width)
|
||||
=> Mapping.GetAreaPermyriad(faceAreaPermyriad, location.Bottom, height, location.Left, location.Right, location.Top, width);
|
||||
|
||||
int TestStatic_GetAreaPermyriad(int faceAreaPermyriad, int bottom, int height, int left, int right, int top, int width)
|
||||
=> GetAreaPermyriad(faceAreaPermyriad, bottom, height, left, right, top, width);
|
||||
static int GetAreaPermyriad(int faceAreaPermyriad, int bottom, int height, int left, int right, int top, int width)
|
||||
=> Mapping.GetAreaPermyriad(faceAreaPermyriad, bottom, height, left, right, top, width);
|
||||
|
||||
int TestStatic_GetAreaPermyriad(int faceAreaPermyriad, Models.Location location, OutputResolution outputResolution)
|
||||
=> GetAreaPermyriad(faceAreaPermyriad, location, outputResolution);
|
||||
static int GetAreaPermyriad(int faceAreaPermyriad, Models.Location location, OutputResolution outputResolution)
|
||||
=> Mapping.GetAreaPermyriad(faceAreaPermyriad, location.Bottom, outputResolution.Height, location.Left, location.Right, location.Top, outputResolution.Width);
|
||||
|
||||
int? TestStatic_GetWholePercentages(ICompareSettings compareSettings, FilePath filePath) =>
|
||||
GetWholePercentages(compareSettings, filePath);
|
||||
static int? GetWholePercentages(ICompareSettings compareSettings, FilePath filePath) =>
|
||||
Mapping.GetWholePercentages(compareSettings, filePath);
|
||||
|
||||
}
|
@ -61,24 +61,24 @@ public interface IPath
|
||||
static string GetDirectory(string sourceDirectory, int level, string directoryName) =>
|
||||
XPath.GetDirectory(sourceDirectory, level, directoryName);
|
||||
|
||||
(string, int) TestStatic_GetDirectoryNameAndIndex(ResultConfiguration resultConfiguration, FileHolder fileHolder) =>
|
||||
GetDirectoryNameAndIndex(resultConfiguration, fileHolder);
|
||||
static (string, int) GetDirectoryNameAndIndex(ResultConfiguration resultConfiguration, FileHolder fileHolder) =>
|
||||
XPath.GetDirectoryNameAndIndex(resultConfiguration, fileHolder);
|
||||
(string, int) TestStatic_GetDirectoryNameAndIndex(ResultSettings resultSettings, FileHolder fileHolder) =>
|
||||
GetDirectoryNameAndIndex(resultSettings, fileHolder);
|
||||
static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, FileHolder fileHolder) =>
|
||||
XPath.GetDirectoryNameAndIndex(resultSettings, fileHolder);
|
||||
|
||||
(string, int) TestStatic_GetDirectoryNameAndIndex(ResultConfiguration resultConfiguration, FilePath filePath) =>
|
||||
GetDirectoryNameAndIndex(resultConfiguration, filePath);
|
||||
static (string, int) GetDirectoryNameAndIndex(ResultConfiguration resultConfiguration, FilePath filePath) =>
|
||||
XPath.GetDirectoryNameAndIndex(resultConfiguration, filePath);
|
||||
(string, int) TestStatic_GetDirectoryNameAndIndex(ResultSettings resultSettings, FilePath filePath) =>
|
||||
GetDirectoryNameAndIndex(resultSettings, filePath);
|
||||
static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, FilePath filePath) =>
|
||||
XPath.GetDirectoryNameAndIndex(resultSettings, filePath);
|
||||
|
||||
(string, int) TestStatic_GetDirectoryNameAndIndex(ResultConfiguration resultConfiguration, int id) =>
|
||||
GetDirectoryNameAndIndex(resultConfiguration, id);
|
||||
static (string, int) GetDirectoryNameAndIndex(ResultConfiguration resultConfiguration, int id) =>
|
||||
XPath.GetDirectoryNameAndIndex(resultConfiguration, id);
|
||||
(string, int) TestStatic_GetDirectoryNameAndIndex(ResultSettings resultSettings, int id) =>
|
||||
GetDirectoryNameAndIndex(resultSettings, id);
|
||||
static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, int id) =>
|
||||
XPath.GetDirectoryNameAndIndex(resultSettings, id);
|
||||
|
||||
ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> TestStatic_GetKeyValuePairs(ResultConfiguration resultConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
|
||||
GetKeyValuePairs(resultConfiguration, resultsFullGroupDirectory, jsonGroups);
|
||||
static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultConfiguration resultConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
|
||||
XPath.GetKeyValuePairs(resultConfiguration, resultsFullGroupDirectory, jsonGroups);
|
||||
ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> TestStatic_GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
|
||||
GetKeyValuePairs(resultSettings, resultsFullGroupDirectory, jsonGroups);
|
||||
static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
|
||||
XPath.GetKeyValuePairs(resultSettings, resultsFullGroupDirectory, jsonGroups);
|
||||
|
||||
}
|
55
Shared/Models/Stateless/Methods/IPerson.cs
Normal file
55
Shared/Models/Stateless/Methods/IPerson.cs
Normal file
@ -0,0 +1,55 @@
|
||||
namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
public interface IPerson
|
||||
{
|
||||
|
||||
// ...
|
||||
|
||||
static (char, char, char) GetPersonHour(string? personDisplayDirectoryName, int hour) =>
|
||||
hour == 0 ? new('U', 'U', 'U') :
|
||||
hour == 1 ? new('U', 'U', 'U') :
|
||||
hour == 2 ? new('U', 'U', 'U') :
|
||||
hour == 3 ? new('A', 'U', 'Y') :
|
||||
hour == 4 ? new('A', 'F', 'Y') :
|
||||
hour == 5 ? new('A', 'M', 'Y') :
|
||||
hour == 6 ? new('A', 'F', 'N') :
|
||||
hour == 7 ? new('A', 'M', 'N') :
|
||||
hour == 13 ? new('D', 'U', 'Y') :
|
||||
hour == 14 ? new('D', 'F', 'Y') :
|
||||
hour == 15 ? new('D', 'M', 'Y') :
|
||||
hour == 16 ? new('D', 'F', 'N') :
|
||||
hour == 17 ? new('D', 'M', 'N') :
|
||||
throw new NotImplementedException(personDisplayDirectoryName);
|
||||
|
||||
static string GetHourGroup(string? personDisplayDirectoryName, int hour) =>
|
||||
hour == 0 ? "Unknown-Unknown-Unknown" :
|
||||
hour == 1 ? "Unknown-Unknown-Unknown" :
|
||||
hour == 2 ? "Unknown-Unknown-Unknown" :
|
||||
hour == 3 ? "Alive-Unknown-Yes" :
|
||||
hour == 4 ? "Alive-Female-Yes" :
|
||||
hour == 5 ? "Alive-Male-Yes" :
|
||||
hour == 6 ? "Alive-Female-No" :
|
||||
hour == 7 ? "Alive-Male-No" :
|
||||
hour == 13 ? "Dead-Unknown-Yes" :
|
||||
hour == 14 ? "Dead-Female-Yes" :
|
||||
hour == 15 ? "Dead-Male-Yes" :
|
||||
hour == 16 ? "Dead-Female-No" :
|
||||
hour == 17 ? "Dead-Male-No" :
|
||||
throw new NotImplementedException(personDisplayDirectoryName);
|
||||
|
||||
bool TestStatic_IsDefaultName(string personDisplayDirectoryName) =>
|
||||
IsDefaultName(personDisplayDirectoryName);
|
||||
static bool IsDefaultName(string personDisplayDirectoryName) =>
|
||||
personDisplayDirectoryName.Length > 1 && personDisplayDirectoryName[0] == 'X' && personDisplayDirectoryName[1] == '+';
|
||||
|
||||
bool TestStatic_IsDefaultName(Models.PersonContainer personContainer) =>
|
||||
IsDefaultName(personContainer);
|
||||
static bool IsDefaultName(Models.PersonContainer personContainer) =>
|
||||
personContainer.ApproximateYears is null || IsDefaultName(personContainer.DisplayDirectoryName);
|
||||
|
||||
bool TestStatic_IsDefaultName(MappingFromPerson mappingFromPerson) =>
|
||||
IsDefaultName(mappingFromPerson);
|
||||
static bool IsDefaultName(MappingFromPerson mappingFromPerson) =>
|
||||
mappingFromPerson.ApproximateYears is null || IsDefaultName(mappingFromPerson.DisplayDirectoryName);
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user