<?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.us-east-2.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 : Windows Notifications</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Tue, 14 Jul 2026 15:32:56 +0000</pubDate>
      <link>https://dev.to/hexfloor/cybersecurity-101-windows-notifications-5afn</link>
      <guid>https://dev.to/hexfloor/cybersecurity-101-windows-notifications-5afn</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;So imagine you are focused on your cappuccino-frappuccino doing something very important on you win laptop and then have a cringe attack due to the unknown Phone Link notification : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Complete linking devices&lt;br&gt;
Your PC and mobile device are almost linked.&lt;br&gt;
Click here to continue linking devices.&lt;br&gt;
via Phone Link&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then you switch off bluetooth, wifi, laptop - and you are right.&lt;br&gt;
What to do next ?&lt;/p&gt;
&lt;h2&gt;
  
  
  Basic checks
&lt;/h2&gt;

&lt;p&gt;Settings -&amp;gt; Bluetooth &amp;amp; devices -&amp;gt; Mobile devices&lt;br&gt;
Settings -&amp;gt; Accounts -&amp;gt; Email &amp;amp; accounts&lt;br&gt;
Inspect recent notifications in Event Viewer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eventvwr.msc
&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;Applications and Services Logs
 └ Microsoft
    └ Windows
       └ Notifications
&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;Applications and Services Logs
 └ Microsoft
    └ Windows
       └ Shell-Core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Digital forensics
&lt;/h2&gt;

&lt;p&gt;Windows stores toast notifications in a local database,&lt;br&gt;
hence you need to install &lt;a href="https://sqlite.org/download.html" rel="noopener noreferrer"&gt;sqlite&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;LOCALAPPDATA&lt;/span&gt;&lt;span class="s2"&gt;\Microsoft\Windows\Notifications"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&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 console"&gt;&lt;code&gt;&lt;span class="gp"&gt;    Directory: C:\Users\$&lt;/span&gt;USERNAME&lt;span class="se"&gt;\A&lt;/span&gt;ppData&lt;span class="se"&gt;\L&lt;/span&gt;ocal&lt;span class="se"&gt;\M&lt;/span&gt;icrosoft&lt;span class="se"&gt;\W&lt;/span&gt;indows&lt;span class="se"&gt;\N&lt;/span&gt;otifications
&lt;span class="go"&gt;

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         1/1/2026    0:00 AM                wpnidm
-a----         1/1/2026    0:00 AM        1000000 wpndatabase.db
-a----         1/1/2026    0:00 AM          10000 wpndatabase.db-shm
-a----         1/1/2026    0:00 AM        1000000 wpndatabase.db-wal
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;wpndatabase.db is a SQLite database.&lt;br&gt;
connect to the database :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlite3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;:LOCALAPPDATA&lt;/span&gt;&lt;span class="se"&gt;\M&lt;/span&gt;&lt;span class="s2"&gt;icrosoft&lt;/span&gt;&lt;span class="se"&gt;\W&lt;/span&gt;&lt;span class="s2"&gt;indows&lt;/span&gt;&lt;span class="se"&gt;\N&lt;/span&gt;&lt;span class="s2"&gt;otifications&lt;/span&gt;&lt;span class="se"&gt;\w&lt;/span&gt;&lt;span class="s2"&gt;pndatabase.db"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;query the &lt;code&gt;Notification&lt;/code&gt; table&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;mode&lt;/span&gt; &lt;span class="k"&gt;column&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArrivalTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Payload&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Notification&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you will have something like :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;Id: [REDACTED]
HandlerId: [REDACTED]
Type: toast
ArrivalTime: [REDACTED]

Payload:
&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;toast&lt;/span&gt; &lt;span class="na"&gt;activationType=&lt;/span&gt;&lt;span class="s"&gt;"protocol"&lt;/span&gt;
       &lt;span class="na"&gt;launch=&lt;/span&gt;&lt;span class="s"&gt;"ms-phone:fre/?cid=[REDACTED]&amp;amp;ref=FreIncompleteToast&amp;amp;reason=IncompleteNotificationsToast"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;visual&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;binding&lt;/span&gt; &lt;span class="na"&gt;template=&lt;/span&gt;&lt;span class="s"&gt;"ToastGeneric"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;hint-maxLines=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Complete linking devices&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;text&amp;gt;&lt;/span&gt;Your PC and mobile device are almost linked&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/binding&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/visual&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/toast&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here you may see the following : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ms-phone: = Phone Link protocol handler&lt;/li&gt;
&lt;li&gt;fre = First Run Experience&lt;/li&gt;
&lt;li&gt;IncompleteNotificationsToast = setup reminder system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So Windows is doing this:&lt;/p&gt;

&lt;p&gt;“Phone Link is installed, but setup is not completed -&amp;gt; show reminder toast”&lt;/p&gt;

&lt;p&gt;Bingo.&lt;br&gt;
❌ No external device was involved&lt;br&gt;
❌ No pairing request happened&lt;br&gt;
❌ No phone initiated anything&lt;br&gt;
This was:&lt;br&gt;
✔ A local Windows notification&lt;br&gt;
✔ Generated by Phone Link app (Microsoft.YourPhone)&lt;br&gt;
✔ Triggered by onboarding logic (FRE = First Run Experience)&lt;/p&gt;
&lt;h2&gt;
  
  
  Post Mortem
