<?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: Nibbo</title>
    <description>The latest articles on DEV Community by Nibbo (@nibbo).</description>
    <link>https://dev.to/nibbo</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%2F4214806%2Fdda915ba-bc1c-40b8-bf13-d1cab8253631.png</url>
      <title>DEV Community: Nibbo</title>
      <link>https://dev.to/nibbo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nibbo"/>
    <language>en</language>
    <item>
      <title>Batch-Renaming Users' Files With No Backend: File System Access API, the Firefox/Safari Fallback, and Why I Ship a ZIP Anyway</title>
      <dc:creator>Nibbo</dc:creator>
      <pubDate>Mon, 02 Nov 2026 08:10:18 +0000</pubDate>
      <link>https://dev.to/nibbo/batch-renaming-users-files-with-no-backend-file-system-access-api-the-firefoxsafari-fallback-4a41</link>
      <guid>https://dev.to/nibbo/batch-renaming-users-files-with-no-backend-file-system-access-api-the-firefoxsafari-fallback-4a41</guid>
      <description>&lt;p&gt;A small feature request: "I have 80 invoice PDFs called &lt;code&gt;invoice (12).pdf&lt;/code&gt;. Can your tool rename them to &lt;code&gt;2026-10-15_Acme_550.00.pdf&lt;/code&gt; based on what's inside?" Reading the PDF and extracting the date, vendor and amount is the fun part. Actually &lt;em&gt;renaming the files on the user's disk&lt;/em&gt; turned out to be the tricky part. Here are the three approaches I tried.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: File System Access API
&lt;/h2&gt;

&lt;p&gt;Chromium browsers let you ask for a directory handle and write to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showDirectoryPicker&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readwrite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFile&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;buildName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// in-place rename&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When it works, it's magical: files are renamed in place, no downloads. The catches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Availability.&lt;/strong&gt; &lt;code&gt;showDirectoryPicker&lt;/code&gt; is Chromium-only. Firefox and Safari don't ship it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;move()&lt;/code&gt; support&lt;/strong&gt; for user-picked files arrived later than the rest of the API. Feature-detect it; the fallback is "write a new file with &lt;code&gt;getFileHandle(newName, { create: true })&lt;/code&gt;, copy the bytes, then &lt;code&gt;removeEntry(oldName)&lt;/code&gt;", which is slower and not atomic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permission prompts.&lt;/strong&gt; The user gets a scary "let site edit files?" dialog. Explain &lt;em&gt;before&lt;/em&gt; the prompt why you need it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collisions.&lt;/strong&gt; Two invoices from the same vendor on the same day with the same amount happen more than you'd think. Check for existing names and append &lt;code&gt;-2&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Option 2: download each renamed file
&lt;/h2&gt;

&lt;p&gt;The universal fallback is to create an object URL per file and click a hidden &lt;code&gt;&amp;lt;a download="newName"&amp;gt;&lt;/code&gt;. It works everywhere, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;browsers throttle or block multiple automatic downloads after the first few;&lt;/li&gt;
&lt;li&gt;the user ends up with 80 &lt;em&gt;copies&lt;/em&gt; in Downloads, and the originals untouched;&lt;/li&gt;
&lt;li&gt;on mobile it's unusable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I keep it only for one or two files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 3: one ZIP with the renamed files
&lt;/h2&gt;

&lt;p&gt;For batches, I settled on generating a ZIP in the browser (a streaming zip writer, &lt;code&gt;STORE&lt;/code&gt; mode since PDFs are already compressed) and offering a single download. Renaming is just choosing the entry name. Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;works in every browser, including iOS Safari;&lt;/li&gt;
&lt;li&gt;one download prompt;&lt;/li&gt;
&lt;li&gt;the user can review the result before replacing anything;&lt;/li&gt;
&lt;li&gt;I can add a &lt;code&gt;rename-log.csv&lt;/code&gt; mapping old names to new names, which accountants love.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Memory-wise, stream entries from &lt;code&gt;file.stream()&lt;/code&gt; into the zip writer instead of reading all files into memory first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I ship
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chromium desktop: offer "rename in folder" (File System Access) &lt;strong&gt;and&lt;/strong&gt; ZIP.&lt;/li&gt;
&lt;li&gt;Everything else: ZIP only, no feature-gap apology dialog, just the button that works.&lt;/li&gt;
&lt;li&gt;Always: a preview table of old â†’ new names, editable, before anything is written.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The preview table was the single most appreciated part. Extraction is never 100% right, and letting people fix one cell is better than any heuristic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nibbo.org/en/invoice-renamer/" rel="noopener noreferrer"&gt;Nibbo's invoice renamer&lt;/a&gt; takes the ZIP route: it reads the PDFs, fills an editable table of new names, and gives you the renamed files as a ZIP, with the PDFs never leaving the machine. That matters a lot when the files are other companies' invoices.&lt;/p&gt;

