Answers.Body: <p>I found a Python script that does the job: <a href="https://github.com/im-PJs/GoPro-File-Organizer/tree/main" rel="nofollow noreferrer">https://github.com/im-PJs/GoPro-File-Organizer/tree/main</a>:</p>
  <pre><code># Author: https://github.com/im-PJs
  import os
  import shutil
  import sys
  from collections import defaultdict
  
  def organize_files(base_path):
      # Count occurrences of each file number for .MP4 files
      mp4_count = defaultdict(int)
      file_paths = defaultdict(list)
  
      # First pass to collect file information
      for filename in os.listdir(base_path):
          if filename.endswith(('.MP4', '.LRV', '.THM')):
              file_number = filename[4:8]
              file_paths[file_number].append(filename)
              if filename.endswith('.MP4'):
                  mp4_count[file_number] += 1
      
      # Save original paths before moving
      with open(os.path.join(base_path, 'original_paths.txt'), 'w') as f:
          # Second pass to move files if there are multiple MP4s with the same number
          for file_number, files in file_paths.items():
              if mp4_count[file_number] &gt; 1:  # Create folder only if more than one MP4 file with the same number
                  folder_name = f&quot;Video_{file_number}&quot;
                  folder_path = os.path.join(base_path, folder_name)
                  if not os.path.exists(folder_path):
                      os.makedirs(folder_path)
                  
                  for filename in files:
                      original_path = os.path.join(base_path, filename)
                      new_path = os.path.join(folder_path, filename)
                      shutil.move(original_path, new_path)
                      print(f&quot;Moved '{filename}' to '{new_path}'&quot;)
                      f.write(f&quot;{new_path}={original_path}\n&quot;)
  
  def undo_organization(base_path):
      paths_file = os.path.join(base_path, 'original_paths.txt')
      
      if not os.path.exists(paths_file):
          print(&quot;No organization to undo.&quot;)
          return
      
      # Read the original paths and move files back
      with open(paths_file, 'r') as file:
          for line in file:
              new_path, original_path = line.strip().split('=')
              shutil.move(new_path, original_path)
              print(f&quot;Moved '{new_path}' back to '{original_path}'&quot;)
      
      # Remove empty folders
      for folder in os.listdir(base_path):
          folder_path = os.path.join(base_path, folder)
          if os.path.isdir(folder_path) and not os.listdir(folder_path):
              os.rmdir(folder_path)
              print(f&quot;Removed empty folder: {folder_path}&quot;)
  
      os.remove(paths_file)
      print(&quot;Undo complete.&quot;)
  
  if __name__ == &quot;__main__&quot;:
      # Use the current directory as the base directory
      base_directory = os.getcwd()
      if len(sys.argv) &gt; 1 and sys.argv[1] == '-undoLast':
          undo_organization(base_directory)
      else:
          organize_files(base_directory)    
  </code></pre>
  <p>Tested on Windows 10 21H2 Pro with Python 3.11.9, running <code>python main.py</code>.</p>
  <p>Before:</p>
  <pre><code>(test) N:\GoPro\test&gt;tree /F
  Folder PATH listing for volume 2024GoPro
  Volume serial number is 0746-89B1
  N:.
      main.py
      GL010356.LRV
      GL020356.LRV
      GL030356.LRV
      GX010356.MP4
      GX010356.THM
      GX020356.MP4
      GX020356.THM
      GX030356.MP4
      GX030356.THM
      GL010355.LRV
      GL020355.LRV
      GL030355.LRV
      GL040355.LRV
      GX010355.MP4
      GX010355.THM
      GX020355.MP4
      GX020355.THM
      GX030355.MP4
      GX030355.THM
      GX040355.MP4
      GX040355.THM
      GL010357.LRV
      GL020357.LRV
      GL030357.LRV
      GL040357.LRV
      GL050357.LRV
      GX010357.MP4
      GX010357.THM
      GX020357.MP4
      GX020357.THM
      GX030357.MP4
      GX030357.THM
      GX040357.MP4
      GX040357.THM
      GX050357.MP4
      GX050357.THM
  
  No subfolders exist
  </code></pre>
  <p>After:</p>
  <pre><code>N:.
  │   main.py
  │   original_paths.txt
  │
  ├───Video_0356
  │       GL010356.LRV
  │       GL020356.LRV
  │       GL030356.LRV
  │       GX010356.MP4
  │       GX010356.THM
  │       GX020356.MP4
  │       GX020356.THM
  │       GX030356.MP4
  │       GX030356.THM
  │
  ├───Video_0355
  │       GL010355.LRV
  │       GL020355.LRV
  │       GL030355.LRV
  │       GL040355.LRV
  │       GX010355.MP4
  │       GX010355.THM
  │       GX020355.MP4
  │       GX020355.THM
  │       GX030355.MP4
  │       GX030355.THM
  │       GX040355.MP4
  │       GX040355.THM
  │
  └───Video_0357
          GL010357.LRV
          GL020357.LRV
          GL030357.LRV
          GL040357.LRV
          GL050357.LRV
          GX010357.MP4
          GX010357.THM
          GX020357.MP4
          GX020357.THM
          GX030357.MP4
          GX030357.THM
          GX040357.MP4
          GX040357.THM
          GX050357.MP4
          GX050357.THM
  </code></pre>
  <p>To undo the file move: <code>python main.py -undoLast</code></p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2024-11-03T07:07:49.057Z