&lt;/h2&gt;

&lt;p&gt;If you don't need it - remove it. &lt;br&gt;
Whether it's Phone Link or any other application.&lt;br&gt;
Settings -&amp;gt; Apps -&amp;gt; Installed apps -&amp;gt; Phone Link -&amp;gt; Advanced options -&amp;gt; Background permissions -&amp;gt; Off&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-AppxPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Microsoft.YourPhone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Remove-AppxPackage&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was year 2026 and we had Claude Mythos and at the same time shell scripting, SQL and XML. Interesting time to live.&lt;/p&gt;

&lt;p&gt;J’y penserai demain.&lt;br&gt;
Bon vent!&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>systemdesign</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>America : from asteroid to AI, a brief history of breakthroughs</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Sat, 04 Jul 2026 07:04:27 +0000</pubDate>
      <link>https://dev.to/hexfloor/america-from-asteroid-to-ai-a-brief-history-of-breakthroughs-1456</link>
      <guid>https://dev.to/hexfloor/america-from-asteroid-to-ai-a-brief-history-of-breakthroughs-1456</guid>
      <description>&lt;h2&gt;
  
  
  _
&lt;/h2&gt;

&lt;p&gt;Today is the day for somewhat less technical more philosophical article on how the USA did become a country where a breakthrough is an ordinary extraordinary event. I will look at the country as more like a philosophical idea and I chose deliberately to mention no historical figure, as &lt;a href="https://en.wikipedia.org/wiki/Batman_in_film#The_Dark_Knight_trilogy" rel="noopener noreferrer"&gt;a hero can be anyone&lt;/a&gt;, and subjectively I find it more objective to view history as a flow that creates the opportunities that are filled by one person or another.&lt;br&gt;
In memory we have strong references to the personalities and less focus on the societies - I wish here to present a slightly different perspective. &lt;br&gt;
Also I choose to select positive subjects only as it is a postal card for the &lt;a href="https://en.wikipedia.org/wiki/Independence_Day_(United_States)" rel="noopener noreferrer"&gt;anniversary&lt;/a&gt; of the USA. &lt;br&gt;
Hence yes, it will be a highly subjective and contradictory selection of random facts that I find important. &lt;br&gt;
C'est la vie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asteroid
&lt;/h2&gt;

&lt;p&gt;The life was peaceful and stressful, Tyrannosauruses (T. rexes) could be found chilling under the sun, till one day &lt;a href="https://en.wikipedia.org/wiki/Chicxulub_crater" rel="noopener noreferrer"&gt;a guest from the space&lt;/a&gt; heavily affected their way of lives and gave space to the most dangerous predators with both eyes looking forward and very functional hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beringia
&lt;/h2&gt;

&lt;p&gt;It's just fascinating how an estimated population of 1280 individuals could survive for 117000 years from 930000 to 817000 years ago.&lt;br&gt;
Thank you for having survived! &lt;br&gt;
Then around 20000 ago some 500 individuals made it to Alaska. Why? Who knows, maybe the grass was greener there. That's the power of small steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  European Exploration
&lt;/h2&gt;

&lt;p&gt;Since 1492 Europeans started coming to the America and till now people come and go, somehow the first colonies reiterated the known pattern - being squeezed between the Appalachian mountains and the Atlantic ocean formed an identity, it started to feel like home.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cumberland Gap
&lt;/h2&gt;

&lt;p&gt;Once it started to be too crowded some of the settlers decided to move further west, to see whether the grass was greener on the other side of the mountain ridge. That was the major preparation event for the rapid transformation of the society.&lt;/p&gt;

&lt;h2&gt;
  
  
  American Independence 1776
&lt;/h2&gt;

&lt;p&gt;From zero to hero in a matter of 30 years : once the USA was established the timeline to become a continental power was remarkably swift - the Louisiana purchase in 1803 sealed the transformation. &lt;br&gt;
To my opinion this is the exact time when the core feature of American society had been formed - capacity to scale innovation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Canals and railways
&lt;/h2&gt;

&lt;p&gt;The single answer why the New York City is rich today is Erie Canal.&lt;br&gt;
The question was how to organize the large territory and to make it efficient for doing business, roughly how to scale the success of the first colonies.&lt;br&gt;
The railways: Chicago to San Francisco, New Orleans to Los Angeles, St. Paul (Minneapolis) to Seattle - that was the answer.&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Once_Upon_a_Time_in_the_West" rel="noopener noreferrer"&gt;Once Upon a Time in the West&lt;/a&gt; - when you have a railway station you can go anywhere.&lt;br&gt;
Another way to travel from East to West was through the Panama Canal. That was how the country became a good place for doing business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Industry
&lt;/h2&gt;

&lt;p&gt;Once you have an infrastructure you may scale fast : telegraph, telephone, steel, oil, electricity, cars, aviation.&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/There_Will_Be_Blood" rel="noopener noreferrer"&gt;There Will Be Blood&lt;/a&gt; - mentality shift in the society, new moral order, from community to individualism, from religion to capitalism. And the cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Great Depression
&lt;/h2&gt;

