<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: hexfloor</title>
    <description>The latest articles on DEV Community by hexfloor (@hexfloor).</description>
    <link>https://dev.to/hexfloor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F623400%2Fc31a13de-3f80-43f2-9274-d52e03b433bf.png</url>
      <title>DEV Community: hexfloor</title>
      <link>https://dev.to/hexfloor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hexfloor"/>
    <language>en</language>
    <item>
      <title>Cybersecurity 101 : https certificate chain</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Sat, 18 Oct 2025 19:57:27 +0000</pubDate>
      <link>https://dev.to/hexfloor/cybersecurity-101-https-certificate-chain-4bon</link>
      <guid>https://dev.to/hexfloor/cybersecurity-101-https-certificate-chain-4bon</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When I get to this subject once again I often see dolorem ipsum in the eyes of interlocutor. I pay my personal tribute to &lt;a href="https://en.wikipedia.org/wiki/Tim_Berners-Lee" rel="noopener noreferrer"&gt;Tim Berners-Lee&lt;/a&gt; who modestly name himself as web developer. Who am I then? Probably web amateur.&lt;br&gt;
HTTP is brilliant. This simple and ingenious protocol brought light to the world.&lt;br&gt;
The question is : what is the link in between the https certificate chain and the icon in the browser near the address bar?&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Let's get the certificate for the &lt;code&gt;dev.to&lt;/code&gt;&lt;br&gt;
What we see here is the chain : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leaf certificate (CN = dev.to)&lt;/li&gt;
&lt;li&gt;Intermediate certificate (CN = GlobalSign Atlas R3 DV TLS CA 2024 Q4)&lt;/li&gt;
&lt;li&gt;Root certificate (CN = GlobalSign Root CA - R3) - implicitly trusted by the browser
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl s_client -connect dev.to:443 -showcerts
Connecting to 151.101.66.217
CONNECTED(00000003)
depth=2 OU=GlobalSign Root CA - R3, O=GlobalSign, CN=GlobalSign
verify return:1
depth=1 C=BE, O=GlobalSign nv-sa, CN=GlobalSign Atlas R3 DV TLS CA 2024 Q4
verify return:1
depth=0 CN=dev.to
verify return:1
---
Certificate chain
 0 s:CN=dev.to
   i:C=BE, O=GlobalSign nv-sa, CN=GlobalSign Atlas R3 DV TLS CA 2024 Q4
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Jan  7 22:00:10 2025 GMT; NotAfter: Feb  8 22:00:09 2026 GMT
-----BEGIN CERTIFICATE-----
MIIGUDCCBTigAwIBAgIQAWQ1KPLfkC70pN8Jc+wF7zANBgkqhkiG9w0BAQsFADBY
MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEuMCwGA1UE
AxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0EgMjAyNCBRNDAeFw0yNTAx
MDcyMjAwMTBaFw0yNjAyMDgyMjAwMDlaMBExDzANBgNVBAMMBmRldi50bzCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOQ2ORjnuRDuJIkWHkjiwUNuu106
2K31kpQ4biv/MyU2aiBFXyfwZA3J3nAVwvWQgfcKGKv4cxc5AMpIn9k6H92+LwyZ
DLmvjTUY0iXgeT06zww7WAkCaVWm4tMrJIIay0FmEefajVgEXiO/eomDPbRP9Wh5
pMYXRhlQ1Xxbi5lV+jZ7YRZ8Qjx0a0dpKW6LTeMx+Ykk41+uZDun0J5S/DdCVdHs
qGQ/i+rGyKaKExROBiveolPbV3nvKmTqHUoNTE9xdx5lmFiX/2ewUNq01dHQSXiK
bunyVILjK2x5mhNSyGUwSUp+rmmSSyulNAN6nVa8OAT63Spzce6qluHAedECAwEA
AaOCA1swggNXMBEGA1UdEQQKMAiCBmRldi50bzAOBgNVHQ8BAf8EBAMCBaAwHQYD
VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBR6wUmxn+wr4zFL
M9Aruej3afnhJjBXBgNVHSAEUDBOMAgGBmeBDAECATBCBgorBgEEAaAyCgEDMDQw
MgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRv
cnkvMAwGA1UdEwEB/wQCMAAwgZ4GCCsGAQUFBwEBBIGRMIGOMEAGCCsGAQUFBzAB
hjRodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9jYS9nc2F0bGFzcjNkdnRsc2Nh
MjAyNHE0MEoGCCsGAQUFBzAChj5odHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29t
L2NhY2VydC9nc2F0bGFzcjNkdnRsc2NhMjAyNHE0LmNydDAfBgNVHSMEGDAWgBRg
kewcAvIO/mNPZctisAIqA1jpszBIBgNVHR8EQTA/MD2gO6A5hjdodHRwOi8vY3Js
Lmdsb2JhbHNpZ24uY29tL2NhL2dzYXRsYXNyM2R2dGxzY2EyMDI0cTQuY3JsMIIB
fwYKKwYBBAHWeQIEAgSCAW8EggFrAWkAdgDLOPcViXyEoURfW8Hd+8lu8ppZzUcK
aQWFsMsUwxRY5wAAAZRCyE72AAAEAwBHMEUCIQDc1mIvrvKoBsv7ydejDlWnQ/0v
+71hHEtT8Re/YDG/vAIgam/Q4rLTXWSdR2nSo6s/D+f9mHViZYcwv8BchIGKbvwA
dgAOV5S8866pPjMbLJkHs/eQ35vCPXEyJd0hqSWsYcVOIQAAAZRCyE9RAAAEAwBH
MEUCIEPOQIDuHMa1CskoxOesIu+0gcknyRqpyUnaFnRLFvMgAiEAsABLJF3sSpMf
ydhKYejLUSpyuoM7VSVNGLckc2eb0/MAdwAlL5TCKynpbp9BGnIHK2lcW1L/l6kN
JUC7/NxR7E3uCwAAAZRCyE/2AAAEAwBIMEYCIQDUVJGQvAx/6q85yuMdGh6DVfwE
xCkmEsyE3XG3zaIn1AIhALeFtcR/RJch/+eFo8mcEtQF301IQBoyDxUYEYWhLRS/
MA0GCSqGSIb3DQEBCwUAA4IBAQBWNAPW1x4TKJPDj6NIlzVhyiodHIWBZMRaPVKc
WWo2UPqqToWup3cxArTzVn6Wq90CHvnXT6bGONM9AE9yaG3ulYzcxd4iUl4p8Kka
vWuZxxxN19aH5cw/L+k4VS+M6egPROBcedMEoO0xvfSllTxMc6UQKMkqlzfxu+Xx
mYSph2SFjTTLMj6Dh/jgW6vz0g/rrtxhctBPYKrDgNlLXwDsRZbXfPlmf4+D0v3k
CbbBKOSwO5TjpkIdMp+fZl6JWzGxBoCVjR8HDcIlCv/qfhiEsJkIV+q2H2a/JDzN
fX/26ksGeatVHK4VfwThfrpBf0tTMlJcoWWYgWNiOJEs7/Gq
-----END CERTIFICATE-----
 1 s:C=BE, O=GlobalSign nv-sa, CN=GlobalSign Atlas R3 DV TLS CA 2024 Q4
   i:OU=GlobalSign Root CA - R3, O=GlobalSign, CN=GlobalSign
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Jul 17 03:09:40 2024 GMT; NotAfter: Jul 17 00:00:00 2026 GMT
-----BEGIN CERTIFICATE-----
MIIEkDCCA3igAwIBAgIRAIF81RQm8t32/1iAIsiFVPYwDQYJKoZIhvcNAQELBQAw
TDEgMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkds
b2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMjQwNzE3MDMwOTQwWhcN
MjYwNzE3MDAwMDAwWjBYMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2ln
biBudi1zYTEuMCwGA1UEAxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0Eg
MjAyNCBRNDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOCRJsRiWdvk
//JkfwxqxdyBjpBRyf9hnvqpK0sGXYq/1lELxkHYFc3sI84sotxVuYPTZef+KILX
cTmvs2SNOvpofDDgWG2HmJcTUgf1F7ZEcsAOzSVwPNZUpqsNL4EbxbbH6Bns2RlV
nA5CygsfjDbCRAl3HRgHcPR+oJ2JbgqY6STT+7Kgbn8ZsQ0nG3iWxLKwd/CbAE/v
Ew9ziXHC7PkTLM1AxIXRRySGoUB+EQ4Vf1s42zrc6nTtyNncIFFDmLgMLBOdIXQQ
H1i17r3hmeUrVbNqPzROFson+Uar41e/dWxJHGl//LKOmuG7H8bO0sj6dX3nD5eM
ITtc11WkukMCAwEAAaOCAV8wggFbMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAU
BggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E
FgQUYJHsHALyDv5jT2XLYrACKgNY6bMwHwYDVR0jBBgwFoAUj/BLf6guRSSuTVD6
Y5qL3uLdG7wwewYIKwYBBQUHAQEEbzBtMC4GCCsGAQUFBzABhiJodHRwOi8vb2Nz
cDIuZ2xvYmFsc2lnbi5jb20vcm9vdHIzMDsGCCsGAQUFBzAChi9odHRwOi8vc2Vj
dXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC9yb290LXIzLmNydDA2BgNVHR8ELzAt
MCugKaAnhiVodHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL3Jvb3QtcjMuY3JsMCEG
A1UdIAQaMBgwCAYGZ4EMAQIBMAwGCisGAQQBoDIKAQMwDQYJKoZIhvcNAQELBQAD
ggEBAD3KE6Hs12ahVEfIugBDwbQA1O9Lf/ZOYuhmKI/o5wGEE4RLSe4mEFIRpF+b
mhCipBau7lDPgMLMJb2dwJEUHVi+nUk36y7B9LcnM4CZr0cYaa4PsTkZtpANfkLa
/35Uitk9p+nmcr6KP0v7CU4yJu2zGawK1TMfeEkmyxxPV9dECIM7hiu5vrIVJo1H
F5TNwODKpVnPIUAVMGkdK8B4Mdbd1FVjkhQlMwEUScl2aAsv6ji1K+j5MwPvTU/a
I5JOoIHyMXnq5TwAClAmZxzZ2YHY2fR/oL/59Qy8U+RaBqgK2/Vf93IwFMyPP836
v2EqJED5yaIwUb45KjP7es1C808=
-----END CERTIFICATE-----
---
Server certificate
subject=CN=dev.to
issuer=C=BE, O=GlobalSign nv-sa, CN=GlobalSign Atlas R3 DV TLS CA 2024 Q4
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3446 bytes and written 538 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-RSA-CHACHA20-POLY1305
Protocol: TLSv1.2
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-CHACHA20-POLY1305
    Session-ID: E52D02DFE2963AC716D7FC6CF0DE0B19CD26ED7DBD8702B730B2A764D61FFB93
    Session-ID-ctx:
    Master-Key: 3997597DF103A8BBA763F6DA1146AF234F5110AD20A9AC9E067B45596CCAE908D1F932D7C2B785790985D287DCF55255
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 86400 (seconds)
    TLS session ticket:
    0000 - 12 b1 47 62 83 d2 07 b1-a9 20 09 da 99 9d 01 79   ..Gb..... .....y
    0010 - 67 99 5f 67 29 d7 ec 01-13 b4 21 8d 19 ca da 13   g._g).....!.....
    0020 - dd 97 b2 69 b9 8c ac 3a-17 af fc 93 05 f6 7a c1   ...i...:......z.
    0030 - f3 67 7b 03 3b 40 39 7e-d3 44 b7 88 2e 17 2d f3   .g{.;@9~.D....-.
    0040 - a1 40 e3 27 15 0a 60 fd-01 18 1b 16 07 9c 1a 9f   .@.'..`.........
    0050 - e5 f2 3e ef 22 00 eb 5d-01 e1 ef a7 13 39 55 b1   ..&amp;gt;."..].....9U.
    0060 - 00 ef 3f b0 97 67 f7 e7-c2 b7 32 1d fe c4 eb d3   ..?..g....2.....
    0070 - e6 43 bc 50 33 ac 0e 98-65 09 4c 82 ae a9 84 e2   .C.P3...e.L.....
    0080 - 81 52 f4 04 ba bd 3d 75-51 4f 0a 36 15 5d b4 38   .R....=uQO.6.].8
    0090 - b3 ee e3 54 81 15 41 55-22 b8 93 d7 30 e0 6c d7   ...T..AU"...0.l.
    00a0 - 4c d2 25 ce a4 90 16 1d-e0 b9 09 c3 7b e6 9b 24   L.%.........{..$
    00b0 - 5a d9 0d 4b 58 ba 6f 07-48 a0 30 7a 0a 72 e8 a4   Z..KX.o.H.0z.r..

    Start Time: 1760808666
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: yes
---


closed

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let's extract the first certificate to &lt;code&gt;dev.to.pem&lt;/code&gt; and the second to &lt;code&gt;intermediate.pem&lt;/code&gt;.&lt;br&gt;
And then verify both :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl verify -CAfile intermediate.pem dev.to.pem
dev.to.pem: OK
:~$ openssl verify intermediate.pem
intermediate.pem: OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  “We need to go deeper” (from Inception, 2010).
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl x509 -in dev.to.pem -noout -text | grep "Signature Algorithm" | head -n 1
        Signature Algorithm: sha256WithRSAEncryption
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following gives that : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hash algorithm is &lt;code&gt;SHA-256&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the public key algorithm is &lt;code&gt;RSA&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;hence we need to verify &lt;code&gt;RSA signature of SHA256(tbsCertificate)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's extract the intermediate certificate public key :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl x509 -in intermediate.pem -pubkey -noout &amp;gt; intermediate_pubkey.pem
:~$ cat intermediate_pubkey.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4JEmxGJZ2+T/8mR/DGrF
3IGOkFHJ/2Ge+qkrSwZdir/WUQvGQdgVzewjziyi3FW5g9Nl5/4ogtdxOa+zZI06
+mh8MOBYbYeYlxNSB/UXtkRywA7NJXA81lSmqw0vgRvFtsfoGezZGVWcDkLKCx+M
NsJECXcdGAdw9H6gnYluCpjpJNP7sqBufxmxDScbeJbEsrB38JsAT+8TD3OJccLs
+RMszUDEhdFHJIahQH4RDhV/WzjbOtzqdO3I2dwgUUOYuAwsE50hdBAfWLXuveGZ
5StVs2o/NE4Wyif5RqvjV791bEkcaX/8so6a4bsfxs7SyPp1fecPl4whO1zXVaS6
QwIDAQAB
-----END PUBLIC KEY-----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extract the &lt;code&gt;tbsCertificate&lt;/code&gt; (to be signed), flash-forward, we need these lines : &lt;code&gt;4:d=1  hl=4 l=1336 cons: SEQUENCE&lt;/code&gt; and &lt;code&gt;1359:d=1  hl=4 l= 257 prim: BIT STRING&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl asn1parse -in dev.to.pem
    0:d=0  hl=4 l=1616 cons: SEQUENCE
    4:d=1  hl=4 l=1336 cons: SEQUENCE
    8:d=2  hl=2 l=   3 cons: cont [ 0 ]
   10:d=3  hl=2 l=   1 prim: INTEGER           :02
   13:d=2  hl=2 l=  16 prim: INTEGER           :01643528F2DF902EF4A4DF0973EC05EF
   31:d=2  hl=2 l=  13 cons: SEQUENCE
   33:d=3  hl=2 l=   9 prim: OBJECT            :sha256WithRSAEncryption
   44:d=3  hl=2 l=   0 prim: NULL
   46:d=2  hl=2 l=  88 cons: SEQUENCE
   48:d=3  hl=2 l=  11 cons: SET
   50:d=4  hl=2 l=   9 cons: SEQUENCE
   52:d=5  hl=2 l=   3 prim: OBJECT            :countryName
   57:d=5  hl=2 l=   2 prim: PRINTABLESTRING   :BE
   61:d=3  hl=2 l=  25 cons: SET
   63:d=4  hl=2 l=  23 cons: SEQUENCE
   65:d=5  hl=2 l=   3 prim: OBJECT            :organizationName
   70:d=5  hl=2 l=  16 prim: PRINTABLESTRING   :GlobalSign nv-sa
   88:d=3  hl=2 l=  46 cons: SET
   90:d=4  hl=2 l=  44 cons: SEQUENCE
   92:d=5  hl=2 l=   3 prim: OBJECT            :commonName
   97:d=5  hl=2 l=  37 prim: PRINTABLESTRING   :GlobalSign Atlas R3 DV TLS CA 2024 Q4
  136:d=2  hl=2 l=  30 cons: SEQUENCE
  138:d=3  hl=2 l=  13 prim: UTCTIME           :250107220010Z
  153:d=3  hl=2 l=  13 prim: UTCTIME           :260208220009Z
  168:d=2  hl=2 l=  17 cons: SEQUENCE
  170:d=3  hl=2 l=  15 cons: SET
  172:d=4  hl=2 l=  13 cons: SEQUENCE
  174:d=5  hl=2 l=   3 prim: OBJECT            :commonName
  179:d=5  hl=2 l=   6 prim: UTF8STRING        :dev.to
  187:d=2  hl=4 l= 290 cons: SEQUENCE
  191:d=3  hl=2 l=  13 cons: SEQUENCE
  193:d=4  hl=2 l=   9 prim: OBJECT            :rsaEncryption
  204:d=4  hl=2 l=   0 prim: NULL
  206:d=3  hl=4 l= 271 prim: BIT STRING
  481:d=2  hl=4 l= 859 cons: cont [ 3 ]
  485:d=3  hl=4 l= 855 cons: SEQUENCE
  489:d=4  hl=2 l=  17 cons: SEQUENCE
  491:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Subject Alternative Name
  496:d=5  hl=2 l=  10 prim: OCTET STRING      [HEX DUMP]:300882066465762E746F
  508:d=4  hl=2 l=  14 cons: SEQUENCE
  510:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Key Usage
  515:d=5  hl=2 l=   1 prim: BOOLEAN           :255
  518:d=5  hl=2 l=   4 prim: OCTET STRING      [HEX DUMP]:030205A0
  524:d=4  hl=2 l=  29 cons: SEQUENCE
  526:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Extended Key Usage
  531:d=5  hl=2 l=  22 prim: OCTET STRING      [HEX DUMP]:301406082B0601050507030106082B06010505070302
  555:d=4  hl=2 l=  29 cons: SEQUENCE
  557:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Subject Key Identifier
  562:d=5  hl=2 l=  22 prim: OCTET STRING      [HEX DUMP]:04147AC149B19FEC2BE3314B33D02BB9E8F769F9E126
  586:d=4  hl=2 l=  87 cons: SEQUENCE
  588:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Certificate Policies
  593:d=5  hl=2 l=  80 prim: OCTET STRING      [HEX DUMP]:304E3008060667810C0102013042060A2B06010401A0320A01033034303206082B06010505070201162668747470733A2F2F7777772E676C6F62616C7369676E2E636F6D2F7265706F7369746F72792F
  675:d=4  hl=2 l=  12 cons: SEQUENCE
  677:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Basic Constraints
  682:d=5  hl=2 l=   1 prim: BOOLEAN           :255
  685:d=5  hl=2 l=   2 prim: OCTET STRING      [HEX DUMP]:3000
  689:d=4  hl=3 l= 158 cons: SEQUENCE
  692:d=5  hl=2 l=   8 prim: OBJECT            :Authority Information Access
  702:d=5  hl=3 l= 145 prim: OCTET STRING      [HEX DUMP]:30818E304006082B060105050730018634687474703A2F2F6F6373702E676C6F62616C7369676E2E636F6D2F63612F677361746C617372336476746C736361323032347134304A06082B06010505073002863E687474703A2F2F7365637572652E676C6F62616C7369676E2E636F6D2F6361636572742F677361746C617372336476746C7363613230323471342E637274
  850:d=4  hl=2 l=  31 cons: SEQUENCE
  852:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 Authority Key Identifier
  857:d=5  hl=2 l=  24 prim: OCTET STRING      [HEX DUMP]:301680146091EC1C02F20EFE634F65CB62B0022A0358E9B3
  883:d=4  hl=2 l=  72 cons: SEQUENCE
  885:d=5  hl=2 l=   3 prim: OBJECT            :X509v3 CRL Distribution Points
  890:d=5  hl=2 l=  65 prim: OCTET STRING      [HEX DUMP]:303F303DA03BA0398637687474703A2F2F63726C2E676C6F62616C7369676E2E636F6D2F63612F677361746C617372336476746C7363613230323471342E63726C
  957:d=4  hl=4 l= 383 cons: SEQUENCE
  961:d=5  hl=2 l=  10 prim: OBJECT            :CT Precertificate SCTs
  973:d=5  hl=4 l= 367 prim: OCTET STRING      [HEX DUMP]:0482016B0169007600CB38F715897C84A1445F5BC1DDFBC96EF29A59CD470A690585B0CB14C31458E70000019442C84EF60000040300473045022100DCD6622FAEF2A806CBFBC9D7A30E55A743FD2FFBBD611C4B53F117BF6031BFBC02206A6FD0E2B2D35D649D4769D2A3AB3F0FE7FD987562658730BFC05C84818A6EFC0076000E5794BCF3AEA93E331B2C9907B3F790DF9BC23D713225DD21A925AC61C54E210000019442C84F510000040300473045022043CE4080EE1CC6B50AC928C4E7AC22EFB481C927C91AA9C949DA16744B16F320022100B0004B245DEC4A931FC9D84A61E8CB512A72BA833B55254D18B72473679BD3F3007700252F94C22B29E96E9F411A72072B695C5B52FF97A90D2540BBFCDC51EC4DEE0B0000019442C84FF60000040300483046022100D4549190BC0C7FEAAF39CAE31D1A1E8355FC04C4292612CC84DD71B7CDA227D4022100B785B5C47F449721FFE785A3C99C12D405DF4D48401A320F15181185A12D14BF
 1344:d=1  hl=2 l=  13 cons: SEQUENCE
 1346:d=2  hl=2 l=   9 prim: OBJECT            :sha256WithRSAEncryption
 1357:d=2  hl=2 l=   0 prim: NULL
 1359:d=1  hl=4 l= 257 prim: BIT STRING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl asn1parse -in dev.to.pem -out dev.to.der -noout -strparse 4

:~$ cat dev.to.der
0�8�d5(�*�H��   s��0
0X1
   0    UBE10U
U60208220009Z0101.0,U%GlobalSign Atlas R3 DV TLS CA 2024 Q40
�0�v.to0*�H��
��p�����H��Cn�]:ح���8n+�3%6j E_'�d
��s9�H��:ݾ/
           �
            ���5�%�y=:�
LOqwe�X��g�Pڴ���Ix�n��T��+ly�R�e0IJ~�i�K+�4z�V�8��*sq��y���[0�W0U��U�6{a|B&amp;lt;tkGi)n�M�1��$�_�d;�ОR�7BU��d?���Ȧ�N+ޢS�Wy�*d�J
�dev.to0U��0U%+0Uz�I���+�1K3�+���i��&amp;amp;0WU P0Ng�
                                              0B
+�2
040+&amp;amp;https://www.globalsign.com/repository/0
                                            U�00�+��0��0+0�4http://ocsp.globalsign.com/ca/gsatlasr3dvtlsca2024q40+0�&amp;gt;http://secure.globalsign.com/cacert/gsatlasr3dvtlsca2024q4.crt0U#0�`����cOe�b�*X�0HUA0?0=�;�9�7http://crl.globalsign.com/ca/gsatlasr3dvtlsca2024q4.crl0�
+�y�o�kiv�8��|��D_[����n�Y�G
i����X��B�N�G0E!��b/�����ףU�C�/��aKS��`1�� jo���]d�Giң�?���ube�0��\���n�vW���&amp;gt;3���ߛ�=q2%�!�%�a�N!�B�OQG0E C�@��Ƶ
%@���Q�M�␦��I�tK� !�K$]�J���Ja��Q*r��;U%M�$sg���w%/��+)�n�A␦r+i\[R���
         �B�O�H0F!�T���
                       �9��␦�U��)&amp;amp;̄�q�͢'�!����D�!�煣ɜ��MH@␦2��-�
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extract the bit string given &lt;code&gt;1359:d=1  hl=4 l= 257 prim: BIT STRING&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl asn1parse -in dev.to.pem -out signature.der -strparse 1359 -noout

:~$ wc -c signature.der
256 signature.der

:~$ cat signature.der
V4��(�Ï�H�5a�*��d�Z=R�Yj6P��N���w1��V~�����O��8�=Orhm���"R^)�␦�k��M�և��?/�8U/���D�\y���1���&amp;lt;Ls�(�*�7��񙄩�d��4�2&amp;gt;����[�����ar�O`�À�K_��%�|�f�����     ��(�;��B2��f^�[1����
��~��W�f�$&amp;lt;�}��Ky�U��~�AKS2R\�e��cb8�,��
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl dgst -sha256 -verify intermediate_pubkey.pem -signature signature.der dev.to.der
Verified OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  “We need to go deeper”
&lt;/h2&gt;

&lt;p&gt;Now know that the critical step here is to understand that verification is the following&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extract the certificate information besides signature &lt;code&gt;dev.to.der&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;hash the certificate information&lt;/li&gt;
&lt;li&gt;apply public key from the intermediate certificate to the signature&lt;/li&gt;
&lt;li&gt;compare both
Let's look again at our certificate :
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl x509 -in dev.to.pem -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            01:64:35:28:f2:df:90:2e:f4:a4:df:09:73:ec:05:ef
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Atlas R3 DV TLS CA 2024 Q4
        Validity
            Not Before: Jan  7 22:00:10 2025 GMT
            Not After : Feb  8 22:00:09 2026 GMT
        Subject: CN=dev.to
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:e4:36:39:18:e7:b9:10:ee:24:89:16:1e:48:e2:
                    c1:43:6e:bb:5d:3a:d8:ad:f5:92:94:38:6e:2b:ff:
                    33:25:36:6a:20:45:5f:27:f0:64:0d:c9:de:70:15:
                    c2:f5:90:81:f7:0a:18:ab:f8:73:17:39:00:ca:48:
                    9f:d9:3a:1f:dd:be:2f:0c:99:0c:b9:af:8d:35:18:
                    d2:25:e0:79:3d:3a:cf:0c:3b:58:09:02:69:55:a6:
                    e2:d3:2b:24:82:1a:cb:41:66:11:e7:da:8d:58:04:
                    5e:23:bf:7a:89:83:3d:b4:4f:f5:68:79:a4:c6:17:
                    46:19:50:d5:7c:5b:8b:99:55:fa:36:7b:61:16:7c:
                    42:3c:74:6b:47:69:29:6e:8b:4d:e3:31:f9:89:24:
                    e3:5f:ae:64:3b:a7:d0:9e:52:fc:37:42:55:d1:ec:
                    a8:64:3f:8b:ea:c6:c8:a6:8a:13:14:4e:06:2b:de:
                    a2:53:db:57:79:ef:2a:64:ea:1d:4a:0d:4c:4f:71:
                    77:1e:65:98:58:97:ff:67:b0:50:da:b4:d5:d1:d0:
                    49:78:8a:6e:e9:f2:54:82:e3:2b:6c:79:9a:13:52:
                    c8:65:30:49:4a:7e:ae:69:92:4b:2b:a5:34:03:7a:
                    9d:56:bc:38:04:fa:dd:2a:73:71:ee:aa:96:e1:c0:
                    79:d1
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                DNS:dev.to
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Subject Key Identifier:
                7A:C1:49:B1:9F:EC:2B:E3:31:4B:33:D0:2B:B9:E8:F7:69:F9:E1:26
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
                Policy: 1.3.6.1.4.1.4146.10.1.3
                  CPS: https://www.globalsign.com/repository/
            X509v3 Basic Constraints: critical
                CA:FALSE
            Authority Information Access:
                OCSP - URI:http://ocsp.globalsign.com/ca/gsatlasr3dvtlsca2024q4
                CA Issuers - URI:http://secure.globalsign.com/cacert/gsatlasr3dvtlsca2024q4.crt
            X509v3 Authority Key Identifier:
                60:91:EC:1C:02:F2:0E:FE:63:4F:65:CB:62:B0:02:2A:03:58:E9:B3
            X509v3 CRL Distribution Points:
                Full Name:
                  URI:http://crl.globalsign.com/ca/gsatlasr3dvtlsca2024q4.crl
            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : CB:38:F7:15:89:7C:84:A1:44:5F:5B:C1:DD:FB:C9:6E:
                                F2:9A:59:CD:47:0A:69:05:85:B0:CB:14:C3:14:58:E7
                    Timestamp : Jan  7 22:00:11.254 2025 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:21:00:DC:D6:62:2F:AE:F2:A8:06:CB:FB:C9:
                                D7:A3:0E:55:A7:43:FD:2F:FB:BD:61:1C:4B:53:F1:17:
                                BF:60:31:BF:BC:02:20:6A:6F:D0:E2:B2:D3:5D:64:9D:
                                47:69:D2:A3:AB:3F:0F:E7:FD:98:75:62:65:87:30:BF:
                                C0:5C:84:81:8A:6E:FC
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 0E:57:94:BC:F3:AE:A9:3E:33:1B:2C:99:07:B3:F7:90:
                                DF:9B:C2:3D:71:32:25:DD:21:A9:25:AC:61:C5:4E:21
                    Timestamp : Jan  7 22:00:11.345 2025 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:20:43:CE:40:80:EE:1C:C6:B5:0A:C9:28:C4:
                                E7:AC:22:EF:B4:81:C9:27:C9:1A:A9:C9:49:DA:16:74:
                                4B:16:F3:20:02:21:00:B0:00:4B:24:5D:EC:4A:93:1F:
                                C9:D8:4A:61:E8:CB:51:2A:72:BA:83:3B:55:25:4D:18:
                                B7:24:73:67:9B:D3:F3
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 25:2F:94:C2:2B:29:E9:6E:9F:41:1A:72:07:2B:69:5C:
                                5B:52:FF:97:A9:0D:25:40:BB:FC:DC:51:EC:4D:EE:0B
                    Timestamp : Jan  7 22:00:11.510 2025 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:46:02:21:00:D4:54:91:90:BC:0C:7F:EA:AF:39:CA:
                                E3:1D:1A:1E:83:55:FC:04:C4:29:26:12:CC:84:DD:71:
                                B7:CD:A2:27:D4:02:21:00:B7:85:B5:C4:7F:44:97:21:
                                FF:E7:85:A3:C9:9C:12:D4:05:DF:4D:48:40:1A:32:0F:
                                15:18:11:85:A1:2D:14:BF
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        56:34:03:d6:d7:1e:13:28:93:c3:8f:a3:48:97:35:61:ca:2a:
        1d:1c:85:81:64:c4:5a:3d:52:9c:59:6a:36:50:fa:aa:4e:85:
        ae:a7:77:31:02:b4:f3:56:7e:96:ab:dd:02:1e:f9:d7:4f:a6:
        c6:38:d3:3d:00:4f:72:68:6d:ee:95:8c:dc:c5:de:22:52:5e:
        29:f0:a9:1a:bd:6b:99:c7:1c:4d:d7:d6:87:e5:cc:3f:2f:e9:
        38:55:2f:8c:e9:e8:0f:44:e0:5c:79:d3:04:a0:ed:31:bd:f4:
        a5:95:3c:4c:73:a5:10:28:c9:2a:97:37:f1:bb:e5:f1:99:84:
        a9:87:64:85:8d:34:cb:32:3e:83:87:f8:e0:5b:ab:f3:d2:0f:
        eb:ae:dc:61:72:d0:4f:60:aa:c3:80:d9:4b:5f:00:ec:45:96:
        d7:7c:f9:66:7f:8f:83:d2:fd:e4:09:b6:c1:28:e4:b0:3b:94:
        e3:a6:42:1d:32:9f:9f:66:5e:89:5b:31:b1:06:80:95:8d:1f:
        07:0d:c2:25:0a:ff:ea:7e:18:84:b0:99:08:57:ea:b6:1f:66:
        bf:24:3c:cd:7d:7f:f6:ea:4b:06:79:ab:55:1c:ae:15:7f:04:
        e1:7e:ba:41:7f:4b:53:32:52:5c:a1:65:98:81:63:62:38:91:
        2c:ef:f1:aa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;tbsCertificate&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl asn1parse -in dev.to.der -inform DER
    0:d=0  hl=4 l=1336 cons: SEQUENCE
    4:d=1  hl=2 l=   3 cons: cont [ 0 ]
    6:d=2  hl=2 l=   1 prim: INTEGER           :02
    9:d=1  hl=2 l=  16 prim: INTEGER           :01643528F2DF902EF4A4DF0973EC05EF
   27:d=1  hl=2 l=  13 cons: SEQUENCE
   29:d=2  hl=2 l=   9 prim: OBJECT            :sha256WithRSAEncryption
   40:d=2  hl=2 l=   0 prim: NULL
   42:d=1  hl=2 l=  88 cons: SEQUENCE
   44:d=2  hl=2 l=  11 cons: SET
   46:d=3  hl=2 l=   9 cons: SEQUENCE
   48:d=4  hl=2 l=   3 prim: OBJECT            :countryName
   53:d=4  hl=2 l=   2 prim: PRINTABLESTRING   :BE
   57:d=2  hl=2 l=  25 cons: SET
   59:d=3  hl=2 l=  23 cons: SEQUENCE
   61:d=4  hl=2 l=   3 prim: OBJECT            :organizationName
   66:d=4  hl=2 l=  16 prim: PRINTABLESTRING   :GlobalSign nv-sa
   84:d=2  hl=2 l=  46 cons: SET
   86:d=3  hl=2 l=  44 cons: SEQUENCE
   88:d=4  hl=2 l=   3 prim: OBJECT            :commonName
   93:d=4  hl=2 l=  37 prim: PRINTABLESTRING   :GlobalSign Atlas R3 DV TLS CA 2024 Q4
  132:d=1  hl=2 l=  30 cons: SEQUENCE
  134:d=2  hl=2 l=  13 prim: UTCTIME           :250107220010Z
  149:d=2  hl=2 l=  13 prim: UTCTIME           :260208220009Z
  164:d=1  hl=2 l=  17 cons: SEQUENCE
  166:d=2  hl=2 l=  15 cons: SET
  168:d=3  hl=2 l=  13 cons: SEQUENCE
  170:d=4  hl=2 l=   3 prim: OBJECT            :commonName
  175:d=4  hl=2 l=   6 prim: UTF8STRING        :dev.to
  183:d=1  hl=4 l= 290 cons: SEQUENCE
  187:d=2  hl=2 l=  13 cons: SEQUENCE
  189:d=3  hl=2 l=   9 prim: OBJECT            :rsaEncryption
  200:d=3  hl=2 l=   0 prim: NULL
  202:d=2  hl=4 l= 271 prim: BIT STRING
  477:d=1  hl=4 l= 859 cons: cont [ 3 ]
  481:d=2  hl=4 l= 855 cons: SEQUENCE
  485:d=3  hl=2 l=  17 cons: SEQUENCE
  487:d=4  hl=2 l=   3 prim: OBJECT            :X509v3 Subject Alternative Name
  492:d=4  hl=2 l=  10 prim: OCTET STRING      [HEX DUMP]:300882066465762E746F
  504:d=3  hl=2 l=  14 cons: SEQUENCE
  506:d=4  hl=2 l=   3 prim: OBJECT            :X509v3 Key Usage
  511:d=4  hl=2 l=   1 prim: BOOLEAN           :255
  514:d=4  hl=2 l=   4 prim: OCTET STRING      [HEX DUMP]:030205A0
  520:d=3  hl=2 l=  29 cons: SEQUENCE
  522:d=4  hl=2 l=   3 prim: OBJECT            :X509v3 Extended Key Usage
  527:d=4  hl=2 l=  22 prim: OCTET STRING      [HEX DUMP]:301406082B0601050507030106082B06010505070302
  551:d=3  hl=2 l=  29 cons: SEQUENCE
  553:d=4  hl=2 l=   3 prim: OBJECT            :X509v3 Subject Key Identifier
  558:d=4  hl=2 l=  22 prim: OCTET STRING      [HEX DUMP]:04147AC149B19FEC2BE3314B33D02BB9E8F769F9E126
  582:d=3  hl=2 l=  87 cons: SEQUENCE
  584:d=4  hl=2 l=   3 prim: OBJECT            :X509v3 Certificate Policies
  589:d=4  hl=2 l=  80 prim: OCTET STRING      [HEX DUMP]:304E3008060667810C0102013042060A2B06010401A0320A01033034303206082B06010505070201162668747470733A2F2F7777772E676C6F62616C7369676E2E636F6D2F7265706F7369746F72792F
  671:d=3  hl=2 l=  12 cons: SEQUENCE
  673:d=4  hl=2 l=   3 prim: OBJECT            :X509v3 Basic Constraints
  678:d=4  hl=2 l=   1 prim: BOOLEAN           :255
  681:d=4  hl=2 l=   2 prim: OCTET STRING      [HEX DUMP]:3000
  685:d=3  hl=3 l= 158 cons: SEQUENCE
  688:d=4  hl=2 l=   8 prim: OBJECT            :Authority Information Access
  698:d=4  hl=3 l= 145 prim: OCTET STRING      [HEX DUMP]:30818E304006082B060105050730018634687474703A2F2F6F6373702E676C6F62616C7369676E2E636F6D2F63612F677361746C617372336476746C736361323032347134304A06082B06010505073002863E687474703A2F2F7365637572652E676C6F62616C7369676E2E636F6D2F6361636572742F677361746C617372336476746C7363613230323471342E637274
  846:d=3  hl=2 l=  31 cons: SEQUENCE
  848:d=4  hl=2 l=   3 prim: OBJECT            :X509v3 Authority Key Identifier
  853:d=4  hl=2 l=  24 prim: OCTET STRING      [HEX DUMP]:301680146091EC1C02F20EFE634F65CB62B0022A0358E9B3
  879:d=3  hl=2 l=  72 cons: SEQUENCE
  881:d=4  hl=2 l=   3 prim: OBJECT            :X509v3 CRL Distribution Points
  886:d=4  hl=2 l=  65 prim: OCTET STRING      [HEX DUMP]:303F303DA03BA0398637687474703A2F2F63726C2E676C6F62616C7369676E2E636F6D2F63612F677361746C617372336476746C7363613230323471342E63726C
  953:d=3  hl=4 l= 383 cons: SEQUENCE
  957:d=4  hl=2 l=  10 prim: OBJECT            :CT Precertificate SCTs
  969:d=4  hl=4 l= 367 prim: OCTET STRING      [HEX DUMP]:0482016B0169007600CB38F715897C84A1445F5BC1DDFBC96EF29A59CD470A690585B0CB14C31458E70000019442C84EF60000040300473045022100DCD6622FAEF2A806CBFBC9D7A30E55A743FD2FFBBD611C4B53F117BF6031BFBC02206A6FD0E2B2D35D649D4769D2A3AB3F0FE7FD987562658730BFC05C84818A6EFC0076000E5794BCF3AEA93E331B2C9907B3F790DF9BC23D713225DD21A925AC61C54E210000019442C84F510000040300473045022043CE4080EE1CC6B50AC928C4E7AC22EFB481C927C91AA9C949DA16744B16F320022100B0004B245DEC4A931FC9D84A61E8CB512A72BA833B55254D18B72473679BD3F3007700252F94C22B29E96E9F411A72072B695C5B52FF97A90D2540BBFCDC51EC4DEE0B0000019442C84FF60000040300483046022100D4549190BC0C7FEAAF39CAE31D1A1E8355FC04C4292612CC84DD71B7CDA227D4022100B785B5C47F449721FFE785A3C99C12D405DF4D48401A320F15181185A12D14BF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comput &lt;code&gt;SHA-256&lt;/code&gt; of the &lt;code&gt;tbsCertificate&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl dgst -sha256 dev.to.der
SHA2-256(dev.to.der)= 37058d7b5a35ebe02ea6baf4b267fadf4474c0f87116e16ef870d57b47ef9d09
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's decrypt the signature with the public key of the intermediate certificate :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ openssl pkeyutl -verifyrecover -pubin -inkey intermediate_pubkey.pem -in signature.der -pkeyopt rsa_padding_mode:none -hexdump
0000 - 00 01 ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0010 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0020 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0030 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0040 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0050 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0060 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0070 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0080 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
0090 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00a0 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00b0 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff   ................
00c0 - ff ff ff ff ff ff ff ff-ff ff ff ff 00 30 31 30   .............010
00d0 - 0d 06 09 60 86 48 01 65-03 04 02 01 05 00 04 20   ...`.H.e.......
00e0 - 37 05 8d 7b 5a 35 eb e0-2e a6 ba f4 b2 67 fa df   7..{Z5.......g..
00f0 - 44 74 c0 f8 71 16 e1 6e-f8 70 d5 7b 47 ef 9d 09   Dt..q..n.p.{G...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you may see in the lines &lt;code&gt;00e0&lt;/code&gt; and &lt;code&gt;00f0&lt;/code&gt; are matching with the signature of the &lt;code&gt;tbsCertificate&lt;/code&gt;.&lt;br&gt;
Bingo!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Here above you may see a simple example how to manually verify the certificate validity : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extract the &lt;code&gt;tbsCertificate&lt;/code&gt; chunk from the leaf certificate&lt;/li&gt;
&lt;li&gt;compute hash&lt;/li&gt;
&lt;li&gt;extract public key from the intermediate certificate&lt;/li&gt;
&lt;li&gt;decrypt signature of the leaf certificate&lt;/li&gt;
&lt;li&gt;compare both - if they are matching then we are good
Then there is a question where is the certificate store with the root CA located ? Well, this is product dependent and system dependent.
Technology at the present level looks like magic, still it's essential to understand how the internet works. 
Cheers!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cybersecurity</category>
      <category>webdev</category>
      <category>programming</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Cybersecurity 101 : data sanitization</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Sat, 18 Oct 2025 14:56:37 +0000</pubDate>
      <link>https://dev.to/hexfloor/cybersecurity-101-data-sanitization-cgo</link>
      <guid>https://dev.to/hexfloor/cybersecurity-101-data-sanitization-cgo</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TL;DR : use HMAC-SHA256&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every once in a while there is a need to perform data sanitization on &lt;a href="https://en.wikipedia.org/wiki/Personal_data" rel="noopener noreferrer"&gt;personal data&lt;/a&gt; to be in compliance with local regulations and with the common sense.&lt;br&gt;
Unless you are working for &lt;a href="https://en.wikipedia.org/wiki/Kong%C5%8D_Gumi" rel="noopener noreferrer"&gt;Kongo Gumi&lt;/a&gt; and even though if you have doubts on whether to keep the data - consider that following the information theory all data is asymptotically public or deleted.&lt;br&gt;
Hence the idea listed below is in no sense a guidance to the internal policies, just a practical method to achieve one goal : replace id that is qualified as a personal data by the id that is a technical construct, which is handy for the machine learning, statistics, business intelligence.&lt;/p&gt;
&lt;h2&gt;
  
  
  Q-Day
&lt;/h2&gt;

&lt;p&gt;It's no more a question &lt;code&gt;if&lt;/code&gt;, and just a question &lt;code&gt;when&lt;/code&gt;. Designing a system right now requires consideration of the recent achievements in the domain of &lt;a href="https://en.wikipedia.org/wiki/Post-quantum_cryptography" rel="noopener noreferrer"&gt;post-quantum cryptography&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Hashing
&lt;/h2&gt;

&lt;p&gt;Let's consider a simple use case with vehicle registration plates : we get the incoming flux of data from the speed cameras and we wish to understand the patterns leading to the excessive speeding, in this context it's a good idea to replace the license plate number by a surrogate.&lt;br&gt;
The first idea that comes in mind is to apply hashing with &lt;code&gt;SHA-256&lt;/code&gt;.&lt;br&gt;
Let's see what could be the caveats.&lt;br&gt;
Input : &lt;code&gt;AA01AA0001&lt;/code&gt;&lt;br&gt;
SHA-256 :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ echo -n "AA01AA0001" | sha256sum
557db30f40ad709d5e075eab6b52913faadb40c36f1e29c60cfd40756e9374c7  -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DISCLAIMER : Only test hashes you are authorized to test. Unauthorized cracking is illegal and unethical.&lt;/strong&gt;&lt;br&gt;
Let's get &lt;a href="https://www.kali.org/" rel="noopener noreferrer"&gt;kali linux&lt;/a&gt; with &lt;a href="https://hashcat.net/hashcat/" rel="noopener noreferrer"&gt;hashcat&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install hashcat
hashcat --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing with 4 digits mask :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ echo -n "AA01AA0001" | sha256sum | cut -d ' ' -f1 &amp;gt; hash.txt
:~$ hashcat -m 1400 -a 3 hash.txt AA01AA?d?d?d?d --status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;output :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;557db30f40ad709d5e075eab6b52913faadb40c36f1e29c60cfd40756e9374c7:AA01AA0001
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1400 (SHA2-256)
Hash.Target......: 557db30f40ad709d5e075eab6b52913faadb40c36f1e29c60cf...9374c7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing with full mask will take more time, you may test it yourself to see the estimate on your system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ hashcat -m 1400 -a 3 hash.txt ?u?u?d?d?u?u?d?d?d?d --status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point you can speedup the work by using wordlist or &lt;a href="https://en.wikipedia.org/wiki/Rainbow_table" rel="noopener noreferrer"&gt;rainbow table&lt;/a&gt;, this is out of scope for this guide, the main outcome is that knowing the pattern you may find the original id that has undergone hashing, hence hashing itself is not suitable for data sanitization.&lt;/p&gt;

&lt;h2&gt;
  
  
  HMAC
&lt;/h2&gt;

&lt;p&gt;The much better alternative which is immune to the procedure listed above and is inherently quantum resistant is &lt;a href="https://en.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC&lt;/a&gt; with sufficiently long key, I will be using the standard variation &lt;code&gt;HMAC-SHA256&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:~$ echo -n 'AA01AA0001' | openssl dgst -sha256 -mac HMAC -macopt hexkey:0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff
SHA2-256(stdin)= 459d458f308f01b165169bf6ec32d0c8cb2af38be91b26bde73c8121a11451e1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key above is predictable and hence for demo purposes only, use high-entropy random key for the real case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Use of &lt;code&gt;HMAC-SHA256&lt;/code&gt; for data sanitization purposes is a more secure alternative to a simple hashing with &lt;code&gt;SHA256&lt;/code&gt; as the data pattern is usually publicly known.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;Hashing with &lt;code&gt;SHA256&lt;/code&gt; is not suitable for password imprints either, internet does remember the cases when industry leaders had undergone severe damage due to the storing of simple hashes in the database.&lt;br&gt;
Yes, you may find a good key derivation function, still it's much better solution to use &lt;a href="https://en.wikipedia.org/wiki/OAuth" rel="noopener noreferrer"&gt;OAuth&lt;/a&gt; standard with an &lt;a href="https://en.wikipedia.org/wiki/List_of_OAuth_providers" rel="noopener noreferrer"&gt;authorization server&lt;/a&gt;.&lt;br&gt;
Happy Diwali!&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>data</category>
      <category>linux</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Snyk and Sonar : committed credentials security test</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Sun, 20 Apr 2025 11:43:45 +0000</pubDate>
      <link>https://dev.to/hexfloor/snyk-and-sonar-committed-credentials-security-test-5719</link>
      <guid>https://dev.to/hexfloor/snyk-and-sonar-committed-credentials-security-test-5719</guid>
      <description>&lt;p&gt;&lt;code&gt;Disclaimer : this guide deliberately contains an Easter egg - easy to spot security issue.&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I've been looking closely to understand the market of the code quality and security providers and got to know the leaders :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://snyk.io/" rel="noopener noreferrer"&gt;Snyk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sonarsource.com/" rel="noopener noreferrer"&gt;Sonar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are both great and are different to certain extent even in terms of pricing model, it's up to you which security provider you prefer for your needs, it's quite certain you need an Enterprise version to protect the business and you definitely need more overall security oriented providers as well, see the Swiss cheese model.&lt;br&gt;
When having been watching some sort of performance around the security subject I have realized that one trivial use-case is quite new for me : committed credentials to the repository, I've witnessed this happening and every time it has been either a design imperfection or a necessity, at the same time the recovery from such an issue is not trivial, that is what I will cover in this guide.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;I will get help from two AI assistants today - let me introduce &lt;a href="https://chatgpt.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; and &lt;a href="https://mistral.ai/" rel="noopener noreferrer"&gt;Mistral&lt;/a&gt;, I hope they will harmonically enhance my both cerebral hemispheres and we get fast to the point and we will have some fun. They have suggested me to use &lt;a href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux" rel="noopener noreferrer"&gt;WSL&lt;/a&gt; with &lt;a href="https://www.kali.org/" rel="noopener noreferrer"&gt;Kali Linux&lt;/a&gt; for this purpose.&lt;/p&gt;

&lt;p&gt;Create the demo project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir demo-project
cd demo-project
git config --global init.defaultBranch main
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git init
touch index.js
git add -A
git commit -m 'init'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and now I will add a sample of credentials for &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, &lt;a href="https://azure.microsoft.com/" rel="noopener noreferrer"&gt;Azure&lt;/a&gt; and &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;GCP&lt;/a&gt; to &lt;code&gt;index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF &amp;gt; index.js
// index.js

// Basic Password
const password = 'SecurePass123!';

// AWS Credentials
const awsCredentials = {
  accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
  secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
};

// Azure Credentials
const azureCredentials = {
  subscriptionId: 'dummy_subscription_id',
  clientId: 'dummy_client_id',
  secret: 'dummy_secret',
  tenant: 'dummy_tenant_id'
};

// GCP Credentials
const gcpCredentials = {
  type: 'service_account',
  project_id: 'dummy-project-id',
  private_key_id: 'dummy_private_key_id',
  private_key: '-----BEGIN PRIVATE KEY-----\nDUMMY_PRIVATE_KEY\n-----END PRIVATE KEY-----\n',
  client_email: 'dummy-service-account@dummy-project-id.iam.gserviceaccount.com',
  client_id: 'dummy_client_id',
  auth_uri: 'https://accounts.google.com/o/oauth2/auth',
  token_uri: 'https://oauth2.googleapis.com/token',
  auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs',
  client_x509_cert_url: 'https://www.googleapis.com/robot/v1/metadata/x509/dummy-service-account%40dummy-project-id.iam.gserviceaccount.com'
};

// Export the credentials and password
module.exports = {
  password,
  awsCredentials,
  azureCredentials,
  gcpCredentials
};
EOF

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add -A
git commit -m 'security-issue'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ivw5m98rh11uitavz5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ivw5m98rh11uitavz5k.png" alt="Image description" width="800" height="46"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Snyk
&lt;/h2&gt;

&lt;p&gt;I need &lt;a href="https://nodejs.org/en/download" rel="noopener noreferrer"&gt;node&lt;/a&gt; to be installed, hence I will just follow the instructions and get &lt;code&gt;nvm&lt;/code&gt; first.&lt;br&gt;
Then I've got some help from AI :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g snyk
snyk --version
snyk code test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;at this point I need to create a test account :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwya8kpsmk6rrbfw849ul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwya8kpsmk6rrbfw849ul.png" alt="Image description" width="800" height="213"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;snyk auth
snyk code test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb8wdchc7s5lo3oqi7576.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb8wdchc7s5lo3oqi7576.png" alt="Image description" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's enable &lt;code&gt;snyk code&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu6npyb794xb7l3ar4wr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu6npyb794xb7l3ar4wr.png" alt="Image description" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And see what we have :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ctvtqttirj00rd2hfau.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ctvtqttirj00rd2hfau.png" alt="Image description" width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OK, good start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sonar
&lt;/h2&gt;

&lt;p&gt;Let's &lt;a href="https://www.kali.org/docs/containers/installing-docker-on-kali/" rel="noopener noreferrer"&gt;install docker&lt;/a&gt;&lt;br&gt;
and the follow the &lt;a href="https://docs.sonarsource.com/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -d --name sonarqube -e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true -p 9000:9000 sonarqube:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and install Sonar CLI (ad-hoc solution)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install unzip wget -y
wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip
unzip sonar-scanner-cli-*.zip
sudo mkdir /opt/sonar-scanner
sudo mv sonar-scanner-* /opt/sonar-scanner
echo 'export PATH=$PATH:/opt/sonar-scanner/sonar-scanner-5.0.1.3006-linux/bin' &amp;gt;&amp;gt; ~/.bashrc
source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open sonar : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://localhost:9000" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;login + change password&lt;/li&gt;
&lt;li&gt;My Account -&amp;gt; Security -&amp;gt; Generate Tokens&lt;/li&gt;
&lt;li&gt;generate &lt;code&gt;sonar-scanner-token&lt;/code&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ykhieo0gqqqch3salhs.png" alt="Image description" width="800" height="289"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;create Sonar configuration file with the generated token :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF &amp;gt; sonar-project.properties
sonar.projectKey=demo-project
sonar.projectName=Demo Project
sonar.projectVersion=1.0
sonar.sources=.
sonar.host.url=http://localhost:9000
sonar.login=&amp;lt;token&amp;gt;
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now run the scanner :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sonar-scanner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibc17m2bn76lzld67oav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibc17m2bn76lzld67oav.png" alt="Image description" width="800" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xkk31hwqjxhf98r0zlz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xkk31hwqjxhf98r0zlz.png" alt="Image description" width="619" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bajmms4inpyzzgb9lc3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bajmms4inpyzzgb9lc3.png" alt="Image description" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will commit the setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add -A
git commit -m 'sonar-setup'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Issue
&lt;/h2&gt;

&lt;p&gt;Alright we have done a mistake, let's try to perform a recovery, I will just revert the commit leading to the security issue, add another line to the &lt;code&gt;index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "console.log('demo');" &amp;gt;&amp;gt; index.js
git commit -m 'console-log'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc08qh49dlf7xnwhtk6s0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc08qh49dlf7xnwhtk6s0.png" alt="Image description" width="800" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snyk&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsay7uw1zzp2b2384gm4t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsay7uw1zzp2b2384gm4t.png" alt="Image description" width="517" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sonar&lt;/strong&gt;&lt;br&gt;
OK we have another issue, I didn't expect that, this was my Easter egg, funny. However we have no initial credentials related issue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbuirm4d6ldn1plpcp42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbuirm4d6ldn1plpcp42.png" alt="Image description" width="800" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Is the problem solved ?&lt;br&gt;
&lt;strong&gt;NO&lt;/strong&gt;&lt;br&gt;
You can get to the previous state in git and retrieve the credentials :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout HEAD~3 &amp;amp;&amp;amp; cat index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flash forward : I will be using &lt;code&gt;git reflog&lt;/code&gt;&lt;br&gt;
Let's do the interactive rebase and remove the commit from history :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git reflog
git checkout HEAD@{1}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nfhszi9bdatq5set6ye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nfhszi9bdatq5set6ye.png" alt="Image description" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc96wlkq6d2uflzfh23ed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc96wlkq6d2uflzfh23ed.png" alt="Image description" width="800" height="123"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git rebase -i HEAD~4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctb7ev3snspyjb9b6m8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctb7ev3snspyjb9b6m8j.png" alt="Image description" width="535" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fki7nq6djojmdc304rwwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fki7nq6djojmdc304rwwp.png" alt="Image description" width="800" height="55"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still I can get the credentials :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git reflog
git checkout HEAD@{7} &amp;amp;&amp;amp; cat index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9kkhsxour9hsn4k1a5i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9kkhsxour9hsn4k1a5i.png" alt="Image description" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery
&lt;/h2&gt;

&lt;p&gt;How to really fix the issue ?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Disclaimer : the technical solution provided below has limited applicability and rather brutal&lt;/code&gt;&lt;br&gt;
Let's just proceed with a clean minimalistic setup :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir demo-project
cd demo-project
git init
touch index.js
git add -A
git commit -m 'init'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF &amp;gt; index.js
// index.js
const password = 'SecurePass123!';
EOF

git add -A
git commit -m 'security-issue'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And let's fix :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remove &lt;code&gt;index.js&lt;/code&gt; from all git history&lt;/li&gt;
&lt;li&gt;expire &lt;code&gt;git reflog&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;prune dangling blobs&lt;/li&gt;
&lt;li&gt;force push (!)&lt;/li&gt;
&lt;li&gt;verify
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install git-filter-repo
which git-filter-repo
git filter-repo --path index.js --invert-paths --force
git reflog expire --expire=now --all
git gc --prune=now --aggressive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Destructive&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git push --force --all
git push --force --tags
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git log --all -- index.js
git grep 'SecurePass123!' $(git rev-list --all)
rm -rf .git/filter-repo
rm -rf .git/lost-found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Post-mortem :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reclone the repository&lt;/li&gt;
&lt;li&gt;rotate keys&lt;/li&gt;
&lt;li&gt;purge system cache (a topic in itself)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Long story short : once you have leaked the keys it's a bit of a challenge to recover as yes, you will rotate the keys and the immediate issue will be fixed, whereas another issue is to prevent it from happening again.&lt;/p&gt;

&lt;p&gt;There have been real cases of leaking the keys through the &lt;code&gt;reflog&lt;/code&gt; by the leaders :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub OAuth Token Leak by Slack – 2015&lt;/li&gt;
&lt;li&gt;Uber AWS Key Leak – 2016&lt;/li&gt;
&lt;li&gt;Facebook Access Token Leak – 2017&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hence yes, you need some sort of code analysis software in Enterprise edition for your business, as it seems that for Sonar the password leaks are part of the Community edition and leaked api keys detection is a part of the Enterprise edition only, for Snyk the Enterprise edition is also more powerful and contains essential features.&lt;br&gt;
And even then you will have to stay vigilant and to go deeper.&lt;br&gt;
I would suggest to switch to cloud native for the dev purposes, as the whole recovery is pretty complicated due to the caching on different levels.&lt;/p&gt;

&lt;p&gt;Happy Easter !&lt;/p&gt;

</description>
      <category>security</category>
      <category>git</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>More storage for media : organize files using ChatGPT</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Thu, 31 Oct 2024 23:11:22 +0000</pubDate>
      <link>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-31m5</link>
      <guid>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-31m5</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-1-data-preparation-2p6c"&gt;More storage for media : organize files using ChatGPT : part 1, data preparation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-2-converting-jpeg-to-heic-1bbi"&gt;More storage for media : organize files using ChatGPT : part 2, converting JPEG to HEIC&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-3-converting-mov-to-hevc-mpeg-4-3mje"&gt;More storage for media : organize files using ChatGPT : part 3, converting MOV to HEVC MPEG-4&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part4-processing-on-gentoo-3pho"&gt;More storage for media : organize files using ChatGPT : part4, processing on Gentoo&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Open Source and Big Tech are equally important, and of course if you can afford to have the latest &lt;a href="https://www.apple.com/mac/" rel="noopener noreferrer"&gt;Mac&lt;/a&gt; - just do it, if you can afford to subscribe for 1TB plan from your cloud provider - just do it.&lt;br&gt;
Prior to writing this blog I thought that by now there should exist simple GUI programs to convert all the media formats, and the reality is different - you still need &lt;code&gt;linux&lt;/code&gt; OR you still need to pay.&lt;br&gt;
And it's okay to have a choice, it's okay to pay as it saves your time, and it's okay to use &lt;code&gt;linux&lt;/code&gt; as it saves your money.&lt;br&gt;
Hence I can freely say here, that you start with the recommendations from &lt;code&gt;ChatGPT&lt;/code&gt;, however remember at every point that it's better to fix the metadata and the filename before any compression, and to maintain both during the conversion, hence the commands below should be tailored to your needs:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzasuyzl64ta1nxhi1ly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzasuyzl64ta1nxhi1ly.png" alt="Image description" width="800" height="342"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlp1szi0pdaqsn2tbd3e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlp1szi0pdaqsn2tbd3e.png" alt="Image description" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As not everyone is thrilled with technology, not everyone will bother to install any linux distribution, not everyone will choose &lt;code&gt;Gentoo&lt;/code&gt; - hence I can freely post my recommendations and just say that you can save 10 times more storage by just compressing the files from the high quality to the normal.&lt;br&gt;
Not every video deserves to get the Oscar and it's okay.&lt;/p&gt;

&lt;p&gt;Why &lt;code&gt;Gentoo&lt;/code&gt; then? Building &lt;code&gt;Gentoo&lt;/code&gt; is like to produce a single-use sterile dental scalpel, you should know what is the purpose and then this distribution will be your best friend. It's too sharp to be used at an average workplace, it's not for your general use system, it's for the specific purpose of getting the specific job done at maximum performance - this is what &lt;code&gt;Gentoo&lt;/code&gt; is about.&lt;br&gt;
Maybe the time has come for the wider adoption of &lt;code&gt;Gentoo&lt;/code&gt;, as in the world where lots of people have a skill to build their system we are approaching to the wiser use of energy. &lt;code&gt;Gentoo&lt;/code&gt; is eco-friendly.&lt;/p&gt;

&lt;p&gt;I'm also grateful to the maintainers of &lt;code&gt;ffmpeg&lt;/code&gt; and &lt;code&gt;ImageMagick&lt;/code&gt;, thanks to these enthusiasts you may be at the edge of the progress with limited resources.&lt;/p&gt;

&lt;p&gt;So yes, give it a try, you may impress others by compressing the size of the media library, and you will impress yourself after having built your first &lt;code&gt;Gentoo&lt;/code&gt; server - it will take time, at least 1 hour, and at most - who knows, let it take the time it needs, or try again later. Do it yourself.&lt;br&gt;
How faster is &lt;code&gt;Gentoo&lt;/code&gt; than &lt;code&gt;Ubuntu&lt;/code&gt; on &lt;code&gt;WSL&lt;/code&gt;?&lt;br&gt;
Exactly the same video processing task took 30.97s on &lt;code&gt;Gentoo&lt;/code&gt; and 37.32s on &lt;code&gt;Ubuntu&lt;/code&gt;, hence you have around of 20% gain. Is it worth it?&lt;br&gt;
The answer is: it depends on your particular case.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;It may happen that image compression from &lt;code&gt;heic&lt;/code&gt; to &lt;code&gt;heic&lt;/code&gt; with &lt;code&gt;ImageMagick&lt;/code&gt; doesn't work for &lt;a href="https://github.com/strukturag/libheif/issues/1190" rel="noopener noreferrer"&gt;iPhone 15 pro iOS 18&lt;/a&gt;, in this case you may install &lt;code&gt;IrfanView&lt;/code&gt; or any other tool, convert &lt;code&gt;heic&lt;/code&gt; to &lt;code&gt;jpeg&lt;/code&gt; without loss of quality and then use &lt;code&gt;ImageMagick&lt;/code&gt; to convert &lt;code&gt;jpeg&lt;/code&gt; to &lt;code&gt;heic&lt;/code&gt; with compression.&lt;br&gt;
As well you should have some good understanding of what is &lt;code&gt;fps&lt;/code&gt; and what is &lt;code&gt;bitrate&lt;/code&gt; before compressing video.&lt;/p&gt;

&lt;p&gt;This should be a good start for &lt;code&gt;ffmpeg&lt;/code&gt; to be used for vertical videos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for file in ./input/*.mov; do ffmpeg -i "$file" -vf "scale=-2:1280" -r 30 -c:v libx265 -crf 28 -preset medium -c:a copy "./output/$(basename "$file" .mov).mp4"; done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;Happy Diwali, may Lakshmi bring you peace and prosperity, or if she is gone elsewhere - do it yourself.&lt;/p&gt;

&lt;p&gt;Happy Halloween!🎃&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>linux</category>
      <category>chatgpt</category>
      <category>ai</category>
    </item>
    <item>
      <title>More storage for media : organize files using ChatGPT : part4, processing on Gentoo</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Thu, 31 Oct 2024 22:57:56 +0000</pubDate>
      <link>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part4-processing-on-gentoo-3pho</link>
      <guid>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part4-processing-on-gentoo-3pho</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-31m5"&gt;Main article&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Let's get the maximum performance and install &lt;code&gt;ImageMagick&lt;/code&gt; and &lt;code&gt;ffmpeg&lt;/code&gt; on &lt;a href="https://www.gentoo.org/" rel="noopener noreferrer"&gt;Gentoo&lt;/a&gt;.&lt;br&gt;
I will use &lt;code&gt;WSL&lt;/code&gt; for demo, hence some functionalities will be limited.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup Gentoo
&lt;/h2&gt;

&lt;p&gt;Let's get the latest &lt;code&gt;stage3&lt;/code&gt; tarball from the &lt;a href="https://wiki.gentoo.org/wiki/Gentoo_in_WSL" rel="noopener noreferrer"&gt;Gentoo Downloads&lt;/a&gt;&lt;br&gt;
powershell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoke-WebRequest -Uri "https://distfiles.gentoo.org/releases/amd64/autobuilds/20241027T164832Z/stage3-amd64-openrc-20241027T164832Z.tar.xz" -OutFile "$env:USERPROFILE\Downloads\stage3-amd64-openrc-20241027T164832Z.tar.xz"

mkdir "$env:LOCALAPPDATA\WSL\Gentoo" -Force

wsl --import Gentoo "$env:LOCALAPPDATA\WSL\Gentoo" "$env:USERPROFILE\Downloads\stage3-amd64-openrc-20241027T164832Z.tar.xz"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's enter Gentoo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl -d Gentoo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the &lt;a href="https://wiki.gentoo.org/wiki/Gentoo_in_WSL" rel="noopener noreferrer"&gt;Gentoo in WSL&lt;/a&gt; to setup a user! I will skip this part and will continue with &lt;code&gt;root&lt;/code&gt; (not secure).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hi ChatGPT, could you please suggest the compiler flags and USE flags for Gentoo to have the maximum performance, my use case is ffmpeg with HEVC and ImageMagick with HEIC. Short answer, thank you !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqm6lt45j5ndbmntkh290.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqm6lt45j5ndbmntkh290.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2sg1ubmlcty15e8c2h6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2sg1ubmlcty15e8c2h6.png" alt="Image description" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ejxtyhogoejcos54can.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ejxtyhogoejcos54can.png" alt="Image description" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's set compiler and USE flags, I have entered as &lt;code&gt;root&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat /etc/portage/make.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8g9h1nbchwvj3xi9afj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8g9h1nbchwvj3xi9afj.png" alt="Image description" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A bit of magic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo -e "COMMON_FLAGS=\"-O2 -pipe\"\nCFLAGS=\"-march=native \${COMMON_FLAGS}\"\nCXXFLAGS=\"\${CFLAGS}\"\nFCFLAGS=\"\${COMMON_FLAGS}\"\nFFLAGS=\"\${COMMON_FLAGS}\"\nLC_MESSAGES=C.utf8" | tee /etc/portage/make.conf &amp;gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;USE&lt;/code&gt; flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo 'USE="ffmpeg hevc imagemagick heic x264 vaapi vdpau opencl threads jpeg png tiff gpl libx264 libx265 libfdk-aac libmp3lame libopus libvpx nonfree"' | tee -a /etc/portage/make.conf &amp;gt; /dev/null

echo "media-gfx/imagemagick heic libheif" | tee /etc/portage/package.use/imagemagick &amp;gt; /dev/null

echo "media-video/ffmpeg gpl libx264 libx265 libfdk-aac libmp3lame libopus libvpx nonfree" | tee /etc/portage/package.use/ffmpeg &amp;gt; /dev/null

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhn2tlarsic4c9ogm08kg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhn2tlarsic4c9ogm08kg.png" alt="Image description" width="800" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's sync and update, at this point you will likely embrace the philosophy of &lt;code&gt;Gentoo&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;emerge --sync
emerge --update --deep --newuse @world
emerge --depclean
emerge dev-vcs/git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we have a media server, let's pack it.&lt;/p&gt;

&lt;h2&gt;
  
  
  ImageMagick on Gentoo
&lt;/h2&gt;

&lt;p&gt;With the &lt;code&gt;USE&lt;/code&gt; flags set from the setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;emerge media-libs/libheif media-libs/libpng gnome-base/librsvg
git clone https://github.com/ImageMagick/ImageMagick.git
cd ImageMagick
./configure --with-heic=yes
make
make install
ldconfig

magick -list format | grep HEIC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F85afhm786n3une6ebxsi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F85afhm786n3une6ebxsi.png" alt="Image description" width="694" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ffmpeg on Gentoo
&lt;/h2&gt;

&lt;p&gt;With the &lt;code&gt;USE&lt;/code&gt; flags set from the setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;emerge media-libs/x264 media-libs/x265 media-libs/fdk-aac media-sound/lame media-libs/opus media-libs/libvpx

git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
cd ffmpeg
./configure --enable-gpl --enable-libx264 --enable-libx265 --enable-libfdk-aac --enable-libmp3lame --enable-libopus --enable-libvpx --enable-nonfree
make
make install

ffmpeg -codecs | grep hevc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghngir5jr6uqbn8yw545.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghngir5jr6uqbn8yw545.png" alt="Image description" width="694" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final step
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;It is the time you have wasted for your Gentoo that makes your Gentoo so important.&lt;br&gt;
Let's add new user:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useradd -m -G users -s /bin/bash &amp;lt;username&amp;gt;
passwd &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And reboot Gentoo, now you can enter as &lt;code&gt;&amp;lt;username&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl -u &amp;lt;username&amp;gt; -d Gentoo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you are afraid to use &lt;code&gt;Gentoo&lt;/code&gt;, come back when you are not afraid or use it while being afraid.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21orphpqtoyxgkfatw6m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21orphpqtoyxgkfatw6m.png" alt="Image description" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9fka6a2668so01hwhbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9fka6a2668so01hwhbg.png" alt="Image description" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gentoo</category>
      <category>linux</category>
      <category>productivity</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>More storage for media : organize files using ChatGPT : part 3, converting MOV to HEVC MPEG-4</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Thu, 31 Oct 2024 21:41:08 +0000</pubDate>
      <link>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-3-converting-mov-to-hevc-mpeg-4-3mje</link>
      <guid>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-3-converting-mov-to-hevc-mpeg-4-3mje</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-31m5"&gt;Main article&lt;/a&gt;&lt;br&gt;
Think to do backups and to fix metadata before processing, see in &lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-1-data-preparation-2p6c"&gt;part 1&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You might wish to know more about &lt;a href="https://en.wikipedia.org/wiki/QuickTime_File_Format" rel="noopener noreferrer"&gt;MOV&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/MPEG-4" rel="noopener noreferrer"&gt;MPEG-4&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding" rel="noopener noreferrer"&gt;HEVC&lt;/a&gt;. You may prompt &lt;code&gt;ChatGPT&lt;/code&gt; on my choice of the end format, there are pros and cons, get to know about &lt;a href="https://en.wikipedia.org/wiki/Versatile_Video_Coding" rel="noopener noreferrer"&gt;VVC&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Given my past experience I will build &lt;a href="https://www.ffmpeg.org/" rel="noopener noreferrer"&gt;ffmpeg&lt;/a&gt; from sources with &lt;code&gt;HEVC&lt;/code&gt; support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install -y build-essential pkg-config git yasm libx264-dev libx265-dev libnuma-dev libfdk-aac-dev libmp3lame-dev libopus-dev libvpx-dev
sudo apt install nasm
git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
cd ffmpeg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My favorite part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./configure --enable-gpl --enable-libx264 --enable-libx265 --enable-libfdk-aac --enable-libmp3lame --enable-libopus --enable-libvpx --enable-nonfree
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -codecs | grep hevc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjs41ul5kmdgm6o9rrkwt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjs41ul5kmdgm6o9rrkwt.png" alt="Image description" width="800" height="166"&gt;&lt;/a&gt;&lt;br&gt;
Great!&lt;br&gt;
Clean up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ..
rm -rf ffmpeg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  convert MOV to MPEG-4 HEVC using ffmpeg
&lt;/h2&gt;

&lt;p&gt;The setup is a complicated part, all the rest is trivial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -i input.mov -c:v libx265 -crf 23 -preset medium -c:a aac -b:a 192k output.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3m3iipc6mq382sq8r0on.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3m3iipc6mq382sq8r0on.png" alt="Image description" width="289" height="72"&gt;&lt;/a&gt;&lt;br&gt;
Feel free to use the settings that reflect your need.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;This should be a good start for you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hi ChatGPT, I wish to compress some old videos of mine into the mp4 hevc with normal quality, possibly with some loss, and my best wish would be to reduce the file size as much as we can, however preserving the quality, could you please suggest me the resolution and the bitrate in one line, and the ffmpeg command in the second line ? Short answer, thank you !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlp1szi0pdaqsn2tbd3e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlp1szi0pdaqsn2tbd3e.png" alt="Image description" width="800" height="371"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -i input.mp4 -c:v libx265 -preset medium -crf 28 -vf "scale=1280:720" -b:v 1500k output.mp4

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, crf should be enough and it's better to encode audio as well, at any point you should remember about the metadata.&lt;br&gt;
Industrial version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Hardcoded input directory and output directory
INPUT_DIR="./input"               # Input directory set to ./input
OUTPUT_DIR="./output"             # Output directory for converted MP4s

# Create the output directory if it doesn't exist
mkdir -p "$OUTPUT_DIR"

# Iterate through all MP4 files in the input directory and subdirectories
find "$INPUT_DIR" -type f -iname "*.mp4" | while read -r mp4_file; do
    # Get the base name of the MP4 file without the extension
    base_name=$(basename "$mp4_file" .mp4)

    # Get video dimensions (width and height) using ffprobe
    dimensions=$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 "$mp4_file")
    width=$(echo "$dimensions" | cut -d 'x' -f 1)
    height=$(echo "$dimensions" | cut -d 'x' -f 2)

    # Debugging: Log the dimensions
    echo "Dimensions of $mp4_file: Width=$width, Height=$height"

    # Initialize the new filename variable
    new_filename="${OUTPUT_DIR}/${base_name}_converted.mp4"

    # Check if rescaling is needed and apply the appropriate scale
    if [[ "$height" -ge "$width" &amp;amp;&amp;amp; "$height" -gt 1280 ]]; then
        # If height &amp;gt;= width and height &amp;gt; 1280, rescale to -2:1280
        scale="-2:1280"
        echo "Rescaling $mp4_file to $scale"
    elif [[ "$width" -gt "$height" &amp;amp;&amp;amp; "$width" -gt 1280 ]]; then
        # If width &amp;gt; height and width &amp;gt; 1280, rescale to 1280:-2
        scale="1280:-2"
        echo "Rescaling $mp4_file to $scale"
    else
        # No scaling needed
        scale=""
        echo "No rescaling needed for $mp4_file"
    fi

    # Run ffmpeg with or without scaling, based on the conditions
    ffmpeg -i "$mp4_file" \
           -r 30 -c:v libx265 -crf 28 -preset medium \
           -c:a aac -b:a 192k \
           -metadata creation_time="$formatted_date" \
           ${scale:+-vf "scale=$scale"} \
           "$new_filename"

done

echo "Processing complete."

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>productivity</category>
      <category>linux</category>
      <category>ai</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>More storage for media : organize files using ChatGPT : part 2, converting JPEG to HEIC</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Tue, 29 Oct 2024 20:44:40 +0000</pubDate>
      <link>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-2-converting-jpeg-to-heic-1bbi</link>
      <guid>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-2-converting-jpeg-to-heic-1bbi</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-31m5"&gt;Main article&lt;/a&gt;&lt;br&gt;
Think to do backups and to fix metadata before processing, see in &lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-1-data-preparation-2p6c"&gt;part 1&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The setup is the outcome of &lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-1-data-preparation-2p6c"&gt;part1&lt;/a&gt;, I will focus on image files only: &lt;a href="https://en.wikipedia.org/wiki/JPEG" rel="noopener noreferrer"&gt;JPEG&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format#HEIC:_HEVC_in_HEIF" rel="noopener noreferrer"&gt;HEIC&lt;/a&gt; and I will convert everything to &lt;code&gt;HEIC&lt;/code&gt;. Why? Because I can, if you can't - use &lt;code&gt;JPEG&lt;/code&gt;.&lt;br&gt;
TL;DR : use &lt;code&gt;ImageMagick&lt;/code&gt; built from sources &lt;code&gt;--with-heic=yes&lt;/code&gt; on linux to make it happen after having installed &lt;code&gt;libheif&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Failed attempt to use jrottenberg/ffmpeg:4.1-alpine image to convert JPEG to HEIC
&lt;/h2&gt;

&lt;p&gt;I will use &lt;a href="https://en.wikipedia.org/wiki/Docker_(software)" rel="noopener noreferrer"&gt;docker&lt;/a&gt; image of &lt;a href="https://www.ffmpeg.org/" rel="noopener noreferrer"&gt;ffpeg&lt;/a&gt; on &lt;a href="https://alpinelinux.org/" rel="noopener noreferrer"&gt;alpine&lt;/a&gt; and I'm skipping steps as you can get this information from the internet search or generative AI, I'm using the &lt;code&gt;ChatGPT&lt;/code&gt; as no login is required and most of readers will be able to reproduce my steps.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thank you! I have jrottenberg/ffmpeg:4.1-alpine image, I wish to convert all the files in the jpg directory to the heic format with the resolution not more than 960x1280 and the size of approximately 300Kb and to have them in the directory jpg_heic, I don't wish to create my dockerfile just to run a command from git bash or cmd on windows&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj04sr67o9h7ga9seka5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj04sr67o9h7ga9seka5k.png" alt="Image description" width="798" height="552"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -v &amp;lt;path_to_jpg_directory&amp;gt;:/input -v &amp;lt;path_to_output_directory&amp;gt;:/output jrottenberg/ffmpeg:4.1-alpine \
    -i /input/%01d.jpg -vf "scale=w=960:h=1280:force_original_aspect_ratio=decrease" -b:v 300k /output/%01d.heic

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7t9vdnrqy3c6cxm7q0r0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7t9vdnrqy3c6cxm7q0r0.png" alt="Image description" width="769" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Somehow it didn't work out of the box, let me try another approach:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;can we start docker once and mount the volumes once, then execute the loop with the same container ? I think it will be more optimal&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9f8es9ukqcqlwys7yrz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9f8es9ukqcqlwys7yrz.png" alt="Image description" width="800" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5l4ncngzswhnab51s7k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5l4ncngzswhnab51s7k.png" alt="Image description" width="777" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -it --rm -v /path/to/jpg:/input -v /path/to/jpg_heic:/output --name ffmpeg-container jrottenberg/ffmpeg:4.1-alpine /bin/sh

for img in /input/*.jpg; do
  ffmpeg -i "$img" -vf "scale=iw*min(960/iw\,1280/ih):ih*min(960/iw\,1280/ih),format=heic" -b:v 300k "/output/$(basename "${img%.jpg}.heic")"
done

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This didn't work either:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sbv62h9nrx4wrlzg0kn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sbv62h9nrx4wrlzg0kn.png" alt="Image description" width="800" height="49"&gt;&lt;/a&gt;&lt;br&gt;
Let's start over:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Unable to find a suitable output format for '/bin/sh'&lt;br&gt;
/bin/sh: Invalid argument&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjo8ux3ha14w4aeh1d6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjo8ux3ha14w4aeh1d6h.png" alt="Image description" width="800" height="554"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -v /path/to/jpg:/input -v /path/to/jpg_heic:/output jrottenberg/ffmpeg:4.1-alpine /bin/sh -c "for img in /input/*.jpg; do ffmpeg -i \"\$img\" -vf 'scale=iw*min(960/iw\,1280/ih):ih*min(960/iw\,1280/ih),format=heic' -b:v 300k \"/output/\$(basename \"\${img%.jpg}.heic\")\"; done"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6ucvuuvsf98zr5a1h5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6ucvuuvsf98zr5a1h5v.png" alt="Image description" width="790" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupv8bzh7f41rs0ybkp9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupv8bzh7f41rs0ybkp9y.png" alt="Image description" width="793" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well...still doesn't work, okay let's use the &lt;a href="https://hub.docker.com/r/jrottenberg/ffmpeg" rel="noopener noreferrer"&gt;creator's guide&lt;/a&gt; and do the work in the interactive way :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -it --entrypoint=/bin/sh -v /path/to/jpg:/input -v /path/to/jpg_heic:/output jrottenberg/ffmpeg:4.1-alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then run the command inside the container :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for img in /input/*.jpg; do ffmpeg -i "$img" -vf "scale=iw*min(960/iw\,1280/ih):ih*min(960/iw\,1280/ih),format=heic" -b:v 300k "/output/$(basename "$img" .jpg).heic"; done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still failure, let's make it more simple :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for img in /input/*.jpg; do ffmpeg -i "$img" -vf "scale=iw*min(960/iw\,1280/ih):ih*min(960/iw\,1280/ih)" -c:v hevc -crf 28 "/output/$(basename "$img" .jpg).heic"; done

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another failure, let's check if the format is supported :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -formats | grep heic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No it's not. Okay let's move on and use the latest image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failed attempt to use jrottenberg/ffmpeg:latest image to convert JPEG to HEIC
&lt;/h2&gt;

&lt;p&gt;Let's go :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker pull jrottenberg/ffmpeg

docker run -it --entrypoint=/bin/sh -v /path/to/jpg:/input -v /path/to/jpg_heic:/output jrottenberg/ffmpeg:latest

ffmpeg -formats | grep heic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still no support for HEIC.&lt;br&gt;
Okay whatever, I will go to the official page now, something I should have considered at the beginning.&lt;/p&gt;
&lt;h2&gt;
  
  
  Failed attempt to use ffmpeg builds from Gyan Dev to convert JPEG to HEIC
&lt;/h2&gt;

&lt;p&gt;I've got some help from ChatGPT, check the SHA256, installed the release build for ffmpeg, feel free to do the same on your risk.&lt;br&gt;
Checking the version and the formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -version

ffmpeg -formats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still no HEIC.&lt;br&gt;
Let's get the latest build from master.&lt;br&gt;
I have installed: ffmpeg-2024-10-24-git-153a6dc8fa-full_build.7z after having checked SHA256.&lt;br&gt;
Still no HEIC.&lt;br&gt;
Let's check the &lt;a href="https://github.com/FFmpeg/FFmpeg" rel="noopener noreferrer"&gt;github of ffmpeg&lt;/a&gt;&lt;br&gt;
and it seems that HEIC is not listed in the &lt;a href="https://ffmpeg.org/ffmpeg-all.html#Image-Formats" rel="noopener noreferrer"&gt;ffmpeg image formats&lt;/a&gt;&lt;br&gt;
HEIC is not supported...read the manual before diving into the implementation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Failed attempt to use ImageMagic on Win11 to convert JPEG to HEIC
&lt;/h2&gt;

&lt;p&gt;Let's give a try with &lt;a href="https://imagemagick.org/" rel="noopener noreferrer"&gt;ImageMagic&lt;/a&gt;&lt;br&gt;
checking if the format is supported :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;magick -list format | find "HEIC"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All good, the command looks pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;magick mogrify -path /path/to/output_directory -resize 960x1280 -format heic /path/to/input_directory/*.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No luck, trying CMD version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for %i in ("C:\path\to\input_directory\*.jpg") do magick convert "%i" -resize 960x1280 "C:\path\to\output_directory\%~ni.heic"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the result is the same: &lt;code&gt;no encode delegate for this image format HEIC&lt;/code&gt;&lt;br&gt;
It seems I need to the have the &lt;code&gt;libheif&lt;/code&gt;.&lt;br&gt;
Looks prominent, I will try to run it on WSL later on.&lt;/p&gt;
&lt;h2&gt;
  
  
  Failed attempt to use IrfanView on Win11 to convert JPEG to HEIC
&lt;/h2&gt;

&lt;p&gt;Got it from &lt;a href="https://www.irfanview.com/" rel="noopener noreferrer"&gt;IrfanView&lt;/a&gt; with all the plugins, conversion from HEIC to JPEG works, vice-versa - NO.&lt;/p&gt;
&lt;h2&gt;
  
  
  Failed attempt to use Win11 Photos to convert JPEG to HEIC
&lt;/h2&gt;

&lt;p&gt;Conversion from HEIC to JPEG works, vice-versa - NO.&lt;/p&gt;
&lt;h2&gt;
  
  
  WSL
&lt;/h2&gt;

&lt;p&gt;Let's get &lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install" rel="noopener noreferrer"&gt;WSL&lt;/a&gt;&lt;br&gt;
Then :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install libheif-dev
sudo apt install build-essential
sudo apt install imagemagick

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;same error when using &lt;code&gt;convert temp.jpg temp.heic&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;convert-im6.q16: no encode delegate for this image format `HEIC' @ warning/constitute.c/WriteImage/1305.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's start over and install ImageMagick from source, at the time of writing the last version is &lt;code&gt;ImageMagick-7.1.1-39&lt;/code&gt;, you may need to change the link to &lt;code&gt;tar.gz&lt;/code&gt;&lt;br&gt;
Cleanup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt remove --purge imagemagick
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install from sources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install build-essential libheif-dev libmagickwand-dev
sudo apt install wget
sudo apt install libjpeg-dev libpng-dev libtiff-dev
wget https://download.imagemagick.org/ImageMagick/download/ImageMagick.tar.gz
tar -xvf ImageMagick.tar.gz
cd ImageMagick-7.1.1-39/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and now the moment of the glory (maybe)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./configure --with-heic=yes
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have seen the line I've been waiting for :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  DELEGATES         = bzlib djvu fontconfig freetype heic jbig jng jp2 jpeg lcms lqr lzma openexr png ps tiff webp x xml zlib zstd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;checking with &lt;code&gt;magick -version&lt;/code&gt;&lt;br&gt;
New error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;magick: error while loading shared libraries: libMagickCore-7.Q16HDRI.so.10: cannot open shared object file: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;let's do our best:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ldconfig
ldconfig -p | grep MagickCore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking again &lt;code&gt;magick -version&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Version: ImageMagick 7.1.1-39 Q16-HDRI x86_64 22428 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenMP(4.5)
Delegates (built-in): bzlib djvu fontconfig freetype heic jbig jng jp2 jpeg lcms lqr lzma openexr png tiff webp x xml zlib zstd
Compiler: gcc (13.2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tricky part is &lt;code&gt;./configure --with-heic=yes&lt;/code&gt; and &lt;code&gt;sudo ldconfig&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://www.instagram.com/jasonstatham/p/Cw9sXtrOOuC/" rel="noopener noreferrer"&gt;Those who know…….know.&lt;/a&gt;.&lt;br&gt;
Testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;magick input.jpg output.heic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've got a log message, however conversion worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;magick: no encode delegate for this image format `HEIC' @ warning/constitute.c/WriteImage/1403.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4eh6dbbt1h5tdibgwvxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4eh6dbbt1h5tdibgwvxq.png" alt="Image description" width="300" height="70"&gt;&lt;/a&gt;&lt;br&gt;
All the rest is trivial.&lt;/p&gt;
&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;It might have worked with a simple ImageMagick installation on WSL, the original goal of the post was to show how easy it is to move on with ChatGPT and in fact still you need to know what you are doing as sometimes you get suggestions which are not supported. Still it's helpful to move on.&lt;br&gt;
Why has it been so difficult to make it work on Win? Most probably due to the fact that patents for &lt;code&gt;HEVC&lt;/code&gt; will have expired somewhere near 2030.&lt;br&gt;
Hopefully we have Linux, OpenSource, ImageMagick, ChatGPT and some brains.&lt;br&gt;
Apply the guides to your needs, if you are in doubt - use &lt;code&gt;JPEG&lt;/code&gt;. Check about &lt;a href="https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding" rel="noopener noreferrer"&gt;HEVC&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Versatile_Video_Coding" rel="noopener noreferrer"&gt;VVC&lt;/a&gt; before taking your own decision.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;This should be a good start for you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hi ChatGPT, I wish to compress some old photos of mine into the heic with normal quality, possibly with some loss, and my best wish would be to reduce the file size as much as we can, however preserving the quality, could you please suggest me the resolution and the file size in one line, and the ImageMagick command in the second line ? Short answer, thank you !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzasuyzl64ta1nxhi1ly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzasuyzl64ta1nxhi1ly.png" alt="Image description" width="800" height="342"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;magick input.jpg -resize 1920x1080 -quality 80 output.heic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Industrial version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Input and Output directories
input_dir="./input"
output_dir="./output"

# Ensure the output directory exists
mkdir -p "$output_dir"

# Loop through all jpg files in the input directory
for img in "$input_dir"/*.jpg; do
    # Get the image dimensions (width x height)
    dimensions=$(identify -format "%wx%h" "$img")
    width=$(echo $dimensions | cut -d'x' -f1)
    height=$(echo $dimensions | cut -d'x' -f2)

    # Check for vertical images (height &amp;gt;= width)
    if [ "$height" -ge "$width" ]; then
        # Vertical and height &amp;gt; 1280, resize to height 1280
        if [ "$height" -gt 1280 ]; then
            output_file="$output_dir/$(basename "$img" .jpg).heic"
            magick "$img" -resize x1280 -quality 80 "$output_file"
            echo "Resized and converted $img to $output_file"
        else
            # Vertical image with height &amp;lt;= 1280, just convert to HEIC
            output_file="$output_dir/$(basename "$img" .jpg).heic"
            magick "$img" "$output_file"
            echo "Converted $img to $output_file"
        fi
    elif [ "$width" -gt "$height" ]; then
        # Landscape and width &amp;gt; 1280, resize to width 1280
        if [ "$width" -gt 1280 ]; then
            output_file="$output_dir/$(basename "$img" .jpg).heic"
            magick "$img" -resize 1280x -quality 80 "$output_file"
            echo "Resized and converted $img to $output_file"
        else
            # Landscape image with width &amp;lt;= 1280, just convert to HEIC
            output_file="$output_dir/$(basename "$img" .jpg).heic"
            magick "$img" "$output_file"
            echo "Converted $img to $output_file"
        fi
    else
        echo "Skipping $img: does not fit any criteria"
    fi
done

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>linux</category>
      <category>jpeg</category>
      <category>productivity</category>
    </item>
    <item>
      <title>More storage for media : organize files using ChatGPT : part 1, data preparation</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Tue, 29 Oct 2024 20:40:46 +0000</pubDate>
      <link>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-1-data-preparation-2p6c</link>
      <guid>https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-1-data-preparation-2p6c</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-31m5"&gt;Main article&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Age of AI
&lt;/h2&gt;

&lt;p&gt;Given the rise of &lt;a href="https://chatgpt.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; and other Gen AI tools I have to reinvent my posts to make them relevant, some introduction &lt;a href="https://openai.com/index/chatgpt/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Hence since now my posts will be less technical and more functional. From my conversation with ChatGPT it (from now on I will use pronouns it) granted me the permission to use our conversation in this blog and to reference ChatGPT as &lt;code&gt;it&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Let's consider a common problem : we wish to optimize storage of media files.&lt;br&gt;
Now, let's imagine we have made a dump of data with photos and videos by year in different formats and a magic tools that may convert them to the format of our choice.&lt;/p&gt;

&lt;p&gt;However, we need to do some preparation work : group the files of each type instead of having the grouping by year. How to achieve this using ChatGPT ? &lt;/p&gt;
&lt;h2&gt;
  
  
  First steps
&lt;/h2&gt;

&lt;p&gt;Hers is our dump of media :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2zasrgsgbdhiac1wxb5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2zasrgsgbdhiac1wxb5x.png" alt="Image description" width="271" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's get some input from ChatGPT : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;windows using git bash and linux commands, I wish to rename the folders in the current dir having name 'Photos from ' simply to year&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjb3kk8rzlt4sr77f1zb3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjb3kk8rzlt4sr77f1zb3.png" alt="Image description" width="800" height="512"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for dir in Photos\ from\ *; do
    if [[ -d "$dir" ]]; then
        year="${dir##* }"  # Extract the year from the folder name
        mv "$dir" "$year"  # Rename the folder
    fi
done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs2k4s5dafe0ju60hl1w9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs2k4s5dafe0ju60hl1w9.jpg" alt="Image description" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Result : &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnvjp8uqrr6537zz09swr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnvjp8uqrr6537zz09swr.png" alt="Image description" width="160" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, let's move on!&lt;/p&gt;

&lt;h1&gt;
  
  
  Metadata : data preparation
&lt;/h1&gt;

&lt;p&gt;The very first thing you should consider is to fix the metadata, which in the narrow sense mean that each file should contain the &lt;code&gt;Date taken&lt;/code&gt; for the image files and &lt;code&gt;Media created&lt;/code&gt; for the video files. In the best case the image name should contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;formatted timestamp in the form &lt;code&gt;date_time&lt;/code&gt;, like 20240101_000000 or even longer&lt;/li&gt;
&lt;li&gt;origin discriminator, anything that would help you to classify the file, when compressing could the the original extension&lt;/li&gt;
&lt;li&gt;some counter with as many digits as necessary to avoid collisions during any compression job
Some of the modern phones do create a speaking file id, some do not, in any case for the old files it may happen that the data is present only in the adjacent json file, the structure name of it usually corresponds to the following : &lt;code&gt;&amp;lt;filename_with_extension&amp;gt;{&amp;lt;.suffix_of_type_metadata&amp;gt;}.json&lt;/code&gt;.
Hence before any processing it is wise to go through all the images and all the videos and update the time attributes, for example with &lt;code&gt;exiftool&lt;/code&gt; for images and &lt;code&gt;ffmpeg&lt;/code&gt; for videos and as well it's a good idea to copy the processed file to the new directory with an extension that has as a prefix the formatted_date, and those who do not - move to another folder to fix them up manually.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall the idea could be the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;list file extensions in all the folders to evaluate the necessary work
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -R ./ | awk -F. '/\./ {print $NF}' | sort -u
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;prepare the next which will be to convert all the files to the lower case to simplify further operation, prior to this we should check for the case insensitive duplicates
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Find all subdirectories and process files in each subdirectory separately
find . -type d | while read -r dir; do
    # Find files in the current directory
    find "$dir" -maxdepth 1 -type f | \
        # Remove the path, leaving only the filename
        sed 's/.*\///' | \
        # Convert filenames to lowercase for case-insensitive comparison
        tr '[:upper:]' '[:lower:]' | \
        # Sort the filenames
        sort | \
        # Find duplicates in the sorted list
        uniq -d | \
        # Print the duplicates with their directory path
        while read -r filename; do
            echo "Duplicates in '$dir':"
            find "$dir" -maxdepth 1 -type f -iname "$filename"
        done
done

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;convert all the files to the lower case:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Find all files recursively
find ./ -type f | while read -r file; do
    dir=$(dirname "$file")
    base=$(basename "$file")

    # Convert the filename to lowercase
    lower_base=$(echo "$base" | tr '[:upper:]' '[:lower:]')

    # If the filename is different (case insensitive), do the two-step rename
    if [[ "$base" != "$lower_base" ]]; then
        # Step 1: Rename to an intermediate name (tmp_&amp;lt;lowercase filename&amp;gt;)
        mv "$file" "$dir/tmp_$lower_base"

        # Step 2: Rename to the final lowercase name (removing tmp_ prefix)
        mv "$dir/tmp_$lower_base" "$dir/$lower_base"
    fi
done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point you are ready to start fixing the metadata&lt;/p&gt;

&lt;h2&gt;
  
  
  Metadata : updating images using data from jsons
&lt;/h2&gt;

&lt;p&gt;At this point we are ready to start fixing the metadata, the bare minimum is to ensure that the &lt;code&gt;Date taken&lt;/code&gt; is up to date, and it's quite often that this date is not set in the image itself but stored in the json separately, here is the script you may use to sort &lt;code&gt;jpg&lt;/code&gt; images into two folder: one containing images with accurate metadata and another containing the images that need to be viewed further and therefore prefixed with the origin folder.&lt;br&gt;
I will use the &lt;code&gt;exiftool&lt;/code&gt; for this purpose and add a formatted date prefix for each file that has a relevant metadata stored in json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Hardcoded input directory and output directories
INPUT_DIR="./input"               # Input directory set to ./input
OUTPUT_DIR_JPG="./jpg"           # Directory for modified JPGs
OUTPUT_DIR_JPG_TO_FIX="./jpg_to_fix"  # Directory for JPGs needing fixing

# Create output directories if they don't exist
mkdir -p "$OUTPUT_DIR_JPG"
mkdir -p "$OUTPUT_DIR_JPG_TO_FIX"

# Iterate through all JPG files in the input directory and subdirectories
find "$INPUT_DIR" -type f -iname "*.jpg" | while read -r jpg_file; do
    # Get the base name of the JPG file without the extension
    base_name=$(basename "$jpg_file" .jpg)

    # Look for the corresponding JSON file that starts with base_name and ends with .json
    json_file=$(find "$(dirname "$jpg_file")" -type f -iname "${base_name}.jpg*.json" | head -n 1)

    # Check if the corresponding JSON file exists
    if [[ -f "$json_file" ]]; then
        # Extract the creation time and description from the JSON file
        creation_time=$(jq -r '.photoTakenTime.timestamp' "$json_file")
        description=$(jq -r '.description' "$json_file")

        # Format the creation time for ExifTool (assuming it's in Unix timestamp)
        if [[ "$creation_time" =~ ^-?[0-9]+$ ]]; then
            formatted_date=$(date -d @"$creation_time" +"%Y%m%d_%H%M%S" 2&amp;gt;/dev/null)
            if [[ $? -ne 0 ]]; then
                formatted_date=""
            fi
        else
            formatted_date=""
        fi

        # **Always update** EXIF data based on JSON content (even if they already exist in JPG)
        if [[ -n "$formatted_date" ]]; then
            exiftool -overwrite_original -DateTimeOriginal="$formatted_date" "$jpg_file" &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
        fi

        if [[ -n "$description" ]]; then
            exiftool -overwrite_original -Description="$description" "$jpg_file" &amp;gt;/dev/null 2&amp;gt;&amp;amp;1
        fi

    else
        # If JSON file is not found, notify but continue to check Date taken
        echo "JSON file not found for: $jpg_file"
    fi

    # Now check if Date taken is set in the JPG file
    date_taken=$(exiftool -DateTimeOriginal -s -s -s "$jpg_file")

    # Construct the new filename based on the Date taken
    if [[ -n "$date_taken" ]]; then
        # Format the date_taken string to be filename-safe
        formatted_date=$(echo "$date_taken" | sed -e 's/://g' -e 's/ /_/g') # Remove colons and spaces
        safe_date_taken="${formatted_date:0:8}_${formatted_date:9:6}"  # Separate date and time
        new_filename="${OUTPUT_DIR_JPG}/${safe_date_taken}_$(basename "$jpg_file")" # New filename based on formatted date
        cp "$jpg_file" "$new_filename"
    else
        # Get the last directory name if Date taken is not set
        last_dir=$(basename "$(dirname "$jpg_file")")
        new_filename="${OUTPUT_DIR_JPG_TO_FIX}/${last_dir}_$(basename "$jpg_file")"
        cp "$jpg_file" "$new_filename"
    fi
done

echo "Processing complete."

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel free to adjust the logic for different file types using &lt;code&gt;ChatGPT&lt;/code&gt; or another Generative AI of your choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metadata : updating videos using data from jsons
&lt;/h2&gt;

&lt;p&gt;The same trick might be used for video files using &lt;code&gt;ffmpeg&lt;/code&gt;, however this time it's a bit more tricky as with &lt;code&gt;ffmpeg&lt;/code&gt; you must create a new file copy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Hardcoded input directory and output directories
INPUT_DIR="./input"               # Input directory set to ./input
OUTPUT_DIR_MP4="./mp4"           # Directory for modified MP4s
OUTPUT_DIR_MP4_TO_FIX="./mp4_to_fix"  # Directory for MP4s needing fixing

# Create output directories if they don't exist
mkdir -p "$OUTPUT_DIR_MP4"
mkdir -p "$OUTPUT_DIR_MP4_TO_FIX"

# Iterate through all MP4 files in the input directory and subdirectories
find "$INPUT_DIR" -type f -iname "*.mp4" | while read -r mp4_file; do
    # Get the base name of the MP4 file without the extension
    base_name=$(basename "$mp4_file" .mp4)

    # Look for the corresponding JSON file that starts with base_name and ends with .json
    json_file=$(find "$(dirname "$mp4_file")" -type f -iname "${base_name}.mp4*.json" | head -n 1)

    # Initialize the new filename variable
    new_filename=""

    # Check if the corresponding JSON file exists
    if [[ -f "$json_file" ]]; then
        echo "JSON file IS found for: $mp4_file"

        # Extract the creation time from the JSON file
        creation_time=$(jq -r '.photoTakenTime.timestamp' "$json_file")

        # Format the creation time for ffmpeg (assuming it's in Unix timestamp)
        if [[ "$creation_time" =~ ^-?[0-9]+$ ]]; then
            # Convert Unix timestamp to "YYYY-MM-DD HH:MM:SS" format
            formatted_date=$(date -d @"$creation_time" +"%Y-%m-%d %H:%M:%S" 2&amp;gt;/dev/null)
            if [[ $? -ne 0 ]]; then
                formatted_date=""
            fi
        else
            formatted_date=""
        fi

        # **Always update** MP4 metadata based on JSON content (creation_time)
        if [[ -n "$formatted_date" ]]; then
            # The filename date format: YYYY-MM-DD_HH:MM:SS
            filename_date=$(echo "$formatted_date" | tr -d ' ' | tr -d '-')  # Strip spaces and dashes
            filename_date="${filename_date//:/}"  # Replace colons with empty string for filename safety

            # Construct the new filename based on the formatted date
            new_filename="${OUTPUT_DIR_MP4}/${filename_date}_$(basename "$mp4_file")"  # New filename based on formatted date

            # Debugging: Log the new filename
            echo "New filename (with date): $new_filename"

            # Update the creation time in the MP4 metadata and copy it to the new location in one step
            ffmpeg -i "$mp4_file" -c copy -metadata creation_time="$formatted_date" "$new_filename"
        fi
    fi

    # If we did not successfully update the file, copy it to the 'mp4_to_fix' directory
    if [[ -z "$new_filename" ]]; then
        echo "JSON file not found or timestamp invalid. Moving to mp4_to_fix: $mp4_file"

        # Get the last directory name (in case the file is being moved to mp4_to_fix)
        last_dir=$(basename "$(dirname "$mp4_file")")
        new_filename="${OUTPUT_DIR_MP4_TO_FIX}/${last_dir}_$(basename "$mp4_file")"

        # Debugging: Log the action taken (moving to mp4_to_fix)
        echo "Moving to $OUTPUT_DIR_MP4_TO_FIX: $new_filename"

        # Copy the file to the mp4_to_fix directory
        cp "$mp4_file" "$new_filename"
    fi
done

echo "Processing complete."

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we are done with the metadata and we can go further.&lt;/p&gt;

&lt;h2&gt;
  
  
  ID : using proper id
&lt;/h2&gt;

&lt;p&gt;It may happen that your files are coming from different sources and have completely different id's, now as your files are prefixed with a date in format &lt;code&gt;20000101_000000&lt;/code&gt; you may erase the previous filename and use an identifier of your choice and an ad-hoc counter, here is an example of transformation of all id's to the format &lt;code&gt;20000101_000000_jpg_0001.jpg&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Set the input and output directories
input_dir="./input"
output_dir="./output"

# Make sure the output directory exists, create it if it doesn't
mkdir -p "$output_dir"

# Counter variable, starting at 1
counter=1

# Loop through sorted .jpg files, handling files with spaces correctly
find "$input_dir" -type f -name "*.jpg" | sort | while IFS= read -r file; do
    # Extract the first part of the filename (before the first three underscores)
    base_name=$(basename "$file")
    prefix=$(echo "$base_name" | cut -d'_' -f1-2)  # Extract "20000101_000000" part

    # Build the new filename with a 4-digit counter and "_jpg_" prefix
    new_filename=$(printf "%s_jpg_%04d.jpg" "$prefix" "$counter")

    # Copy the file to the output directory with the new filename
    cp "$file" "$output_dir/$new_filename"

    # Increment the counter
    ((counter++))
done

echo "Files renamed and copied to $output_dir"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compress images
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;ImageMagick&lt;/code&gt; from &lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-2-converting-jpeg-to-heic-1bbi"&gt;part 2&lt;/a&gt; you may convert all the images to the format of your choice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Input and Output directories
input_dir="./input"
output_dir="./output"

# Ensure the output directory exists
mkdir -p "$output_dir"

# Loop through all jpg files in the input directory
for img in "$input_dir"/*.jpg; do
    # Get the image dimensions (width x height)
    dimensions=$(identify -format "%wx%h" "$img")
    width=$(echo $dimensions | cut -d'x' -f1)
    height=$(echo $dimensions | cut -d'x' -f2)

    # Check for vertical images (height &amp;gt;= width)
    if [ "$height" -ge "$width" ]; then
        # Vertical and height &amp;gt; 1280, resize to height 1280
        if [ "$height" -gt 1280 ]; then
            output_file="$output_dir/$(basename "$img" .jpg).heic"
            magick "$img" -resize x1280 -quality 80 "$output_file"
            echo "Resized and converted $img to $output_file"
        else
            # Vertical image with height &amp;lt;= 1280, just convert to HEIC
            output_file="$output_dir/$(basename "$img" .jpg).heic"
            magick "$img" "$output_file"
            echo "Converted $img to $output_file"
        fi
    elif [ "$width" -gt "$height" ]; then
        # Landscape and width &amp;gt; 1280, resize to width 1280
        if [ "$width" -gt 1280 ]; then
            output_file="$output_dir/$(basename "$img" .jpg).heic"
            magick "$img" -resize 1280x -quality 80 "$output_file"
            echo "Resized and converted $img to $output_file"
        else
            # Landscape image with width &amp;lt;= 1280, just convert to HEIC
            output_file="$output_dir/$(basename "$img" .jpg).heic"
            magick "$img" "$output_file"
            echo "Converted $img to $output_file"
        fi
    else
        echo "Skipping $img: does not fit any criteria"
    fi
done

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compress videos
&lt;/h2&gt;

&lt;p&gt;Similar logic can be applied to the videos using &lt;code&gt;ffmpeg&lt;/code&gt; from &lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part-3-converting-mov-to-hevc-mpeg-4-3mje"&gt;part 3&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Hardcoded input directory and output directory
INPUT_DIR="./input"               # Input directory set to ./input
OUTPUT_DIR="./output"             # Output directory for converted MP4s

# Create the output directory if it doesn't exist
mkdir -p "$OUTPUT_DIR"

# Iterate through all MP4 files in the input directory and subdirectories
find "$INPUT_DIR" -type f -iname "*.mp4" | while read -r mp4_file; do
    # Get the base name of the MP4 file without the extension
    base_name=$(basename "$mp4_file" .mp4)

    # Get video dimensions (width and height) using ffprobe
    dimensions=$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 "$mp4_file")
    width=$(echo "$dimensions" | cut -d 'x' -f 1)
    height=$(echo "$dimensions" | cut -d 'x' -f 2)

    # Debugging: Log the dimensions
    echo "Dimensions of $mp4_file: Width=$width, Height=$height"

    # Initialize the new filename variable
    new_filename="${OUTPUT_DIR}/${base_name}_converted.mp4"

    # Check if rescaling is needed and apply the appropriate scale
    if [[ "$height" -ge "$width" &amp;amp;&amp;amp; "$height" -gt 1280 ]]; then
        # If height &amp;gt;= width and height &amp;gt; 1280, rescale to -2:1280
        scale="-2:1280"
        echo "Rescaling $mp4_file to $scale"
    elif [[ "$width" -gt "$height" &amp;amp;&amp;amp; "$width" -gt 1280 ]]; then
        # If width &amp;gt; height and width &amp;gt; 1280, rescale to 1280:-2
        scale="1280:-2"
        echo "Rescaling $mp4_file to $scale"
    else
        # No scaling needed
        scale=""
        echo "No rescaling needed for $mp4_file"
    fi

    # Run ffmpeg with or without scaling, based on the conditions
    ffmpeg -i "$mp4_file" \
           -r 30 -c:v libx265 -crf 28 -preset medium \
           -c:a aac -b:a 192k \
           -metadata creation_time="$formatted_date" \
           ${scale:+-vf "scale=$scale"} \
           "$new_filename"

done

echo "Processing complete."

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Overall, if some formats have a limited support for metadata, for example &lt;code&gt;png&lt;/code&gt;, it could be a good idea to encode it into the filename in order to make it available for an eventual conversion tool.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>productivity</category>
      <category>ai</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>Security : CVE-2024-3094 unauthorized remote SSH access</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Mon, 08 Apr 2024 17:44:10 +0000</pubDate>
      <link>https://dev.to/hexfloor/security-cve-2024-3094-unauthorized-remote-ssh-access-2ml8</link>
      <guid>https://dev.to/hexfloor/security-cve-2024-3094-unauthorized-remote-ssh-access-2ml8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This article will be less technical as usual, as all you need to know about &lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2024-3094"&gt;CVE-2024-3094&lt;/a&gt; is described on &lt;a href="https://jfrog.com/blog/xz-backdoor-attack-cve-2024-3094-all-you-need-to-know/"&gt;jFrog&lt;/a&gt; and &lt;a href="https://news.ycombinator.com/item?id=39890817"&gt;Y Combinator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This backdoor is massive. Just to give you an idea, there are maybe 2-3 entities in the world capable of performing this activity, if it was a single person then maybe less than a thousand people in the world would have had a skill to perform the activity, even less than a hundred would dare to try and maybe less than ten would reach this point. Purely technically speaking the person behind is as professional as &lt;a href="https://en.wikipedia.org/wiki/Usain_Bolt"&gt;Usain Bolt&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Taylor_Swift"&gt;Taylor Swift&lt;/a&gt; in the respective domain. How to deal with a situation when you have an adversary that strong and there is a non zero probability that a villain has a different point of view on your personal well-being ?&lt;/p&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;The principles are somewhat evident, however sometimes you may be tempted to trade a short-term benefit over an somewhat illusionary guidelines : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you'll never walk alone : use stable releases of mainstream standard solutions, then you will have thousand eyes watching your back and maybe one of them will be &lt;a href="https://twitter.com/AndresFreundTec"&gt;Andres Freund&lt;/a&gt;, from the point of view of probability theory, if a chance to discover a backdoor by an engineer is &lt;code&gt;0.001&lt;/code&gt; and there are &lt;code&gt;1000&lt;/code&gt; engineers looking at the code then the probability to discover a backdoor is
&lt;code&gt;1 - (1-0.001)^1000 = 1 - 0.368 = 0.632 = 63.2%&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;"If you fail to plan, you are planning to fail!" (c) : have a plan of which software you intend to use and why, a recurrent problem is the questionable application of &lt;a href="https://en.wikipedia.org/wiki/Fake_it_till_you_make_it"&gt;fake it till you make it&lt;/a&gt;, you should have a plan and this plan should take into account the boundary conditions since day 0 &lt;/li&gt;
&lt;li&gt;limit the number of software you are using to remediate &lt;a href="https://en.wikipedia.org/wiki/The_Paradox_of_Choice"&gt;the paradox of choice&lt;/a&gt; and to minimize the maintenance overhead&lt;/li&gt;
&lt;li&gt;apply the best &lt;a href="https://en.wikipedia.org/wiki/Computer_security_software"&gt;cybersecurity practices&lt;/a&gt; : the keyword is &lt;code&gt;apply&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;have a professional available that knows how the things should be done : still valid in 2024, you have plenty of software providing all the possible insights with hundreds or thousand of alerts / warnings / updates, if you wish to have the business running there must be an expert onsite. &lt;a href="https://twitter.com/realjstatham/status/1700446807816311077"&gt;"Those who know.......know."&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;have a plan B when the plan A has failed : someday, and that day may never come, you did everything right and still failed, the best thing you can do is to be ready for this day and to plan the recovery&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Every happy ending is vanished rather fast from memory. &lt;br&gt;
We have just witnessed an interception of a brutal attack.&lt;/p&gt;

</description>
      <category>security</category>
      <category>linux</category>
      <category>ssh</category>
      <category>cloud</category>
    </item>
    <item>
      <title>SQL Pro Tips : industrial AWS Athena SQL using WITH</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Thu, 28 Mar 2024 19:33:03 +0000</pubDate>
      <link>https://dev.to/hexfloor/sql-pro-tips-industrial-aws-athena-sql-using-with-4230</link>
      <guid>https://dev.to/hexfloor/sql-pro-tips-industrial-aws-athena-sql-using-with-4230</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A common industrial flow of data analysis consists of : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data preparation&lt;/li&gt;
&lt;li&gt;data aggregation&lt;/li&gt;
&lt;li&gt;data join&lt;/li&gt;
&lt;li&gt;data output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this guide I will show how to implement efficiently data preparation, aggregation and output.&lt;br&gt;
Feel free to check on &lt;a href="https://dev.to/hexfloor/sql-pro-tips-industrial-oracle-sql-using-with-ap8"&gt;industrial Oracle SQL using WITH&lt;/a&gt; and &lt;a href="https://dev.to/hexfloor/sql-pro-tips-aws-athena-sql-unpivot-cross-join-unnest-444e"&gt;AWS Athena SQL UNPIVOT : CROSS JOIN UNNEST&lt;/a&gt;, &lt;a href="https://www.geeksforgeeks.org/sql-with-clause/"&gt;SQL WITH clause&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Problem definition
&lt;/h2&gt;

&lt;p&gt;Let's define the following problem : as the input we have sale revenue per day, and we wish to have a json as the output with the sale revenue percentage per day of week and the sale revenue percentage per day of week within the group : weekdays and weekends. The problem definition is the same as for the Oracle SQL guide, therefore I will add an additional constraint : the output and the way of aggregation must be the same as for the Oracle SQL guide to make an easy cross check after each stage.&lt;/p&gt;
&lt;h2&gt;
  
  
  Parameters
&lt;/h2&gt;

&lt;p&gt;In order to write professional SQL we should better define all the parameters within a separate block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
)
select * from parameters;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F655x3c6vu8v2j1nvs5jj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F655x3c6vu8v2j1nvs5jj.png" alt="Image description" width="800" height="43"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a simple example how to use &lt;code&gt;with&lt;/code&gt;. Let's extend the success and go further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test data
&lt;/h2&gt;

&lt;p&gt;Now let's generate some test data : date and sale revenue for the date :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
),
data_in as (
  select 
    date_add('day', (t.v - 1), from_iso8601_date('2024-01-01')) as sale_date,
    sqrt(t.v * 31) as sale_revenue
  from unnest(sequence(1, 7)) t(v)
)
select * from data_in;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo622n2idxn8ucadktctk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo622n2idxn8ucadktctk.png" alt="Image description" width="800" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Now let's prepare our data for aggregation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data preparation
&lt;/h2&gt;

&lt;p&gt;At this stage we need to be sure that we have all the necessary information for the data aggregation and that all the data types are the ones that we need. Feel free to check &lt;a href="https://dev.to/hexfloor/how-to-make-money-with-java-and-c-ieee-754-and-the-real-life-010203-yes-but-how-5244"&gt;the guide on decimal data types&lt;/a&gt; which is applicable to our use case as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
),
data_in as (
  select 
    date_add('day', (t.v - 1), from_iso8601_date('2024-01-01')) as sale_date,
    sqrt(t.v * 31) as sale_revenue
  from unnest(sequence(1, 7)) t(v)
),
data_prep as (
  select 
    data_in_prep.sale_date,
    data_in_prep.sale_revenue,
    data_in_prep.sale_dow,
    (case when (data_in_prep.sale_dow = parameters.weekend_v0 or data_in_prep.sale_dow = parameters.weekend_v1) 
            then weekend_label else workweek_label end) as sale_type
  from (
        select
            sale_date,
            cast(sale_revenue as decimal(31,2)) as sale_revenue,
            trim( upper(date_format(sale_date, '%W'))) as sale_dow
        from data_in
  ) data_in_prep cross join parameters
)
select * from data_prep;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fni52uz6bplbl8dr78kic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fni52uz6bplbl8dr78kic.png" alt="Image description" width="800" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good! Now we are set to start data aggregation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data aggregation
&lt;/h2&gt;

&lt;p&gt;With properly prepared data the data aggregation is straight forward :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
),
data_in as (
  select 
    date_add('day', (t.v - 1), from_iso8601_date('2024-01-01')) as sale_date,
    sqrt(t.v * 31) as sale_revenue
  from unnest(sequence(1, 7)) t(v)
),
data_prep as (
  select 
    data_in_prep.sale_date,
    data_in_prep.sale_revenue,
    data_in_prep.sale_dow,
    (case when (data_in_prep.sale_dow = parameters.weekend_v0 or data_in_prep.sale_dow = parameters.weekend_v1) 
            then weekend_label else workweek_label end) as sale_type
  from (
        select
            sale_date,
            cast(sale_revenue as decimal(31,2)) as sale_revenue,
            trim( upper(date_format(sale_date, '%W'))) as sale_dow
        from data_in
  ) data_in_prep cross join parameters
),
data_agg as (
    select
        sale_dow,
        sale_revenue,
        100 * sale_revenue / (sum(sale_revenue) over ())  as sale_revenue_perc,
        sale_type,
        100 * sale_revenue / (sum(sale_revenue) over (partition by sale_type)) as sale_type_revenue_perc
    from data_prep
    order by sale_revenue asc

)
select * from data_agg;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvb56sr1trmitcyjgl6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvb56sr1trmitcyjgl6a.png" alt="Image description" width="800" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are done with the aggregation and as we have a single data source we will skip &lt;code&gt;join&lt;/code&gt; and we may start with data output. Depending on the consumer we may have multiple data outputs by caching the results of data aggregation with a view creation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data output
&lt;/h2&gt;

&lt;p&gt;AWS Athena has a native support for json output :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
),
data_in as (
  select 
    date_add('day', (t.v - 1), from_iso8601_date('2024-01-01')) as sale_date,
    sqrt(t.v * 31) as sale_revenue
  from unnest(sequence(1, 7)) t(v)
),
data_prep as (
  select 
    data_in_prep.sale_date,
    data_in_prep.sale_revenue,
    data_in_prep.sale_dow,
    (case when (data_in_prep.sale_dow = parameters.weekend_v0 or data_in_prep.sale_dow = parameters.weekend_v1) 
            then weekend_label else workweek_label end) as sale_type
  from (
        select
            sale_date,
            cast(sale_revenue as decimal(31,2)) as sale_revenue,
            trim( upper(date_format(sale_date, '%W'))) as sale_dow
        from data_in
  ) data_in_prep cross join parameters
),
data_agg as (
    select
        sale_dow,
        sale_revenue,
        100 * sale_revenue / (sum(sale_revenue) over ())  as sale_revenue_perc,
        sale_type,
        100 * sale_revenue / (sum(sale_revenue) over (partition by sale_type)) as sale_type_revenue_perc
    from data_prep
    order by sale_revenue asc

),
data_output as (
    select 
        upper(json_object('sale_data' value '['||array_join(array_agg(sale_data_dow), ',') ||']' format json)) as json_output
    from (
        select
            json_object(
                'sale_dow' value sale_dow,
                'sale_revenue' value sale_revenue,
                'sale_revenue_perc' value sale_revenue_perc,
                'sale_type' value sale_type,
                'sale_type_revenue_perc' value sale_type_revenue_perc
            ) as sale_data_dow
        from data_agg
    )

)
select * from data_output;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here is the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"SALE_DATA":[{"SALE_TYPE_REVENUE_PERC":11.93,"SALE_REVENUE_PERC":7.42,"SALE_TYPE":"WORKWEEK","SALE_REVENUE":5.57,"SALE_DOW":"MONDAY"},{"SALE_TYPE_REVENUE_PERC":16.86,"SALE_REVENUE_PERC":10.49,"SALE_TYPE":"WORKWEEK","SALE_REVENUE":7.87,"SALE_DOW":"TUESDAY"},{"SALE_TYPE_REVENUE_PERC":20.66,"SALE_REVENUE_PERC":12.85,"SALE_TYPE":"WORKWEEK","SALE_REVENUE":9.64,"SALE_DOW":"WEDNESDAY"},{"SALE_TYPE_REVENUE_PERC":23.87,"SALE_REVENUE_PERC":14.85,"SALE_TYPE":"WORKWEEK","SALE_REVENUE":11.14,"SALE_DOW":"THURSDAY"},{"SALE_TYPE_REVENUE_PERC":26.68,"SALE_REVENUE_PERC":16.59,"SALE_TYPE":"WORKWEEK","SALE_REVENUE":12.45,"SALE_DOW":"FRIDAY"},{"SALE_TYPE_REVENUE_PERC":48.08,"SALE_REVENUE_PERC":18.18,"SALE_TYPE":"WEEKEND","SALE_REVENUE":13.64,"SALE_DOW":"SATURDAY"},{"SALE_TYPE_REVENUE_PERC":51.92,"SALE_REVENUE_PERC":19.63,"SALE_TYPE":"WEEKEND","SALE_REVENUE":14.73,"SALE_DOW":"SUNDAY"}]}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Athena there are more efficient ways to use parameters and to aggregate the data. By the way, if you know how to use &lt;code&gt;json_array&lt;/code&gt; in this context, feel free to write in the comments. There are more efficient ways to output the data using &lt;a href="https://docs.aws.amazon.com/fr_fr/athena/latest/ug/unload.html"&gt;unload&lt;/a&gt;. Stay tuned!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>database</category>
      <category>bigdata</category>
      <category>sql</category>
    </item>
    <item>
      <title>SQL Pro Tips : industrial GCP BigQuery SQL using WITH</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Thu, 28 Mar 2024 19:32:47 +0000</pubDate>
      <link>https://dev.to/hexfloor/sql-pro-tips-industrial-gcp-bigquery-sql-using-with-57fi</link>
      <guid>https://dev.to/hexfloor/sql-pro-tips-industrial-gcp-bigquery-sql-using-with-57fi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A common industrial flow of data analysis consists of : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data preparation&lt;/li&gt;
&lt;li&gt;data aggregation&lt;/li&gt;
&lt;li&gt;data join&lt;/li&gt;
&lt;li&gt;data output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this guide I will show how to implement efficiently data preparation, aggregation and output.&lt;br&gt;
Feel free to check on &lt;a href="https://dev.to/hexfloor/sql-pro-tips-industrial-oracle-sql-using-with-ap8"&gt;industrial Oracle SQL using WITH&lt;/a&gt; and &lt;a href="https://dev.to/hexfloor/sql-pro-tips-gcp-bigquery-sql-cross-join-with-unpivot-unnest-33lf"&gt;GCP BigQuery SQL CROSS JOIN with UNPIVOT UNNEST&lt;/a&gt;, &lt;a href="https://www.geeksforgeeks.org/sql-with-clause/"&gt;SQL WITH clause&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Problem definition
&lt;/h2&gt;

&lt;p&gt;Let's define the following problem : as the input we have sale revenue per day, and we wish to have a json as the output with the sale revenue percentage per day of week and the sale revenue percentage per day of week within the group : weekdays and weekends. The problem definition is the same as for the Oracle SQL guide, therefore I will add an additional constraint : the output and the way of aggregation must be the same as for the Oracle SQL guide to make an easy cross check after each stage.&lt;/p&gt;
&lt;h2&gt;
  
  
  Parameters
&lt;/h2&gt;

&lt;p&gt;In order to write professional SQL we should better define all the parameters within a separate block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
)
select * from parameters;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lskxij9oeapjtlf1mdm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lskxij9oeapjtlf1mdm.png" alt="Image description" width="800" height="48"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a simple example how to use &lt;code&gt;with&lt;/code&gt;. Let's extend the success and go further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test data
&lt;/h2&gt;

&lt;p&gt;Now let's generate some test data : date and sale revenue for the date :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
),
data_in as (
  select 
    date_add(date('2024-01-01'), interval (v - 1) DAY) as sale_date,
    sqrt(v * 31) as sale_revenue
  from unnest(generate_array(1, 7)) v
)
select * from data_in;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshk3utby0dc8pm3ion2k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshk3utby0dc8pm3ion2k.png" alt="Image description" width="463" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Now let's prepare our data for aggregation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data preparation
&lt;/h2&gt;

&lt;p&gt;At this stage we need to be sure that we have all the necessary information for the data aggregation and that all the data types are the ones that we need. Feel free to check &lt;a href="https://dev.to/hexfloor/how-to-make-money-with-java-and-c-ieee-754-and-the-real-life-010203-yes-but-how-5244"&gt;the guide on decimal data types&lt;/a&gt; which is applicable to our use case as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
),
data_in as (
  select 
    date_add(date('2024-01-01'), interval (v - 1) DAY) as sale_date,
    sqrt(v * 31) as sale_revenue
  from unnest(generate_array(1, 7)) v
),
data_prep as (
  select 
    data_in_prep.sale_date,
    data_in_prep.sale_revenue,
    data_in_prep.sale_dow,
    (case when (data_in_prep.sale_dow = parameters.weekend_v0 or data_in_prep.sale_dow = parameters.weekend_v1) 
            then weekend_label else workweek_label end) as sale_type
  from (
        select
            sale_date,
            round(cast(sale_revenue as numeric),2) as sale_revenue,
            trim(upper(format_date('%A',sale_date))) as sale_dow
        from data_in
  ) data_in_prep cross join parameters
)
select * from data_prep;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31koqvht5lrdlrszq4yp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31koqvht5lrdlrszq4yp.png" alt="Image description" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good! Now we are set to start data aggregation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data aggregation
&lt;/h2&gt;

&lt;p&gt;With properly prepared data the data aggregation is straight forward :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
),
data_in as (
  select 
    date_add(date('2024-01-01'), interval (v - 1) DAY) as sale_date,
    sqrt(v * 31) as sale_revenue
  from unnest(generate_array(1, 7)) v
),
data_prep as (
  select 
    data_in_prep.sale_date,
    data_in_prep.sale_revenue,
    data_in_prep.sale_dow,
    (case when (data_in_prep.sale_dow = parameters.weekend_v0 or data_in_prep.sale_dow = parameters.weekend_v1) 
            then weekend_label else workweek_label end) as sale_type
  from (
        select
            sale_date,
            round(cast(sale_revenue as numeric),2) as sale_revenue,
            trim(upper(format_date('%A',sale_date))) as sale_dow
        from data_in
  ) data_in_prep cross join parameters
),
data_agg as (
    select
        sale_dow,
        sale_revenue,
        round(cast(100 * sale_revenue / (sum(sale_revenue) over ()) 
            as numeric), 2) as sale_revenue_perc,
        sale_type,
        round(cast(100 * sale_revenue / (sum(sale_revenue) over (partition by sale_type)) 
            as numeric), 2) as sale_type_revenue_perc
    from data_prep
    order by sale_revenue asc

)
select * from data_agg;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6b1k0cx6ruxnvubi4aq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6b1k0cx6ruxnvubi4aq.png" alt="Image description" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We may also check the chart:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvslr6ps8mtqku82pd6p2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvslr6ps8mtqku82pd6p2.png" alt="Image description" width="800" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and the json : &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuqqmgeptq20l92e0d3th.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuqqmgeptq20l92e0d3th.png" alt="Image description" width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are done with the aggregation and as we have a single data source we will skip &lt;code&gt;join&lt;/code&gt; and we may start with data output. Depending on the consumer we may have multiple data outputs by caching the results of data aggregation with a view creation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data output
&lt;/h2&gt;

&lt;p&gt;BigQuery has a native support for json output :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
),
data_in as (
  select 
    date_add(date('2024-01-01'), interval (v - 1) DAY) as sale_date,
    sqrt(v * 31) as sale_revenue
  from unnest(generate_array(1, 7)) v
),
data_prep as (
  select 
    data_in_prep.sale_date,
    data_in_prep.sale_revenue,
    data_in_prep.sale_dow,
    (case when (data_in_prep.sale_dow = parameters.weekend_v0 or data_in_prep.sale_dow = parameters.weekend_v1) 
            then weekend_label else workweek_label end) as sale_type
  from (
        select
            sale_date,
            round(cast(sale_revenue as numeric),2) as sale_revenue,
            trim(upper(format_date('%A',sale_date))) as sale_dow
        from data_in
  ) data_in_prep cross join parameters
),
data_agg as (
    select
        sale_dow,
        sale_revenue,
        round(cast(100 * sale_revenue / (sum(sale_revenue) over ()) 
            as numeric), 2) as sale_revenue_perc,
        sale_type,
        round(cast(100 * sale_revenue / (sum(sale_revenue) over (partition by sale_type)) 
            as numeric), 2) as sale_type_revenue_perc
    from data_prep
    order by sale_revenue asc

),
data_output as (
  select 
    upper(to_json_string(json_object('sale_data', sale_data_dow_array))) as json_output
  from (
    select array_agg(sale_data_dow) as sale_data_dow_array
      from(
        select 
          to_json(data_agg_output) as sale_data_dow
        from (
          select 
            data_agg.sale_dow,
            trim(cast(data_agg.sale_revenue as string format '999999.99')) as sale_revenue,
            trim(cast(data_agg.sale_revenue_perc as string format '999999.99')) as sale_revenue_perc,
            data_agg.sale_type,
            trim(cast(data_agg.sale_type_revenue_perc as string format '999999.99')) as sale_type_revenue_perc
          from data_agg
        ) data_agg_output
    )
  )
)
select * from data_output;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here is the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"SALE_DATA":[{"SALE_DOW":"MONDAY","SALE_REVENUE":"5.57","SALE_REVENUE_PERC":"7.42","SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":"11.93"},{"SALE_DOW":"TUESDAY","SALE_REVENUE":"7.87","SALE_REVENUE_PERC":"10.49","SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":"16.86"},{"SALE_DOW":"WEDNESDAY","SALE_REVENUE":"9.64","SALE_REVENUE_PERC":"12.85","SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":"20.66"},{"SALE_DOW":"THURSDAY","SALE_REVENUE":"11.14","SALE_REVENUE_PERC":"14.85","SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":"23.87"},{"SALE_DOW":"FRIDAY","SALE_REVENUE":"12.45","SALE_REVENUE_PERC":"16.59","SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":"26.68"},{"SALE_DOW":"SATURDAY","SALE_REVENUE":"13.64","SALE_REVENUE_PERC":"18.18","SALE_TYPE":"WEEKEND","SALE_TYPE_REVENUE_PERC":"48.08"},{"SALE_DOW":"SUNDAY","SALE_REVENUE":"14.73","SALE_REVENUE_PERC":"19.63","SALE_TYPE":"WEEKEND","SALE_TYPE_REVENUE_PERC":"51.92"}]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With BigQuery there are more efficient ways to use parameters and to aggregate the data, if you know them feel free to post in the comments. Stay tuned!&lt;/p&gt;

</description>
      <category>gcp</category>
      <category>sql</category>
      <category>database</category>
      <category>bigdata</category>
    </item>
    <item>
      <title>SQL Pro Tips : industrial Oracle SQL using WITH</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Thu, 28 Mar 2024 19:32:14 +0000</pubDate>
      <link>https://dev.to/hexfloor/sql-pro-tips-industrial-oracle-sql-using-with-ap8</link>
      <guid>https://dev.to/hexfloor/sql-pro-tips-industrial-oracle-sql-using-with-ap8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A common industrial flow of data analysis consists of : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data preparation&lt;/li&gt;
&lt;li&gt;data aggregation&lt;/li&gt;
&lt;li&gt;data join&lt;/li&gt;
&lt;li&gt;data output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this guide I will show how to implement efficiently data preparation, aggregation and output.&lt;br&gt;
Feel free to check on &lt;a href="https://dev.to/hexfloor/sql-pro-tips-oracle-xe-quickstart-jei"&gt;Oracle XE Quick Start&lt;/a&gt; and &lt;a href="https://dev.to/hexfloor/sql-pro-tips-oracle-sql-cross-join-with-unpivot-57ff"&gt;Oracle SQL CROSS JOIN with UNPIVOT&lt;/a&gt;, &lt;a href="https://www.geeksforgeeks.org/sql-with-clause/"&gt;SQL WITH clause&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Problem definition
&lt;/h2&gt;

&lt;p&gt;Let's define the following problem : as the input we have sale revenue per day, and we wish to have a json as the output with the sale revenue percentage per day of week and the sale revenue percentage per day of week within the group : weekdays and weekends.&lt;/p&gt;
&lt;h2&gt;
  
  
  Parameters
&lt;/h2&gt;

&lt;p&gt;In order to write professional SQL we should better define all the parameters within a separate block :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH
PARAMETERS AS (
    SELECT 
        'SATURDAY' AS WEEKEND_V0,
        'SUNDAY' AS WEEKEND_V1,
        'WEEKEND' AS WEEKEND_LABEL,
        'WORKWEEK' AS WORKWEEK_LABEL
    FROM DUAL
)
SELECT * FROM PARAMETERS;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkx5hkwnjo0n0ol1zf20k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkx5hkwnjo0n0ol1zf20k.png" alt="Image description" width="800" height="69"&gt;&lt;/a&gt;&lt;br&gt;
This is a simple example how to use &lt;code&gt;with&lt;/code&gt;. Let's extend the success and go further.&lt;/p&gt;
&lt;h2&gt;
  
  
  Test data
&lt;/h2&gt;

&lt;p&gt;Now let's generate some test data : date and sale revenue for the date :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH
PARAMETERS AS (
    SELECT 
        'SATURDAY' AS WEEKEND_V0,
        'SUNDAY' AS WEEKEND_V1,
        'WEEKEND' AS WEEKEND_LABEL,
        'WORKWEEK' AS WORKWEEK_LABEL
    FROM DUAL
),
DATA_IN AS (
    SELECT
        TO_TIMESTAMP('2024-01-01', 'YYYY-MM-DD') + (ROWNUM - 1) AS SALE_DATE,
        SQRT(ROWNUM * 31 ) AS SALE_REVENUE
    FROM DUAL
    CONNECT BY LEVEL &amp;lt;= 7
)
SELECT * FROM DATA_IN;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfyrwnmjdmy344zoqt1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfyrwnmjdmy344zoqt1x.png" alt="Image description" width="445" height="244"&gt;&lt;/a&gt;&lt;br&gt;
Great! Now let's prepare our data for aggregation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Data preparation
&lt;/h2&gt;

&lt;p&gt;At this stage we need to be sure that we have all the necessary information for the data aggregation and that all the data types are the ones that we need. Feel free to check &lt;a href="https://dev.to/hexfloor/how-to-make-money-with-java-and-c-ieee-754-and-the-real-life-010203-yes-but-how-5244"&gt;the guide on decimal data types&lt;/a&gt; which is applicable to our use case as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH
PARAMETERS AS (
    SELECT 
        'SATURDAY' AS WEEKEND_V0,
        'SUNDAY' AS WEEKEND_V1,
        'WEEKEND' AS WEEKEND_LABEL,
        'WORKWEEK' AS WORKWEEK_LABEL
    FROM DUAL
),
DATA_IN AS (
    SELECT
        TO_TIMESTAMP('2024-01-01', 'YYYY-MM-DD') + (ROWNUM - 1) AS SALE_DATE,
        SQRT(ROWNUM * 31 ) AS SALE_REVENUE
    FROM DUAL
    CONNECT BY LEVEL &amp;lt;= 7
),
DATA_PREP AS (
    SELECT 
        DATA_IN_PREP.SALE_DATE,
        DATA_IN_PREP.SALE_REVENUE,
        DATA_IN_PREP.SALE_DOW,
        (CASE WHEN (DATA_IN_PREP.SALE_DOW = PARAMETERS.WEEKEND_V0 OR DATA_IN_PREP.SALE_DOW = PARAMETERS.WEEKEND_V1) 
            THEN WEEKEND_LABEL ELSE WORKWEEK_LABEL END) AS SALE_TYPE        
    FROM (
        SELECT
            SALE_DATE,
            CAST(SALE_REVENUE AS DECIMAL (31, 2)) AS SALE_REVENUE,
            TRIM(TO_CHAR(SALE_DATE, 'DAY')) AS SALE_DOW
        FROM DATA_IN) DATA_IN_PREP
    CROSS JOIN PARAMETERS
)
SELECT * FROM DATA_PREP;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxipwfqbtsdinrrzc7y8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxipwfqbtsdinrrzc7y8.png" alt="Image description" width="745" height="249"&gt;&lt;/a&gt;&lt;br&gt;
Good! Now we are set to start data aggregation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Data aggregation
&lt;/h2&gt;

&lt;p&gt;With properly prepared data the data aggregation is straight forward :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with 
parameters as (
    select 
        'SATURDAY' as weekend_v0,
        'SUNDAY' as weekend_v1,
        'WEEKEND' as weekend_label,
        'WORKWEEK' as workweek_label
),
data_in as (
  select 
    date_add(date('2024-01-01'), interval (value - 1) DAY) as sale_date,
    sqrt(value) as sale_revenue
  from unnest(generate_array(1, 7)) value
),
data_prep as (
  select 
    data_in_prep.sale_date,
    data_in_prep.sale_revenue,
    data_in_prep.sale_dow,
    (case when (data_in_prep.sale_dow = parameters.weekend_v0 or data_in_prep.sale_dow = parameters.weekend_v1) 
            then weekend_label else workweek_label end) as sale_type
  from (
        select
            sale_date,
            round(cast(sale_revenue as numeric),2) as sale_revenue,
            trim(upper(format_date('%A',sale_date))) as sale_dow
        from data_in
  ) data_in_prep cross join parameters
),
data_agg as (
    select
        sale_dow,
        sale_revenue,
        sale_type,
        round(cast(100 * sale_revenue / (sum(sale_revenue) over (partition by sale_type)) 
            as numeric), 2) as sale_type_revenue_perc
    from data_prep
    order by sale_revenue asc

)
select * from data_agg;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are done with the aggregation and as we have a single data source we will skip &lt;code&gt;join&lt;/code&gt; and we may start with data output. Depending on the consumer we may have multiple data outputs by caching the results of data aggregation with a view creation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data output
&lt;/h2&gt;

&lt;p&gt;Oracle has a native support for json output :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH
PARAMETERS AS (
    SELECT 
        'SATURDAY' AS WEEKEND_V0,
        'SUNDAY' AS WEEKEND_V1,
        'WEEKEND' AS WEEKEND_LABEL,
        'WORKWEEK' AS WORKWEEK_LABEL
    FROM DUAL
),
DATA_IN AS (
    SELECT
        TO_TIMESTAMP('2024-01-01', 'YYYY-MM-DD') + (ROWNUM - 1) AS SALE_DATE,
        SQRT(ROWNUM * 31 ) AS SALE_REVENUE
    FROM DUAL
    CONNECT BY LEVEL &amp;lt;= 7
),
DATA_PREP AS (
    SELECT 
        DATA_IN_PREP.SALE_DATE,
        DATA_IN_PREP.SALE_REVENUE,
        DATA_IN_PREP.SALE_DOW,
        (CASE WHEN (DATA_IN_PREP.SALE_DOW = PARAMETERS.WEEKEND_V0 OR DATA_IN_PREP.SALE_DOW = PARAMETERS.WEEKEND_V1) 
            THEN WEEKEND_LABEL ELSE WORKWEEK_LABEL END) AS SALE_TYPE        
    FROM (
        SELECT
            SALE_DATE,
            CAST(SALE_REVENUE AS DECIMAL (31, 2)) AS SALE_REVENUE,
            TRIM(TO_CHAR(SALE_DATE, 'DAY')) AS SALE_DOW
        FROM DATA_IN) DATA_IN_PREP
    CROSS JOIN PARAMETERS
),
DATA_AGG AS (
    SELECT
        SALE_DOW,
        SALE_REVENUE,
        SALE_TYPE,
        CAST(100 * SALE_REVENUE / (SUM(SALE_REVENUE) OVER (PARTITION BY SALE_TYPE)) 
            AS DECIMAL(31,2)) AS SALE_TYPE_REVENUE_PERC
    FROM DATA_PREP
    ORDER BY SALE_REVENUE ASC

),
DATA_OUTPUT AS (
    SELECT JSON_OBJECT(*) AS JSON_OUTPUT FROM(
        SELECT  JSON_ARRAYAGG (  
            JSON_OBJECT (*) RETURNING CLOB   
        ) AS SALE_DATA
        FROM DATA_AGG
    )
)
SELECT * FROM DATA_OUTPUT;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here is the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"SALE_DATA":[{"SALE_DOW":"TUESDAY","SALE_REVENUE":7.87,"SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":16.86},{"SALE_DOW":"WEDNESDAY","SALE_REVENUE":9.64,"SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":20.66},{"SALE_DOW":"THURSDAY","SALE_REVENUE":11.14,"SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":23.87},{"SALE_DOW":"FRIDAY","SALE_REVENUE":12.45,"SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":26.68},{"SALE_DOW":"SATURDAY","SALE_REVENUE":13.64,"SALE_TYPE":"WEEKEND","SALE_TYPE_REVENUE_PERC":48.08},{"SALE_DOW":"SUNDAY","SALE_REVENUE":14.73,"SALE_TYPE":"WEEKEND","SALE_TYPE_REVENUE_PERC":51.92},{"SALE_DOW":"MONDAY","SALE_REVENUE":5.57,"SALE_TYPE":"WORKWEEK","SALE_TYPE_REVENUE_PERC":11.93}]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQL is still good for the data analysis, it's immortal as Microsoft Excel. Feel free to add comments if some steps require additional details. Stay tuned !&lt;/p&gt;

</description>
      <category>sql</category>
      <category>oracle</category>
      <category>bigdata</category>
      <category>database</category>
    </item>
  </channel>
</rss>
