Skip to content

360mpgui V1.5.0.0 • Ad-Free

| Module | Functionality | |--------|----------------| | File Browser | Browse folders, show thumbnails, list all images/videos | | Metadata Viewer | Shows dimensions, size, type, FPS for videos | | 360° Panorama Viewer | Drag to rotate yaw/pitch, real-time equirectangular preview | | Cubemap Converter | Convert selected images to 6-face cubemap (right/left/up/down/front/back) | | Video Frame Extractor | Extract frames from 360° video at custom intervals | | Persistent Settings | Remembers last opened folder | | Dark UI | Professional dark theme with splitter panels |


Under the hood, 360mpgui relies on GPAC’s MP4Box. Version 1.5.0.0 ships with a more recent build of MP4Box, bringing:

class MainWindow(QMainWindow): def init(self): super().init() self.setWindowTitle("360mpgui v1.5.0.0 - 360° Media Manager") self.setGeometry(100, 100, 1300, 800) self.setStyleSheet(""" QMainWindow background-color: #2d2d2d; QLabel, QListWidget, QTextEdit color: #f0f0f0; QPushButton background-color: #3c3c3c; color: white; border: none; padding: 5px; border-radius: 3px; QPushButton:hover background-color: #505050; QListWidget::item:selected background-color: #0078d7; QTabWidget::pane border: 1px solid #3c3c3c; background: #252526; QTabBar::tab background: #2d2d2d; color: #ccc; padding: 6px; QTabBar::tab:selected background: #0078d7; """)

    self.current_files = []   # list of file paths in current folder
    self.current_media = None # numpy array for preview
self.init_ui()
    self.load_last_folder()
def init_ui(self):
    # Central widget with splitter
    central = QWidget()
    self.setCentralWidget(central)
    main_layout = QHBoxLayout(central)
    main_layout.setContentsMargins(5,5,5,5)
splitter = QSplitter(Qt.Horizontal)
    main_layout.addWidget(splitter)
# Left panel: file browser & metadata
    left_panel = QWidget()
    left_layout = QVBoxLayout(left_panel)
self.folder_edit = QLineEdit()
    self.folder_edit.setPlaceholderText("Folder path...")
    self.browse_btn = QPushButton("Browse")
    self.browse_btn.clicked.connect(self.browse_folder)
top_row = QHBoxLayout()
    top_row.addWidget(self.folder_edit)
    top_row.addWidget(self.browse_btn)
    left_layout.addLayout(top_row)
self.file_list = QListWidget()
    self.file_list.itemClicked.connect(self.on_file_selected)
    left_layout.addWidget(QLabel("Media Files:"))
    left_layout.addWidget(self.file_list)
self.meta_text = QTextEdit()
    self.meta_text.setReadOnly(True)
    self.meta_text.setMaximumHeight(200)
    left_layout.addWidget(QLabel("Metadata:"))
    left_layout.addWidget(self.meta_text)
splitter.addWidget(left_panel)
# Right panel: tabs for Viewer, Converter, Video Tools
    right_tabs = QTabWidget()
    splitter.addWidget(right_tabs)
    splitter.setSizes([400, 900])
# Tab 1: Panorama Viewer
    viewer_tab = QWidget()
    viewer_layout = QVBoxLayout(viewer_tab)
    self.panorama = PanoramaViewer()
    viewer_layout.addWidget(self.panorama)
    info_lbl = QLabel("🖱️ Drag mouse to rotate 360° panorama")
    info_lbl.setAlignment(Qt.AlignCenter)
    viewer_layout.addWidget(info_lbl)
    right_tabs.addTab(viewer_tab, "360° Viewer")
# Tab 2: Batch Converter (Equirectangular -> Cubemap)
    conv_tab = QWidget()
    conv_layout = QVBoxLayout(conv_tab)
    conv_layout.addWidget(QLabel("Convert equirectangular images to cubemap faces"))
    self.conv_list = QListWidget()
    self.conv_list.setSelectionMode(QAbstractItemView.MultiSelection)
    conv_layout.addWidget(self.conv_list)
    self.output_dir_edit = QLineEdit()
    self.output_dir_edit.setPlaceholderText("Output directory for cubemaps")
    self.browse_output_btn = QPushButton("Select Output")
    self.browse_output_btn.clicked.connect(self.select_output_dir)
    row = QHBoxLayout()
    row.addWidget(self.output_dir_edit)
    row.addWidget(self.browse_output_btn)
    conv_layout.addLayout(row)
    self.cube_size = QSpinBox()
    self.cube_size.setRange(256, 2048)
    self.cube_size.setValue(512)
    conv_layout.addWidget(QLabel("Cubemap face size (px):"))
    conv_layout.addWidget(self.cube_size)
    self.convert_btn = QPushButton("Convert Selected to Cubemap")
    self.convert_btn.clicked.connect(self.start_conversion)
    conv_layout.addWidget(self.convert_btn)
    self.conv_progress = QProgressBar()
    conv_layout.addWidget(self.conv_progress)
    right_tabs.addTab(conv_tab, "Cubemap Converter")