&lt;p&gt;Once you know how to scale success eventually you may end up scaling failure. Great it happened then, so it has less chances to happen again.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Manhattan Project
&lt;/h2&gt;

&lt;p&gt;This is the hardest part to write. It's a story of another country that had casual visits of foreigners, went to isolation, ended isolation, launched a study if they should evolve their institutions to match the ones on the West, decided to change their way of living, became strong and successful. &lt;a href="https://en.wikipedia.org/wiki/Sh%C5%8Dgun_(2024_TV_series)" rel="noopener noreferrer"&gt;Shogun&lt;/a&gt; - &lt;a href="https://en.wikipedia.org/wiki/The_Pacific_(miniseries)" rel="noopener noreferrer"&gt;The Pacific&lt;/a&gt; - &lt;a href="https://en.wikipedia.org/wiki/Oppenheimer_(film)" rel="noopener noreferrer"&gt;The Manhattan Project&lt;/a&gt;. &lt;br&gt;
Success is not the only dimension, and not the main one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appolo 13
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This is one small step for a man, one giant leap for mankind&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;So far the Moon Landing achievement is unmatched.&lt;br&gt;
To my opinion, the &lt;a href="https://en.wikipedia.org/wiki/Apollo_13_(film)" rel="noopener noreferrer"&gt;Appolo 13&lt;/a&gt; is unmatched in a different dimension - successful failure, the most focused effort in a limited time.&lt;br&gt;
&lt;em&gt;Houston, we’ve had a problem.&lt;/em&gt;&lt;br&gt;
How to put the square peg into the round hole?&lt;br&gt;
The mission itself became a redefinition of success.&lt;/p&gt;

&lt;h2&gt;
  
  
  Silicon Valley
&lt;/h2&gt;

&lt;p&gt;Research -&amp;gt; startup -&amp;gt; industry.&lt;br&gt;
Universities + talents + capital + risk tolerant culture + density + good weather + freedom = fast scale of innovation.&lt;br&gt;
The equation is simple, the variables are known.&lt;br&gt;
The Silicon Valley turned the internet into a product and placed it in people's pockets.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI
&lt;/h2&gt;

&lt;p&gt;We are here now.&lt;br&gt;
The founders of the AI-startups are sitting around the same table as the country leaders.&lt;br&gt;
The water is needed by the servers and by the people.&lt;br&gt;
The innovation cycle is short.&lt;br&gt;
There will be a disruption, every innovation brings disruption.&lt;br&gt;
The cognitive load in 2026 is hard, you need to be ready for everything, and the world is changing fast.&lt;br&gt;
The ongoing breakthrough might be the most impressive in the human history.&lt;br&gt;
It won’t be smooth, and it will raise both everyday and philosophical questions.&lt;br&gt;
For example : what is the meaning of life?&lt;/p&gt;

&lt;h2&gt;
  
  
  ...
&lt;/h2&gt;

&lt;p&gt;This article has no new thoughts, and the general structure is somewhat inspired by &lt;a href="https://en.wikipedia.org/wiki/2001:_A_Space_Odyssey" rel="noopener noreferrer"&gt;2001 : A Space Odyssey&lt;/a&gt;.&lt;br&gt;
It serves as an appetizer that invites to unfold different layers of context and to see why the scale of innovation is a core American feature.&lt;/p&gt;

&lt;p&gt;Happy 250th anniversary, land of the free, home of the brave.&lt;br&gt;
To finalize this article I will refer to the citation from &lt;a href="https://en.wikipedia.org/wiki/Monty_Python%27s_The_Meaning_of_Life" rel="noopener noreferrer"&gt;Monty Python's The Meaning of Life&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Try and be nice to people, avoid eating fat, read a good book every now and then, get some walking in, and try and live together in peace and harmony with people of all creeds and nations.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>systemdesign</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Photos and videos processing in 2026 : more space in the cloud</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Tue, 09 Jun 2026 12:00:58 +0000</pubDate>
      <link>https://dev.to/hexfloor/photos-and-videos-processing-in-2026-more-space-in-the-cloud-2eb3</link>
      <guid>https://dev.to/hexfloor/photos-and-videos-processing-in-2026-more-space-in-the-cloud-2eb3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem : how to compress photos and videos to save storage.&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;TL;DR : Arch Linux + [Image Magick -&amp;gt; JPEG XL / HEIC] + [ffmpeg -&amp;gt; MP4 HEVC].&lt;/em&gt;&lt;br&gt;