Answers.LastActivityDate: 2024-11-03T07:07:49.057Z
Answers.UserDisplayName: Franck Dernoncourt
Body: <p>By default, GoPro recordings are saved in one folder, and recordings are cut into several files as shown below:</p>
  <p>Recording ID 356:</p>
  <ul>
  <li>GX010356.MP4</li>
  <li>GX020356.MP4</li>
  <li>GX030356.MP4</li>
  <li>GX040356.MP4</li>
  </ul>
  <p>Recording ID 357:</p>
  <ul>
  <li>GX010357.MP4</li>
  <li>GX020357.MP4</li>
  <li>GX030357.MP4</li>
  </ul>
  <p>That stupid naming convention makes it hard to select all files of the same recording, since sorting files <a href="https://i.sstatic.net/ZktCwkmS.png" rel="nofollow noreferrer">doesn't help</a>.</p>
  <p>How to group GoPro videos by folder?</p>
  <p>E.g., I want all the files of Recording ID 356:</p>
  <ul>
  <li>GX010356.MP4</li>
  <li>GX020356.MP4</li>
  <li>GX030356.MP4</li>
  <li>GX040356.MP4</li>
  </ul>
  <p>in folder <code>356</code>, and all the files of Recording ID 357:</p>
  <ul>
  <li>GX010357.MP4</li>
  <li>GX020357.MP4</li>
  <li>GX030357.MP4</li>
  </ul>
  <p>in folder <code>357</code>.</p>
  