&lt;p&gt;â€” Vitalii&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>browser</category>
    </item>
    <item>
      <title>Your Upload Preview Shows a Black Box: Handling 3GP, WMV, AVI and MTS Files That the Video Element Can't Play</title>
      <dc:creator>Nibbo</dc:creator>
      <pubDate>Sat, 31 Oct 2026 08:08:06 +0000</pubDate>
      <link>https://dev.to/nibbo/your-upload-preview-shows-a-black-box-handling-3gp-wmv-avi-and-mts-files-that-the-video-element-4fl1</link>
      <guid>https://dev.to/nibbo/your-upload-preview-shows-a-black-box-handling-3gp-wmv-avi-and-mts-files-that-the-video-element-4fl1</guid>
      <description>&lt;p&gt;You build an upload form, add a nice preview with &lt;code&gt;URL.createObjectURL(file)&lt;/code&gt; in a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag, and test it with an MP4 from your phone. Works great. Then support tickets arrive: "the preview is black", "it says the file is broken", "the upload button never activates".&lt;/p&gt;

&lt;p&gt;The file usually isn't broken. It is in a container or codec the browser simply does not decode. Here's how I deal with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What browsers actually play
&lt;/h2&gt;

&lt;p&gt;Roughly, across current Chrome, Firefox and Safari:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Container&lt;/th&gt;
&lt;th&gt;Typical codecs&lt;/th&gt;
&lt;th&gt;Plays in &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MP4&lt;/td&gt;
&lt;td&gt;H.264 + AAC&lt;/td&gt;
&lt;td&gt;Yes, everywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebM&lt;/td&gt;
&lt;td&gt;VP8/VP9/AV1 + Opus&lt;/td&gt;
&lt;td&gt;Yes (Safari: recent versions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MOV&lt;/td&gt;
&lt;td&gt;H.264/HEVC&lt;/td&gt;
&lt;td&gt;Often, HEVC depends on OS/hardware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MKV&lt;/td&gt;
&lt;td&gt;anything&lt;/td&gt;
&lt;td&gt;Chrome sometimes, Safari no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AVI, WMV, 3GP, MTS/M2TS, FLV&lt;/td&gt;
&lt;td&gt;DivX, WMV9, H.263, AMR, AC-3…&lt;/td&gt;
&lt;td&gt;Usually &lt;strong&gt;no&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Old phones (3GP with AMR audio), Windows Movie Maker exports (WMV), camcorders (MTS/AVCHD) and screen recorders from 2010 (AVI) are exactly what non-technical users find in their archives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: detect failure honestly
&lt;/h2&gt;

&lt;p&gt;Don't rely on the file extension or &lt;code&gt;file.type&lt;/code&gt; (it is often empty for MTS). Try to load it and listen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;canPreview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;muted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metadata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onloadeddata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;videoWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;videoWidth &amp;gt; 0&lt;/code&gt; check: some browsers load audio from a file whose video codec they can't decode, giving you a black frame with sound.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: decide where to convert
&lt;/h2&gt;

&lt;p&gt;Options, from cheapest to most robust:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Accept and convert server-side&lt;/strong&gt; with ffmpeg. Most reliable, but you pay for compute and you now store users' family videos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convert client-side&lt;/strong&gt; with ffmpeg.wasm. No server cost and better privacy, but slow on phones and memory-limited for files over ~2 GB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask the user to convert first&lt;/strong&gt;, with a clear message and a link to a tool.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For many products, option 3 is the pragmatic first step. A precise message beats a black rectangle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This video is in WMV format, which browsers can't preview. Convert it to MP4 first, then upload it again."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3: write the message per format
&lt;/h2&gt;

&lt;p&gt;Generic "unsupported format" errors make users think their file is damaged. Name the format and the fix. For the conversion link I point people to &lt;a href="https://nibbo.org/en/video-converter/" rel="noopener noreferrer"&gt;Nibbo's video converter&lt;/a&gt;, which turns AVI, WMV, 3GP, MTS and friends into MP4 in the browser without uploading the file anywhere, so I'm not sending users' videos to yet another third party.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: still accept the original if you can
&lt;/h2&gt;

&lt;p&gt;If your backend transcodes anyway, let the upload proceed without a preview and show a thumbnail placeholder instead. Blocking the upload just because the preview failed is the worst outcome.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Probe playability with a real &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element, check &lt;code&gt;videoWidth&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Never trust &lt;code&gt;file.type&lt;/code&gt; for legacy containers.&lt;/li&gt;
&lt;li&gt;Name the format in the error and give a concrete fix.&lt;/li&gt;
&lt;li&gt;Don't block the upload on preview failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;— Vitalii&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>video</category>
    </item>
  </channel>
</rss>