I have a better solution than the &lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-31m5"&gt;previous one&lt;/a&gt;.&lt;br&gt;
Better means wider audience, more user oriented, faster to setup, less steps, intended to be enhanced by an AI-agent of your choice.&lt;br&gt;
Hereby I will list more the ideas and some critical steps, feel free to tailor to your needs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Yes, &lt;a href="https://dev.to/hexfloor/more-storage-for-media-organize-files-using-chatgpt-part4-processing-on-gentoo-3pho"&gt;Gentoo Setup&lt;/a&gt; is efficient, still I find it's a geek way. Gentoo is great for those who love Gentoo.&lt;br&gt;
Hence this time I will do the same with &lt;a href="https://archlinux.org/" rel="noopener noreferrer"&gt;Arch Linux&lt;/a&gt; to simplify the setup.&lt;br&gt;
Also I will convert images to &lt;code&gt;JPEG&lt;/code&gt; this time thanks to the fantastic progress done by &lt;a href="https://jpeg.org/jpegxl/" rel="noopener noreferrer"&gt;JPEG XL Team&lt;/a&gt;.&lt;br&gt;
For videos I will stick to the &lt;code&gt;MP4&lt;/code&gt; with &lt;a href="https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding" rel="noopener noreferrer"&gt;HEVC&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pacman &lt;span class="nt"&gt;-Syu&lt;/span&gt; &lt;span class="nt"&gt;--needed&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    ffmpeg &lt;span class="se"&gt;\&lt;/span&gt;
    imagemagick &lt;span class="se"&gt;\&lt;/span&gt;
    libheif &lt;span class="se"&gt;\&lt;/span&gt;
    libpng &lt;span class="se"&gt;\&lt;/span&gt;
    librsvg &lt;span class="se"&gt;\&lt;/span&gt;
    x264 &lt;span class="se"&gt;\&lt;/span&gt;
    x265 &lt;span class="se"&gt;\&lt;/span&gt;
    fdk-aac &lt;span class="se"&gt;\&lt;/span&gt;
    lame &lt;span class="se"&gt;\&lt;/span&gt;
    opus &lt;span class="se"&gt;\&lt;/span&gt;
    libvpx &lt;span class="se"&gt;\&lt;/span&gt;
    jq
&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 shell"&gt;&lt;code&gt;magick &lt;span class="nt"&gt;-list&lt;/span&gt; format | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; HEIC
ffmpeg &lt;span class="nt"&gt;-encoders&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'libx264|libx265|libvpx|libopus|libmp3lame|libfdk_aac'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel free to digest as is to an AI agent if any customisation needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data verification
&lt;/h2&gt;

&lt;p&gt;Okay, let's assume we have some set of files, let's see the types of files :&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then see if we have case-sensitive duplicates :&lt;br&gt;
&lt;/p&gt;

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

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

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

&lt;/div&gt;



&lt;p&gt;then convert all to the lower case :&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="c"&gt;# Find all files recursively&lt;/span&gt;
find ./ &lt;span class="nt"&gt;-type&lt;/span&gt; f | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Convert the filename to lowercase&lt;/span&gt;
    &lt;span class="nv"&gt;lower_base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

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

        &lt;span class="c"&gt;# Step 2: Rename to the final lowercase name (removing tmp_ prefix)&lt;/span&gt;
        &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;/tmp_&lt;/span&gt;&lt;span class="nv"&gt;$lower_base&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$lower_base&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also I would recommend to have simple filenames using digits, lower case letters and underscore only, dots in the middle might make the processing harder for &lt;code&gt;ffmpeg&lt;/code&gt;.&lt;br&gt;
For example : &lt;br&gt;
OK : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a_123.mp4&lt;/li&gt;
&lt;li&gt;a_123.mp4.metadata.json &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NOK :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a_123.b.mp4&lt;/li&gt;
&lt;li&gt;a_123.b.mp4.metadata.json &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Data preparation
&lt;/h2&gt;

&lt;p&gt;You may get inspired by the following file + metadata collection script :&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nv"&gt;INPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./input"&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./output"&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s2"&gt;"*.mov"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s2"&gt;"*.mp4"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; video_file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do

    &lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Copying video: &lt;/span&gt;&lt;span class="nv"&gt;$filename&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;

    &lt;span class="c"&gt;# Copy matching JSON sidecars&lt;/span&gt;
    find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;*.json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;

&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Done."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Image processing
&lt;/h2&gt;

&lt;p&gt;As I have mentioned, I will compress all to &lt;code&gt;JPEG&lt;/code&gt; this time as it may further be converted with no losses to &lt;a href="https://jpeg.org/jpegxl/" rel="noopener noreferrer"&gt;JPEG XL&lt;/a&gt;. You may use &lt;a href="https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format" rel="noopener noreferrer"&gt;HEIC&lt;/a&gt; as well.&lt;br&gt;
It's faster to update the metadata, compress and resize in one go :&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-Eeuo&lt;/span&gt; pipefail

&lt;span class="c"&gt;###############################################################################&lt;/span&gt;
&lt;span class="c"&gt;# Configuration&lt;/span&gt;
&lt;span class="c"&gt;###############################################################################&lt;/span&gt;

&lt;span class="nv"&gt;INPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./input"&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./output"&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_FIX_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./output_to_fix"&lt;/span&gt;

&lt;span class="nv"&gt;MAX_SIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1280
&lt;span class="nv"&gt;JPG_QUALITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;90

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_FIX_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;###############################################################################&lt;/span&gt;

find_json_sidecar&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;candidate &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;.&lt;span class="k"&gt;*&lt;/span&gt;.json&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$candidate&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;continue
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$candidate&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="k"&gt;done

    return &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;###############################################################################&lt;/span&gt;
&lt;span class="c"&gt;# Main&lt;/span&gt;
&lt;span class="c"&gt;###############################################################################&lt;/span&gt;

