AA.Compare Project to Match not runToDoCollectionFirst

Removed Layered AppSettings with Nested Objects at First Level
This commit is contained in:
2024-12-28 19:34:09 -07:00
parent 3ff8153393
commit 0215e838e7
110 changed files with 6331 additions and 1275 deletions

3
.gitignore vendored
View File

@ -470,4 +470,5 @@ globalStorage/
Shared/.kanbn
.vscode/Har-Files
.vscode/.UserSecrets/*
Rename/.vscode/.UserSecrets/secrets.json
Compare/.vscode/.UserSecrets/secrets.json

535
.vscode/launch.json vendored
View File

@ -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"
],
@ -344,223 +37,3 @@
}
]
}
// 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
View File

@ -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"
```

View File

@ -30,6 +30,7 @@
"paramref",
"permille",
"permyriad",
"pged",
"Phares",
"Phgtv",
"Photoshop",

139
.vscode/tasks.json vendored
View File

@ -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
View File

@ -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
View 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
View 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);
}
}
}

View 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
{
}

View 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
{
}

View 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
View 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, []);
}
}

View 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>

View File

@ -0,0 +1,3 @@
namespace View_by_Distance.Metadata.Models;
public class C_Resize() { }

View File

@ -0,0 +1,3 @@
namespace View_by_Distance.Metadata.Models;
public class D2_FaceParts() { }

View 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;
}
}

View File

@ -0,0 +1,3 @@
namespace View_by_Distance.Metadata.Models;
public class E_Distance() { }

View 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);
}

View 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();
}
}

View 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();
}
}

View 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();
}
}

View 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();
}
}

View 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)
{ }
}
}
}

View 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();
}
}

View 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();
}
}

View 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
View 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
View File

@ -0,0 +1,3 @@
namespace View_by_Distance.Metadata.Models;
public class D_Face() { }

View 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();
}
}

View 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);
}

View 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>

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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();
}
}

View 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);

View 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";
}

View 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
}

View 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
}

View 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
}

View File

@ -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();

View File

@ -62,7 +62,7 @@ internal static class Base
results.Add(value);
}
}
return new(results);
return results.AsReadOnly();
}
}

View File

@ -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,7 +167,7 @@ internal abstract class Exif
&& xResolution is not null
&& yResolution is null)
continue;
results.Add(new(aperture,
exifDirectoryBase = new(aperture,
applicationNotes,
artist,
bitsPerSample,
@ -209,7 +212,8 @@ internal abstract class Exif
winSubject,
winTitle,
xResolution,
yResolution));
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,
gpsDirectory = new(altitude,
latitude,
latitudeRef,
longitude,
longitudeRef,
timeStamp));
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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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
View 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>

View File

@ -0,0 +1,3 @@
namespace View_by_Distance.Metadata.Models;
public class A2_People() { }

View 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);
}

View 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;
}
}

View 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;
}
}

View File

@ -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)]

View File

@ -1,2 +0,0 @@
[*.cs]
csharp_preserve_single_line_statements = true

View File

@ -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
{
}

View File

@ -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
{
}

View 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
{
}

View File

@ -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);

View File

@ -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);

View 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
{
}

View File

@ -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)]

View 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
{
}

View 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
{
}

View 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
{
}

View File

@ -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,

View File

@ -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();

View 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
{
}

View File

@ -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;
}
}

View 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;
}
}

View 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
{
}

View File

@ -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
{
}

View 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
{
}

View 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
{
}

View 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
{
}

View 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
{
}

View 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
{
}

View 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
{
}

View 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
{
}

View 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
{
}

View 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; }
}

View 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; }
}

View File

@ -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; }

View 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
{
}

View File

@ -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
{
}

View 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
{
}

View 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
{
}

View 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;
}

View File

@ -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;
}

View 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,
}

View 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;
}
}

View 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);
}

View File

@ -0,0 +1,9 @@
namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface ICompare
{
void Tick();
void ConstructProgressBar(int maxTicks, string message);
}

View File

@ -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);

View 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);
}

View 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);
}

View File

@ -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);
}

View 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