Tags: <gopro>
Title: How to group GoPro recordings by folder?
UserDisplayName: Franck Dernoncourt
ThreadText: # How to group GoPro recordings by folder?
  
  (Asked by: username_0 on 2024-11-03)
  
  By default, GoPro recordings are saved in one folder, and recordings are cut into several files as shown below:
  
  Recording ID 356:
  
  * GX010356.MP4
  * GX020356.MP4
  * GX030356.MP4
  * GX040356.MP4
  
  Recording ID 357:
  
  * GX010357.MP4
  * GX020357.MP4
  * GX030357.MP4
  
  That stupid naming convention makes it hard to select all files of the same recording, since sorting files [doesn't help](https://i.sstatic.net/ZktCwkmS.png).
  
  How to group GoPro videos by folder?
  
  E.g., I want all the files of Recording ID 356:
  
  * GX010356.MP4
  * GX020356.MP4
  * GX030356.MP4
  * GX040356.MP4
  
  in folder `356`, and all the files of Recording ID 357:
  
  * GX010357.MP4
  * GX020357.MP4
  * GX030357.MP4
  
  in folder `357`.
  
  **Tags:** gopro
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_0 on 2024-11-03. Score: 0
  
  I found a Python script that does the job: <https://github.com/im-PJs/GoPro-File-Organizer/tree/main>:
  
  ```
  # Author: https://github.com/im-PJs
  import os
  import shutil
  import sys
  from collections import defaultdict
  
  def organize_files(base_path):
      # Count occurrences of each file number for .MP4 files
      mp4_count = defaultdict(int)
      file_paths = defaultdict(list)
  
      # First pass to collect file information
      for filename in os.listdir(base_path):
          if filename.endswith(('.MP4', '.LRV', '.THM')):
              file_number = filename[4:8]
              file_paths[file_number].append(filename)
              if filename.endswith('.MP4'):
                  mp4_count[file_number] += 1
      
      # Save original paths before moving
      with open(os.path.join(base_path, 'original_paths.txt'), 'w') as f:
          # Second pass to move files if there are multiple MP4s with the same number
          for file_number, files in file_paths.items():
              if mp4_count[file_number] > 1:  # Create folder only if more than one MP4 file with the same number
                  folder_name = f"Video_{file_number}"
                  folder_path = os.path.join(base_path, folder_name)
                  if not os.path.exists(folder_path):
                      os.makedirs(folder_path)
                  
                  for filename in files:
                      original_path = os.path.join(base_path, filename)
                      new_path = os.path.join(folder_path, filename)
                      shutil.move(original_path, new_path)
                      print(f"Moved '{filename}' to '{new_path}'")
                      f.write(f"{new_path}={original_path}\n")
  
  def undo_organization(base_path):
      paths_file = os.path.join(base_path, 'original_paths.txt')
      
      if not os.path.exists(paths_file):
          print("No organization to undo.")
          return
      
      # Read the original paths and move files back
      with open(paths_file, 'r') as file:
          for line in file:
              new_path, original_path = line.strip().split('=')
              shutil.move(new_path, original_path)
              print(f"Moved '{new_path}' back to '{original_path}'")
      
      # Remove empty folders
      for folder in os.listdir(base_path):
          folder_path = os.path.join(base_path, folder)
          if os.path.isdir(folder_path) and not os.listdir(folder_path):
              os.rmdir(folder_path)
              print(f"Removed empty folder: {folder_path}")
  
      os.remove(paths_file)
      print("Undo complete.")
  
  if __name__ == "__main__":
      # Use the current directory as the base directory
      base_directory = os.getcwd()
      if len(sys.argv) > 1 and sys.argv[1] == '-undoLast':
          undo_organization(base_directory)
      else:
          organize_files(base_directory)    
  
  ```
  
  Tested on Windows 10 21H2 Pro with Python 3.11.9, running `python main.py`.
  
  Before:
  
  ```
  (test) N:\GoPro\test>tree /F
  Folder PATH listing for volume 2024GoPro
  Volume serial number is 0746-89B1
  N:.
      main.py
      GL010356.LRV
      GL020356.LRV
      GL030356.LRV
      GX010356.MP4
      GX010356.THM
      GX020356.MP4
      GX020356.THM
      GX030356.MP4
      GX030356.THM
      GL010355.LRV
      GL020355.LRV
      GL030355.LRV
      GL040355.LRV
      GX010355.MP4
      GX010355.THM
      GX020355.MP4
      GX020355.THM
      GX030355.MP4
      GX030355.THM
      GX040355.MP4
      GX040355.THM
      GL010357.LRV
      GL020357.LRV
      GL030357.LRV
      GL040357.LRV
      GL050357.LRV
      GX010357.MP4
      GX010357.THM
      GX020357.MP4
      GX020357.THM
      GX030357.MP4
      GX030357.THM
      GX040357.MP4
      GX040357.THM
      GX050357.MP4
      GX050357.THM
  
  No subfolders exist
  
  ```
  
  After:
  
  ```
  N:.
  │   main.py
  │   original_paths.txt
  │
  ├───Video_0356
  │       GL010356.LRV
  │       GL020356.LRV
  │       GL030356.LRV
  │       GX010356.MP4
  │       GX010356.THM
  │       GX020356.MP4
  │       GX020356.THM
  │       GX030356.MP4
  │       GX030356.THM
  │
  ├───Video_0355
  │       GL010355.LRV
  │       GL020355.LRV
  │       GL030355.LRV
  │       GL040355.LRV
  │       GX010355.MP4
  │       GX010355.THM
  │       GX020355.MP4
  │       GX020355.THM
  │       GX030355.MP4
  │       GX030355.THM
  │       GX040355.MP4
  │       GX040355.THM
  │
  └───Video_0357
          GL010357.LRV
          GL020357.LRV
          GL030357.LRV
          GL040357.LRV
          GL050357.LRV
          GX010357.MP4
          GX010357.THM
          GX020357.MP4
          GX020357.THM
          GX030357.MP4
          GX030357.THM
          GX040357.MP4
          GX040357.THM
          GX050357.MP4
          GX050357.THM
  
  ```
  
  To undo the file move: `python main.py -undoLast`
  
  
  ---
  
  