# Tab 3: Video Tools (extract frames)
    video_tab = QWidget()
    video_layout = QVBoxLayout(video_tab)
    video_layout.addWidget(QLabel("Extract frames from 360° video"))
    self.video_file_edit = QLineEdit()
    self.video_file_edit.setPlaceholderText("Select video file")
    self.select_video_btn = QPushButton("Browse Video")
    self.select_video_btn.clicked.connect(self.select_video_file)
    row2 = QHBoxLayout()
    row2.addWidget(self.video_file_edit)
    row2.addWidget(self.select_video_btn)
    video_layout.addLayout(row2)
    self.frame_interval = QSpinBox()
    self.frame_interval.setRange(1, 300)
    self.frame_interval.setValue(30)
    video_layout.addWidget(QLabel("Extract every N frames:"))
    video_layout.addWidget(self.frame_interval)
    self.extract_btn = QPushButton("Extract Frames")
    self.extract_btn.clicked.connect(self.extract_frames)
    video_layout.addWidget(self.extract_btn)
    self.video_progress = QProgressBar()
    video_layout.addWidget(self.video_progress)
    right_tabs.addTab(video_tab, "Video Extractor")
# Status bar
    self.statusBar().showMessage("Ready")
def load_last_folder(self):
    config_file = Path.home() / ".360mpgui_config.json"
    if config_file.exists():
        try:
            with open(config_file, "r") as f:
                cfg = json.load(f)
                last = cfg.get("last_folder", "")
                if os.path.isdir(last):
                    self.folder_edit.setText(last)
                    self.load_folder(last)
        except:
            pass
def save_config(self):
    cfg = "last_folder": self.folder_edit.text()
    try:
        with open(Path.home() / ".360mpgui_config.json", "w") as f:
            json.dump(cfg, f)
    except:
        pass
def browse_folder(self):
    folder = QFileDialog.getExistingDirectory(self, "Select Media Folder")
    if folder:
        self.folder_edit.setText(folder)
        self.load_folder(folder)
def load_folder(self, folder):
    self.current_files = []
    self.file_list.clear()
    self.conv_list.clear()
    for ext in SUPPORTED_IMG | SUPPORTED_VID:
        for f in Path(folder).glob(f"*ext"):
            self.current_files.append(str(f))
            self.file_list.addItem(f.name)
            self.conv_list.addItem(f.name)
    self.statusBar().showMessage(f"Loaded len(self.current_files) media files")
    self.save_config()
def on_file_selected(self, item):
    idx = self.file_list.row(item)
    if idx < len(self.current_files):
        path = self.current_files[idx]
        meta = get_media_metadata(path)
        meta_str = "\n".join([f"k: v" for k, v in meta.items()])
        self.meta_text.setText(meta_str)
# Load for panorama viewer if image
        ext = Path(path).suffix.lower()
        if ext in SUPPORTED_IMG:
            try:
                img = cv2.imread(path)
                if img is not None:
                    self.current_media = img
                    self.panorama.set_image(img)
                    self.statusBar().showMessage(f"Loaded 360° image: Path(path).name")
            except Exception as e:
                self.statusBar().showMessage(f"Error loading image: e")
        elif ext in SUPPORTED_VID:
            self.statusBar().showMessage(f"Video selected: use Video Extractor tab")
def select_output_dir(self):
    dir_ = QFileDialog.getExistingDirectory(self, "Output for cubemaps")
    if dir_:
        self.output_dir_edit.setText(dir_)
def start_conversion(self):
    selected = self.conv_list.selectedItems()
    if not selected or not self.output_dir_edit.text():
        QMessageBox.warning(self, "Error", "Select images and output folder")
        return
    indices = [self.conv_list.row(item) for item in selected]
    files = [self.current_files[i] for i in indices]
    self.convert_btn.setEnabled(False)
    self.conv_progress.setMaximum(len(files))
    self.conv_progress.setValue(0)
    threading.Thread(target=self.batch_convert, args=(files,), daemon=True).start()