find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s2"&gt;"*.jpg"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s2"&gt;"*.jpeg"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s2"&gt;"*.heic"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="nt"&gt;-print0&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; img_file
&lt;span class="k"&gt;do

    &lt;/span&gt;&lt;span class="nb"&gt;echo
    echo&lt;/span&gt; &lt;span class="s2"&gt;"===================================================="&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Processing: &lt;/span&gt;&lt;span class="nv"&gt;$img_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;basename_noext&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nv"&gt;json_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nv"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nv"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;
    &lt;span class="c"&gt;# Read JSON sidecar&lt;/span&gt;
    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nv"&gt;json_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find_json_sidecar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then

        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"JSON: &lt;/span&gt;&lt;span class="nv"&gt;$json_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="nv"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'
            .photoTakenTime.timestamp //
            .creationTime.timestamp //
            empty
        '&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$json_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

        &lt;span class="nv"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'
            .description //
            empty
        '&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$json_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"JSON not found"&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;
    &lt;span class="c"&gt;# Fallback to existing EXIF&lt;/span&gt;
    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$timestamp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then

        &lt;/span&gt;&lt;span class="nv"&gt;exif_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;exiftool &lt;span class="nt"&gt;-s3&lt;/span&gt; &lt;span class="nt"&gt;-DateTimeOriginal&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$exif_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nv"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$exif_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; +%s 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Using EXIF timestamp"&lt;/span&gt;
        &lt;span class="k"&gt;fi
    fi&lt;/span&gt;

    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;
    &lt;span class="c"&gt;# No timestamp -&amp;gt; output_to_fix&lt;/span&gt;
    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$timestamp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then

        &lt;/span&gt;&lt;span class="nv"&gt;output_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_FIX_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;basename_noext&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jpg"&lt;/span&gt;

        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No timestamp available -&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        magick &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-auto-orient&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-quality&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JPG_QUALITY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;continue
    fi&lt;/span&gt;

    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;
    &lt;span class="c"&gt;# Build dates&lt;/span&gt;
    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;

    &lt;span class="nv"&gt;exif_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; @&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$timestamp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"+%Y:%m:%d %H:%M:%S"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nv"&gt;file_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; @&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$timestamp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"+%Y%m%d_%H%M%S"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then

        &lt;/span&gt;&lt;span class="nv"&gt;output_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_FIX_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;basename_noext&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jpg"&lt;/span&gt;

        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Invalid timestamp -&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        magick &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-auto-orient&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-quality&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JPG_QUALITY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;continue
    fi&lt;/span&gt;

    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;
    &lt;span class="c"&gt;# Output filename&lt;/span&gt;
    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;

    &lt;span class="nv"&gt;output_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file_date&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;basename_noext&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jpg"&lt;/span&gt;

    &lt;span class="nv"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nv"&gt;output_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file_date&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;basename_noext&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;counter&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jpg"&lt;/span&gt;
        &lt;span class="o"&gt;((&lt;/span&gt;counter++&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;

    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;
    &lt;span class="c"&gt;# Determine orientation and resize only if needed&lt;/span&gt;
    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;

    &lt;span class="nv"&gt;dimensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;identify &lt;span class="nt"&gt;-format&lt;/span&gt; &lt;span class="s2"&gt;"%wx%h"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;%x*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;#*x&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Dimensions: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; height &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; width &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;resize_arg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"x&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MAX_SIZE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nv"&gt;resize_arg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MAX_SIZE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;x&amp;gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;
    &lt;span class="c"&gt;# Convert to JPG + resize&lt;/span&gt;
    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;

    magick &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$img_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-auto-orient&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-resize&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$resize_arg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-quality&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JPG_QUALITY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;
    &lt;span class="c"&gt;# Restore metadata&lt;/span&gt;
    &lt;span class="c"&gt;###########################################################################&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$exif_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then

        &lt;/span&gt;exiftool &lt;span class="nt"&gt;-overwrite_original&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-DateTimeOriginal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$exif_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-CreateDate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$exif_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-ModifyDate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$exif_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
    &lt;span class="k"&gt;fi

    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"null"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then

        &lt;/span&gt;exiftool &lt;span class="nt"&gt;-overwrite_original&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-ImageDescription&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Created: &lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo
echo&lt;/span&gt; &lt;span class="s2"&gt;"===================================================="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Done."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point you have had all your images compressed to the size of around 300 kB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video processing
&lt;/h2&gt;

&lt;p&gt;Let's apply the same logic and proceed with the metadata update, conversion and rescaling in one go :&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nv"&gt;INPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./input"&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./output"&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_DIR_TO_FIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./mp4_to_fix"&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR_TO_FIX&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s2"&gt;"*.mp4"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s2"&gt;"*.mov"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; video_file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Processing: &lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;extension&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;##*.&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;extension_lower&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$extension&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt; &lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nv"&gt;base_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Look for Google Photos JSON sidecar&lt;/span&gt;
    &lt;span class="nv"&gt;json_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-iname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;*.json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nv"&gt;formatted_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nv"&gt;new_filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$json_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"JSON found: &lt;/span&gt;&lt;span class="nv"&gt;$json_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="nv"&gt;creation_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.photoTakenTime.timestamp'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$json_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$creation_time&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^[0-9]+&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nv"&gt;formatted_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; @&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$creation_time&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; +&lt;span class="s2"&gt;"%Y-%m-%d %H:%M:%S"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;fi
    fi

    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$formatted_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Missing or invalid timestamp."&lt;/span&gt;

        &lt;span class="nv"&gt;last_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_DIR_TO_FIX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;last_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;continue
    fi&lt;/span&gt;

    &lt;span class="c"&gt;# Get dimensions&lt;/span&gt;
    &lt;span class="nv"&gt;dimensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ffprobe &lt;span class="nt"&gt;-v&lt;/span&gt; error &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-select_streams&lt;/span&gt; v:0 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-show_entries&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;width,height &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-of&lt;/span&gt; &lt;span class="nv"&gt;csv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x:p&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dimensions&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'x'&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dimensions&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'x'&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Dimensions: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nv"&gt;scale_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 1280 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;scale_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"scale=-2:1280"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Scaling to -2:1280"&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 1280 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;scale_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"scale=1280:-2"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Scaling to 1280:-2"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No scaling required"&lt;/span&gt;
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nv"&gt;filename_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$formatted_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; +&lt;span class="s2"&gt;"%Y%m%d_%H%M%S"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nv"&gt;output_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename_date&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;base_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.mp4"&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Output: &lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$scale_filter&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;ffmpeg &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-vf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$scale_filter&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-r&lt;/span&gt; 30 &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-c&lt;/span&gt;:v libx265 &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-crf&lt;/span&gt; 28 &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-preset&lt;/span&gt; medium &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-c&lt;/span&gt;:a aac &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-b&lt;/span&gt;:a 192k &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-metadata&lt;/span&gt; &lt;span class="nv"&gt;creation_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$formatted_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;ffmpeg &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$video_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-r&lt;/span&gt; 30 &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-c&lt;/span&gt;:v libx265 &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-crf&lt;/span&gt; 28 &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-preset&lt;/span&gt; medium &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-c&lt;/span&gt;:a aac &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-b&lt;/span&gt;:a 192k &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-metadata&lt;/span&gt; &lt;span class="nv"&gt;creation_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$formatted_date&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi

done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Processing complete."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It may happen that your videos have some issue that would prevent &lt;code&gt;ffmpeg&lt;/code&gt; to process all in one batch correctly, hence a cleanup before a failover might be helpful :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;INPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./input"&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./output"&lt;/span&gt;

find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; output_file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do

    &lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="nv"&gt;search_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;:16&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;search_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;search_name&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Looking for: &lt;/span&gt;&lt;span class="nv"&gt;$search_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;search_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;*"&lt;/span&gt; &lt;span class="nt"&gt;-print&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt;

&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may combine both in one monstrous script, I have listed them separately to simplify the understanding of the idea.&lt;br&gt;
At this point you have all your videos processed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Data export
&lt;/h2&gt;

&lt;p&gt;Now let's adjust the names of the files for housekeeping :&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="c"&gt;# Set the input and output directories&lt;/span&gt;
&lt;span class="nv"&gt;input_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./input"&lt;/span&gt;
&lt;span class="nv"&gt;output_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./output"&lt;/span&gt;

&lt;span class="c"&gt;# Make sure the output directory exists, create it if it doesn't&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Counter variable, starting at 1&lt;/span&gt;
&lt;span class="nv"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

&lt;span class="c"&gt;# Loop through sorted .jpg files, handling files with spaces correctly&lt;/span&gt;
find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$input_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.jpg"&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c"&gt;# Extract the first part of the filename (before the first three underscores)&lt;/span&gt;
    &lt;span class="nv"&gt;base_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$base_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'_'&lt;/span&gt; &lt;span class="nt"&gt;-f1-2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# Extract "20000101_000000" part&lt;/span&gt;

    &lt;span class="c"&gt;# Build the new filename with a 4-digit counter and "_jpg_" prefix&lt;/span&gt;
    &lt;span class="nv"&gt;new_filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%s_jpg_%04d.jpg"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$counter&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Copy the file to the output directory with the new filename&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output_dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$new_filename&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Increment the counter&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;counter++&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Files renamed and copied to &lt;/span&gt;&lt;span class="nv"&gt;$output_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

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

&lt;/div&gt;



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

&lt;p&gt;Now all your images and videos are ready to be exported.&lt;br&gt;
And you have done an eco-friendly action, given that El Niño is on the rise and this year is expected to be record hot.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>productivity</category>
      <category>cloud</category>
      <category>ai</category>
    </item>
    <item>
      <title>Workspace 2026 : May the Fourth Be With You — The Rise of Ephemeral Dev Environments</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Mon, 04 May 2026 17:40:18 +0000</pubDate>
      <link>https://dev.to/hexfloor/workspace-2026-may-the-fourth-be-with-you-the-rise-of-ephemeral-dev-environments-153m</link>
      <guid>https://dev.to/hexfloor/workspace-2026-may-the-fourth-be-with-you-the-rise-of-ephemeral-dev-environments-153m</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Let's have some fun and ask &lt;em&gt;ChatGPT&lt;/em&gt; the following question : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;From the perspective of software system architecture in 2026, what would the Jedi and Sith approaches respectively be toward AI, and what would the digital workspace of each faction look like? Provide a short, concise summary.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;strong&gt;Jedi (architecture mindset)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI philosophy:&lt;/strong&gt; Ethical, human-in-the-loop, explainable, bias-aware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systems design:&lt;/strong&gt; Decentralized/federated, privacy-first, resilient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role of AI:&lt;/strong&gt; Advisor that augments judgment, never overrides it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace:&lt;/strong&gt; Minimal, secure, interoperable tools; open ecosystems; curated knowledge hubs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sith (architecture mindset)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI philosophy:&lt;/strong&gt; Outcome-maximizing, unconstrained, opaque.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systems design:&lt;/strong&gt; Centralized, data-maximalist, surveillance-heavy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role of AI:&lt;/strong&gt; Autonomous decision-maker driving control and efficiency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace:&lt;/strong&gt; Closed, proprietary platforms; real-time command dashboards; heavy automation and monitoring.&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;I’m building a cloud-based digital container workspace. Key ideas include stateless containers enhanced with a Visual Studio Code Server, necessary mainstream runtimes, and a mounted persistent network filesystem. The everyday workflow involves deploying and starting a container via a cloud console, connecting to the dev workspace through a browser using SSO, attaching desired AI agents, and getting work done. Please suggest a suitable solution architecture.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;An &lt;strong&gt;ephemeral dev environment&lt;/strong&gt; is a short-lived, on-demand workspace: each session spins up a clean containerized environment, connects it to persistent storage, and shuts down when idle—ensuring reproducibility, isolation, and cost efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Simplified Architecture Flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User (Browser + SSO)
        ↓
Control Panel (Web Console + API + Auth)
        ↓
Workspace Orchestrator
        ↓
Kubernetes Cluster (Ephemeral Dev Environment Pods)
        ↓
Container (VS Code Server + runtimes + tools)
        ↓
Mounted Persistent Storage (Network File System)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔌 Extended View (with access + AI agents)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Browser
   ↓ (SSO via OIDC)
Auth Provider (e.g., Auth0)
   ↓
Control Panel (UI + API)
   ↓
Orchestrator
   ↓
Kubernetes
   ↓
Ephemeral Workspace Pod
   → VS Code Server (code-server)
   → Dev Runtimes (Node, Python, etc.)
   ↓
Persistent Volume (e.g., Amazon EFS)
   ↓
Ingress / Proxy (e.g., NGINX)
   ↓
Browser IDE Access

Parallel:
Workspace ↔ AI Agent Gateway ↔ OpenAI APIs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧠 One-line takeaway
&lt;/h2&gt;

&lt;p&gt;Stateless containers + persistent storage + browser-based IDE + orchestration layer = scalable, secure &lt;strong&gt;ephemeral dev environments&lt;/strong&gt;.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;How is the ephemeral dev environment vision aligned with the Jedi and Sith views of architecture? Provide percentages and concise feedback.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Fun framing—but it actually maps surprisingly well.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚖️ Alignment with your &lt;strong&gt;ephemeral dev environment&lt;/strong&gt; vision
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🟦 Jedi-style architecture (≈ &lt;strong&gt;80–90% match&lt;/strong&gt;)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Prefers &lt;strong&gt;ephemeral, stateless, reproducible systems&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Strong on &lt;strong&gt;governance, auditability, least privilege&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;AI used as &lt;strong&gt;assistive, transparent agents&lt;/strong&gt; (human-in-the-loop)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dev environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean-slate workspaces (very aligned with ephemeral containers)&lt;/li&gt;
&lt;li&gt;Standardized templates, policy enforcement&lt;/li&gt;
&lt;li&gt;Shared knowledge, low drift&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Your model (stateless containers + persistent storage + controlled AI access) fits almost perfectly here.&lt;/p&gt;




&lt;h3&gt;
  
  
  🟥 Sith-style architecture (≈ &lt;strong&gt;40–55% match&lt;/strong&gt;)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Prefers &lt;strong&gt;persistent, stateful, power-concentrated systems&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;AI used as &lt;strong&gt;autonomous, outcome-driven agents&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Less concern for isolation; more for &lt;strong&gt;speed and control&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dev environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long-lived, heavily customized “snowflake” workspaces&lt;/li&gt;
&lt;li&gt;Direct system access, fewer guardrails&lt;/li&gt;
&lt;li&gt;Optimized for individual dominance, not reproducibility&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Your ephemeral approach conflicts with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Their preference for persistence&lt;/li&gt;
&lt;li&gt;Their tolerance for environment drift&lt;/li&gt;
&lt;li&gt;Their desire for tightly coupled, powerful systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Bottom line
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jedi ≈ 85% fit&lt;/strong&gt; → Your architecture is almost a canonical Jedi system: clean, controlled, scalable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sith ≈ 50% fit&lt;/strong&gt; → They’d push it toward persistent, personalized, less-governed environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you wanted to “Sith-optimize” your design, you’d add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Workspace persistence by default&lt;/li&gt;
&lt;li&gt;Fewer lifecycle resets&lt;/li&gt;
&lt;li&gt;More autonomous AI agents with broader permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right now, your vision is clearly &lt;strong&gt;Jedi-leaning with modern cloud-native discipline&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;In balance, the Force endures. &lt;br&gt;
May the Force be with us all.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>architecture</category>
      <category>cloud</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Workspace 2026 : one USB-C to charge them all</title>
      <dc:creator>hexfloor</dc:creator>
      <pubDate>Sun, 03 May 2026 18:23:41 +0000</pubDate>
      <link>https://dev.to/hexfloor/workspace-2026-one-usb-c-to-charge-them-all-36of</link>
      <guid>https://dev.to/hexfloor/workspace-2026-one-usb-c-to-charge-them-all-36of</guid>
      <description>&lt;h2&gt;
  
  
  Introduction in 2026 ?
&lt;/h2&gt;

&lt;p&gt;More inspiration - less explanation. AI - that is why.&lt;br&gt;
What will be the workspace of the distant future - who knows.&lt;br&gt;
What will be the workspace in few years of now - probably a device resulting from a convergence of a mobile phone and a laptop, all the dev work done in the cloud only and this new device is just a thin client.&lt;br&gt;
This has become possible also thanks to &lt;a href="https://en.wikipedia.org/wiki/Radio_Equipment_Directive_(2022)" rel="noopener noreferrer"&gt;the rise of the USB-C&lt;/a&gt;&lt;br&gt;
The fact that the future has come is manifested by the arrival of &lt;a href="https://en.wikipedia.org/wiki/MacBook_Neo" rel="noopener noreferrer"&gt;MacBook Neo&lt;/a&gt; - The Chosen One. This device is just incredible, and yes, it does use &lt;em&gt;Apple A18 Pro SOC&lt;/em&gt;, the same One you may find in &lt;em&gt;iPhone 16&lt;/em&gt;.&lt;br&gt;
Convergence is coming.&lt;br&gt;
Hence it's vital to get a good charger to be ready for the future - whatever will it be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chargers : the Good, the Bad and the Ugly.
&lt;/h2&gt;

&lt;p&gt;Choosing a good charger is the same vibe as picking a ripe watermelon - the good charger must be heavy.&lt;/p&gt;

&lt;p&gt;What are the core features of a good charger : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;voltage regulation (stable output)&lt;/li&gt;
&lt;li&gt;low internal resistance (doesn't heat much)&lt;/li&gt;
&lt;li&gt;USB Power Delivery protocol support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How is it represented in terms of physical hardware : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;large transformer&lt;/li&gt;
&lt;li&gt;capacitators for energy smoothing&lt;/li&gt;
&lt;li&gt;inductors to resist rapid current changes&lt;/li&gt;
&lt;li&gt;thicker circuit board for heat management&lt;/li&gt;
&lt;li&gt;better isolation for safety&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My best advice - find a disassembly video on &lt;em&gt;YouTube&lt;/em&gt; of any charger you wish to buy and you will see that a good charger has more components inside and they are more massive and thus a good charger feels heavier - a very efficient and simple test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Charger : which power output ?
&lt;/h2&gt;

&lt;p&gt;Roughly : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;65W for laptops, tablets, smartphones&lt;/li&gt;
&lt;li&gt;30W for tablets, smartphones&lt;/li&gt;
&lt;li&gt;20W for smartphones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would recommend to have at least one decent 65W charger that would cover any needs.&lt;br&gt;
It is important that charger does support &lt;a href="https://en.wikipedia.org/wiki/USB_hardware#USB_Power_Delivery" rel="noopener noreferrer"&gt;USB Power Delivery&lt;/a&gt; and that charger cable is of decent quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Charger : shortlist
&lt;/h2&gt;

&lt;p&gt;My selection for the 65W charger is the following : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Apple Nimble Wally Stretch 65W charger&lt;/em&gt; + &lt;em&gt;Apple 60W charging cable&lt;/em&gt; = prime engineering worth every penny&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Ikea SJÖSS 65W charger&lt;/em&gt; + &lt;em&gt;Ikea SITTBRUNN 60W charging cable&lt;/em&gt; = decent quality and good safety for money&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to explore other chargers and add comments to the article.&lt;/p&gt;

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

&lt;p&gt;Good charger means fast and consistent charging, device battery health and end-user safety - on the long run it's as important as choosing a device to build your digital workplace.&lt;br&gt;
Long live USB-C!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>workplace</category>
      <category>architecture</category>
      <category>cloud</category>
    </item>
    <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=" " width="800" height="47"&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=" " width="800" height="214"&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=" " 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=" " width="800" height="391"&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=" " width="800" height="275"&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=" " width="799" 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=" " 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=" " 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=" " width="800" height="201"&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=" " width="800" height="118"&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=" " 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=" " width="799" 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=" " width="800" height="157"&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=" " width="799" 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=" " 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=" " width="800" height="56"&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=" " width="799" 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=" " width="799" 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=" " width="800" height="372"&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=" " width="800" height="473"&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=" " width="799" 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=" " width="800" height="323"&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=" " width="799" 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=" " width="800" height="104"&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=" " 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=" " 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=" " width="800" height="374"&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=" " 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=" " width="799" 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=" " 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=" " width="800" height="372"&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=" " 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=" " 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=" " width="800" height="674"&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=" " 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=" " width="800" height="50"&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=" " 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=" " 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=" " 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=" " 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=" " width="799" 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>
  </channel>
</rss>