def batch_convert(self, files):
    out_dir = self.output_dir_edit.text()
    for i, fpath in enumerate(files):
        img = cv2.imread(fpath)
        if img is None:
            continue
        cubes = equirect_to_cubemap(img, self.cube_size.value())
        name = Path(fpath).stem
        face_dir = Path(out_dir) / name
        face_dir.mkdir(exist_ok=True)
        for face, data in cubes.items():
            cv2.imwrite(str(face_dir / f"face.jpg"), data)
        QMetaObject.invokeMethod(self.conv_progress, "setValue", Qt.QueuedConnection, Q_ARG(int, i+1))
    QMetaObject.invokeMethod(self, "conversion_done", Qt.QueuedConnection)
def conversion_done(self):
    self.convert_btn.setEnabled(True)
    QMessageBox.information(self, "Done", "Cubemap conversion finished.")
    self.statusBar().showMessage("Conversion completed")
def select_video_file(self):
    fname, _ = QFileDialog.getOpenFileName(self, "Select 360° Video", "", "Video Files (*.mp4 *.mov *.avi *.mkv)")
    if fname:
        self.video_file_edit.setText(fname)
def extract_frames(self):
    video_path = self.video_file_edit.text()
    if not video_path or not os.path.exists(video_path):
        QMessageBox.warning(self, "Error", "Select valid video file")
        return
    out_dir = QFileDialog.getExistingDirectory(self, "Select output folder for frames")
    if not out_dir:
        return
    self.extract_btn.setEnabled(False)
    self.video_progress.setValue(0)
    threading.Thread(target=self.do_extract_frames, args=(video_path, out_dir), daemon=True).start()
def do_extract_frames(self, video_path, out_dir):
    cap = cv2.VideoCapture(video_path)
    total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    interval = self.frame_interval.value()
    count = 0
    saved = 0
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        if count % interval == 0:
            out_path = os.path.join(out_dir, f"frame_saved:06d.jpg")
            cv2.imwrite(out_path, frame)
            saved += 1
        count += 1
        if total > 0:
            QMetaObject.invokeMethod(self.video_progress, "setValue", Qt.QueuedConnection, Q_ARG(int, int(count/total*100)))
    cap.release()
    QMetaObject.invokeMethod(self, "extraction_done", Qt.QueuedConnection, Q_ARG(int, saved))
def extraction_done(self, saved):
    self.extract_btn.setEnabled(True)
    QMessageBox.information(self, "Done", f"Extracted saved frames.")
    self.statusBar().showMessage("Video extraction finished")
  • Monitor the graph: Green blocks are good; orange/red indicate slow or bad sectors.
  • After completion: Check the log for reallocated sectors.
  • Tip: For drives with thousands of bad sectors, run ERASE WAIT before remap to stabilize the surface. 360mpgui v1.5.0.0


    Rating: 8.9/10


    360mpgui v1.5.0.0 is more than just a version number—it is a milestone in storage diagnostics. Offering a stable, feature-rich interface for HDD repair, it empowers users to extend the life of mechanical drives, recover critical data, and understand the low-level anatomy of their storage devices.

    Whether you are clamping a failing laptop drive for one final backup or auditing the health of a server farm, mastering 360mpgui v1.5.0.0 gives you a level of control that commercial software often hides behind a paywall. Under the hood, 360mpgui relies on GPAC’s MP4Box

    Remember: With great power comes great responsibility. Always back up your data before running destructive tests, and respect the hardware boundaries that keep our digital lives intact.


    Warning: Many third-party websites bundle malware with older diagnostic tools. Follow these steps to obtain a clean copy of 360mpgui v1.5.0.0:

    Direct link pattern (conceptual): https://archive.org/download/hdd-utilities/360mpgui_v1.5.0.0.zip Monitor the graph: Green blocks are good; orange/red

    After download, scan the ZIP file with Windows Defender or Malwarebytes before extraction.


    360mpgui v1.5.0.0 should never be used for:

    However, it is fully legal to use on your own hardware for diagnostics, repair, or educational research. Many open-source projects reference the 360mpgui driver as a learning example for Windows ATA passthrough.


    Compare products

    {"one"=>"Select 2 or 3 items to compare", "other"=>" of 3 items selected"}

    Select first item to compare

    Select second item to compare

    Select third item to compare

    Compare