main push to repo

This commit is contained in:
Stan 2024-06-29 01:01:51 -05:00
commit 18ca545440
31 changed files with 6901 additions and 0 deletions

27
.dockerignore Executable file
View File

@ -0,0 +1,27 @@
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

19
.vscode/launch.json vendored Executable file
View File

@ -0,0 +1,19 @@
{
"configurations": [
{
"name": "Docker: Python - General",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"python": {
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}
],
"projectType": "general"
}
}
]
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"github.gitAuthentication": false
}

26
.vscode/tasks.json vendored Executable file
View File

@ -0,0 +1,26 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "docker-build",
"label": "docker-build",
"platform": "python",
"dockerBuild": {
"tag": "fbrowser:latest",
"dockerfile": "${workspaceFolder}/Dockerfile",
"context": "${workspaceFolder}",
"pull": true
}
},
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": [
"docker-build"
],
"python": {
"file": "Fbrowser.py"
}
}
]
}

23
Dockerfile Executable file
View File

@ -0,0 +1,23 @@
# For more information, please refer to https://aka.ms/vscode-docker-python
FROM python:3-slim
# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1
# Install pip requirements
COPY requirements.txt .
RUN python -m pip install -r requirements.txt
WORKDIR /app
COPY . /app
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers
RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser
# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug
CMD ["python", "Fbrowser.py"]

273
Fbrowser.py Executable file
View File

@ -0,0 +1,273 @@
# Path: Fbrowser.py
# Sample Music Browser & Ogranizer: Main.py
# Importing Libraries
import sys
import os
from ScanOrg import organizer, file_scanner, DirectoryFilterProxyModel, FileFilterProxyModel
from stanzip import Extractor as extractor
from stanzip import Compressor as compressor
from stanzip import zipfile, py7zr, rarfile
from PyQt5.QtGui import QStandardItem , QStandardItemModel, QContextMenuEvent
from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QVBoxLayout, QMenu, QTreeView, QMessageBox, QSlider, QWidget, QFileSystemModel, QSplitter, QHBoxLayout, QFileDialog
from PyQt5.QtMultimedia import QMediaPlaylist, QMediaPlayer, QMediaContent, QAudioFormat, QAudioDeviceInfo, QAudio
from PyQt5.QtCore import QDir, QSortFilterProxyModel, Qt, QUrl
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as MPLCanvas
from matplotlib.figure import Figure
# Sample Music Browser Main Class
class SampleMusicBrowser(QWidget):
def __init__(self):
super().__init__()
self.organizer = organizer()
self.extractor = extractor()
self.file_model = QStandardItemModel()
self.player = QMediaPlayer()
self.playlist = QMediaPlaylist()
self.player.setPlaylist(self.playlist)
self.tree_model = QFileSystemModel()
self.init_ui()
#self.midi_player = MidPlay()
self.folder_contents_view.setEditTriggers(QTreeView.NoEditTriggers)
self.player.error.connect(self.player_error)
self.player.mediaStatusChanged.connect(self.player_media_status_changed)
self.player.setAudioRole(QAudio.MusicRole)
self.layout = QHBoxLayout()
self.canvas = MPLCanvas()
self.layout.addWidget(self.canvas)
self.setLayout(self.layout)
# Player Error Debugging
def player_error(self, error):
try:
if error == QMediaPlayer.NoError:
return
print(f"An error occurred: Code:{error} {self.player.errorString()}")
except Exception as e:
print(f"Error: {e}")
# Media Status Changed Debugging
def player_media_status_changed(self, status):
if status == QMediaPlayer.NoMedia:
return
print('Media Status: ' + str(status))
def on_extract_button_clicked(self):
extraction_directory = QFileDialog.getExistingDirectory(self, "Select Extraction Directory")
if extraction_directory:
index = self.folder_contents_view.currentIndex()
if index.isValid():
self.extractor.zipviewer(index, self.file_filter_model, self.list_model, extraction_directory)
def show_context_menu(self, position):
menu = QMenu(self)
extract_action = menu.addAction('Extract')
extract_action.triggered.connect(self.on_extract_button_clicked) # Connect to the extraction function
menu.exec(self.folder_contents_view.mapToGlobal(position))
def init_ui(self):
layout = QVBoxLayout()
label = QLabel('Sample Music Browser')
buttons_layout = QHBoxLayout()
layout.addWidget(label)
#self.midi_player = MidPlay()
self.file_tree = QTreeView()
self.file_tree.setHeaderHidden(True)
self.file_tree.clicked.connect(self.change_directory)
play_button = QPushButton('Play')
play_button.clicked.connect(self.player.play)
#play_button.clicked.connect(self.midi_player.play_midi)
buttons_layout.addWidget(play_button)
stop_button = QPushButton('Stop')
stop_button.clicked.connect(self.player.stop)
# stop_button.clicked.connect(self.midi_player.stop)
buttons_layout.addWidget(stop_button)
self.player.stateChanged.connect(self.player_state_changed)
self.player.positionChanged.connect(self.player_position_changed)
self.player.durationChanged.connect(self.player_duration_changed)
layout.addLayout(buttons_layout)
self.folder_contents_view = QTreeView()
self.folder_contents_view.setHeaderHidden(False)
self.folder_contents_view.setRootIsDecorated(False)
self.folder_contents_view.setSortingEnabled(True)
splitter = QSplitter()
splitter.addWidget(self.file_tree)
splitter.addWidget(self.folder_contents_view)
layout.addWidget(splitter)
self.current_dir_label = QLabel()
layout.addWidget(self.current_dir_label)
up_dir_button = QPushButton('Up Directory')
up_dir_button.clicked.connect(self.go_up_directory)
layout.addWidget(up_dir_button)
forward_button = QPushButton('Forward')
forward_button.clicked.connect(self.go_forward_directory)
layout.addWidget(forward_button)
self.setLayout(layout)
self.setWindowTitle('Samples are life!')
path = QFileDialog.getExistingDirectory(self, 'Select Directory')
if path:
self.populate_file_tree(path)
self.player.setVolume(50)
volume_slider = QSlider(Qt.Horizontal)
volume_slider.setRange(0, 100)
volume_slider.setValue(50)
volume_slider.valueChanged.connect(self.player.setVolume)
layout.addWidget(volume_slider)
self.playlist.currentIndexChanged.connect(self.playlist_current_index_changed)
self.playlist.currentMediaChanged.connect(self.playlist_current_media_changed)
self.playlist.mediaInserted.connect(self.playlist_media_inserted)
self.playlist.mediaRemoved.connect(self.playlist_media_removed)
self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
self.folder_contents_view.doubleClicked.connect(self.play_file)
self.folder_contents_view.setContextMenuPolicy(Qt.CustomContextMenu)
self.folder_contents_view.customContextMenuRequested.connect(self.show_context_menu)
def directory_loaded(self, path):
self.file_tree.setRootIndex(self.directory_model.mapFromSource(self.model.index(path)))
self.folder_contents_view.setRootIndex(self.file_filter_model.mapFromSource(self.list_model.index(path)))
def populate_file_tree(self, path):
try:
self.tree_model.setRootPath(path)
self.file_tree.setModel(self.tree_model)
self.directory_model = DirectoryFilterProxyModel()
self.directory_model.setSourceModel(self.tree_model)
self.file_tree.setModel(self.directory_model)
self.file_tree.setRootIndex(self.directory_model.mapFromSource(self.tree_model.index(path)))
self.list_model = QFileSystemModel()
self.list_model.setRootPath(path)
self.file_filter_model = FileFilterProxyModel()
self.file_filter_model.setSourceModel(self.list_model)
self.folder_contents_view.setModel(self.file_filter_model)
self.folder_contents_view.setRootIndex(self.file_filter_model.mapFromSource(self.list_model.index(path)))
self.current_dir_label.setText(path)
except Exception as e:
print(f"Error Populating File Tree: {e}")
def closeEvent(self, event):
reply = QMessageBox.question(self, 'Exit', 'Are you sure you want to exit?',
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
def play_file(self, index):
try:
index = self.file_filter_model.mapToSource(index)
file_path = self.list_model.filePath(index)
if file_path.endswith(('.zip', '.rar', '.7z')):
with zipfile.ZipFile(file_path, 'r') as zip_ref:
for filename in zip_ref.namelist():
if filename.lower().endswith(('mp3', 'wav', 'ogg', 'flac',
'm4a', 'wma', 'aac', 'aiff', 'alac',
'mid', 'midi', 'mp4', 'm4a')):
audo_file = zip_ref.extract(filename)
media = QMediaContent(QUrl.fromLocalFile(audo_file))
self.playlist.clear()
self.playlist.addMedia(media)
self.player.play()
break
if os.path.exists(audo_file):
os.remove(audo_file)
elif file_path.endswith(('.mid', '.midi')):
#self.midi_player = MidPlay()
#fig = self.midi_player.play_midi(file_path)
self.canvas.draw()
else:
media = QMediaContent(QUrl.fromLocalFile(file_path))
self.playlist.clear()
self.playlist.addMedia(media)
self.player.play()
except Exception as e:
print(f"Error Playing File: {e}")
def player_state_changed(self, state):
if state == QMediaPlayer.StoppedState:
self.playlist.setCurrentIndex(0)
def player_position_changed(self, position):
pass
def player_duration_changed(self, duration):
pass
def playlist_current_index_changed(self, index):
pass
def playlist_current_media_changed(self, media):
pass
def playlist_media_inserted(self, start, end):
pass
def playlist_media_removed(self, start, end):
pass
def change_directory(self, index):
index = self.directory_model.mapToSource(index)
try:
file_path = self.tree_model.filePath(index)
self.list_model.setRootPath(file_path)
self.current_dir_label.setText(file_path)
self.folder_contents_view.setRootIndex(self.file_filter_model.mapFromSource(self.list_model.index(file_path)))
except Exception as e:
print(f"Error Changing Dirs.: {e}")
def go_up_directory(self):
index = self.folder_contents_view.rootIndex()
index = self.file_filter_model.mapToSource(index)
parent_index = index.parent()
if parent_index.isValid(): # Check if the parent index is valid
self.folder_contents_view.setRootIndex(self.file_filter_model.mapFromSource(parent_index))
self.current_dir_label.setText(self.list_model.filePath(parent_index))
def go_forward_directory(self):
index = self.folder_contents_view.rootIndex()
index = self.file_filter_model.mapToSource(index)
parent_index = index.parent()
if parent_index.isValid():
self.folder_contents_view.setRootIndex(self.file_filter_model.mapFromSource(parent_index))
self.current_dir_label.setText(self.list_model.filePath(parent_index))
if __name__ == '__main__':
# player = MidPlay()
# file_path = list(player.select_file()) # Get the selected file path
# viewer = MidViewer()
# viewer.read_midi(file_path)
# viewer.view_midi()
#viewer.show()
# viewer.save('test.png')
# viewer.clear()
# viewer.close()
# print(viewer.get_midi_info(file_path)) # Use the file path
# print(viewer.get_piano_roll(file_path)) # Use the file path
# print(viewer.get_tempo(file_path)) # Use the file path
# print(viewer.get_notes(file_path)) # Use the file path
app = QApplication(sys.argv)
sampleMusicBrowser = SampleMusicBrowser()
sampleMusicBrowser.show()
sys.exit(app.exec_())

255
MidPlay.py Executable file
View File

@ -0,0 +1,255 @@
#Path: MidPlay.py
# Description: A class to play MIDI files and a class to view MIDI files
# probably switching to a different library for midi handling
# pretty_midi is not very good for this purpose or real-time playback of midi files
"""Pretty Midi module type stubs are included but incomplete.
Pretty Midi comes with a statement to cite the following paper
when used in a research project:
Colin Raffel and Daniel P. W. Ellis. Intuitive Analysis,
Creation and Manipulation of MIDI Data with pretty_midi.
In Proceedings of the 15th International Conference on Music
Information Retrieval Late Breaking and Demo Papers, 2014.
colinraffel.com/publications/ismir2014intuitive.pdf
"""
import pygame
# Imports
import pretty_midi
import fluidsynth
import sys
import os
from PyQt5.QtWidgets import (QApplication, QLabel, QListWidget, QFileDialog, QMessageBox, QWidget, QPushButton, QHBoxLayout,
QVBoxLayout,
QProgressBar,
QSlider) # structured for readability and to avoid long lines and it annoys my friend XD
from PyQt5.QtCore import QTimer, Qt
import threading
import cProfile # profiler remove for production
pygame.mixer.init()
pygame.init()
class MidPlayGUI(QWidget):
def __init__(self):
super().__init__()
self.player = MidPlay()
self.current_midi_label = QLabel()
self.playlist_widget = QListWidget()
self.setWindowTitle("MidPlay - Midi Player")
self.init_ui()
self.timer = QTimer()
self.timer.timeout.connect(self.handle_song_end)
self.timer.start(1000)
def set_volume(self, value):
volume = value / 100
pygame.mixer.music.set_volume(volume)
def update_progress(self):
if self.player.current_midi:
current_time = pygame.mixer.music.get_pos() / 1000 # get_pos returns time in milliseconds NOT SECONDS!
total_time = self.player.current_midi.get_end_time()
progress = current_time / total_time * 100
self.progress_bar.setValue(int(progress))
def handle_song_end(self):
if self.player.playing and not pygame.mixer.music.get_busy():
self.player.next_song()
if self.player.playlist:
self.player.current_index %= len(self.player.playlist)
filepath = self.player.playlist[self.player.current_index]
filename = os.path.basename(filepath)
self.current_midi_label.setText(f"Current MIDI: {filename}")
self.update_progress()
def init_ui(self):
#label = QLabel("MidPlay - Midi player")
#label.setStyleSheet("font-size: 20px; font-weight: bold;")
self.progress_bar = QProgressBar()
self.volume_slider = QSlider(Qt.Horizontal)
self.volume_slider.setMinimum(0)
self.volume_slider.setMaximum(100)
self.volume_slider.setValue(100)
self.volume_slider.valueChanged.connect(self.set_volume)
self.current_midi_label.setText("Current MIDI: None")
self.playlist_widget.itemDoubleClicked.connect(self.play_selected_song)
pygame.mixer.music.set_endevent(pygame.USEREVENT)
# Buttons
play_button = QPushButton("Play")
play_button.clicked.connect(self.player.play_midi)
pause_button = QPushButton("Pause")
pause_button.clicked.connect(self.player.pause)
stop_button = QPushButton("Stop")
stop_button.clicked.connect(self.player.stop)
next_button = QPushButton("Next")
next_button.clicked.connect(self.player.next_song)
back_button = QPushButton("Back")
back_button.clicked.connect(self.previous_song)
add_button = QPushButton("Add to Playlist")
add_button.clicked.connect(self.load_midi_file)
add_folder_button = QPushButton("Add Folder to Playlist")
add_folder_button.clicked.connect(self.load_folder)
clear_button = QPushButton("Clear Playlist")
clear_button.clicked.connect(self.clear_playlist)
# Window layout
layout = QVBoxLayout()
layout.addWidget(self.current_midi_label)
layout.addWidget(self.playlist_widget)
layout.addWidget(self.progress_bar)
layout.addWidget(self.volume_slider)
layout.addWidget(play_button)
layout.addWidget(pause_button)
layout.addWidget(stop_button)
layout.addWidget(next_button)
layout.addWidget(back_button)
layout.addWidget(add_button)
layout.addWidget(add_folder_button)
layout.addWidget(clear_button)
progress_volume_layout = QHBoxLayout()
progress_volume_layout.addWidget(self.progress_bar)
progress_volume_layout.addWidget(self.volume_slider)
layout.addLayout(progress_volume_layout)
self.setLayout(layout)
# Event handlers
def play_selected_song(self, item):
index = self.playlist_widget.row(item)
self.current_index = index
filepath = self.player.playlist[self.current_index]
self.player.load_midi(filepath)
self.player.play_midi()
pygame.mixer.music.set_endevent(pygame.USEREVENT)
filename = os.path.basename(filepath)
self.current_midi_label.setText(f"Current MIDI: {filename}")
def load_midi_file(self):
filepath, _ = QFileDialog.getOpenFileName(self, "Select MIDI File", filter="MIDI files (*.mid *.midi)")
if filepath:
filename = os.path.basename(filepath)
self.player.load_midi(filepath)
self.current_midi_label.setText(f"Current MIDI: {filename}")
self.player.play_midi()
self.playlist_widget.addItem(filename)
self.player.add_to_playlist(filepath)
def load_folder(self):
folder = QFileDialog.getExistingDirectory(self, "Select Folder")
if folder:
for file in os.listdir(folder):
if file.endswith((".midi", ".mid")):
filepath = os.path.join(folder, file)
self.playlist_widget.addItem(file)
self.player.add_to_playlist(filepath) # Only add to playlist, don't load immediately!!!!!!!!!!!!!!!
#probably should be in the MidPlay class
def previous_song(self):
if self.player.playlist:
filepath = self.player.playlist[self.current_index]
filename = os.path.basename(filepath)
self.player.current_index = (self.player.current_index - 1) % len(self.player.playlist)
self.current_midi_label.setText(f"Current MIDI: {filename}")
self.player.play_midi()
def clear_playlist(self):
self.player.clear_playlist()
self.playlist_widget.clear()
def closeEvent(self, event):
confirmation = QMessageBox.question(self, "Exit Confirmation", "Are you sure you want to exit?", QMessageBox.Yes | QMessageBox.No)
if confirmation == QMessageBox.Yes:
pygame.mixer.quit()
pygame.quit()
event.accept()
else:
event.ignore()
class MidPlay:
"""The Heart of Midi Playback"""
def __init__(self):
self.playlist = []
self.current_midi = None
self.playing = False
self.current_index = 0
def load_midi(self, filepath: str) -> None:
def load():
try:
self.current_midi = pretty_midi.PrettyMIDI(filepath)
pygame.mixer.music.load(filepath)
except Exception as e:
print(f"Error loading MIDI: {e}")
threading.Thread(target=load).start()
def add_to_playlist(self, filepath: str) -> None:
self.playlist.append(filepath)
def clear_playlist(self) -> None:
self.playlist = []
def play_midi(self) -> None:
def play():
if self.current_midi:
self.current_midi.instruments[0].synthesize()
pygame.mixer.music.play()
self.playing = True
pygame.mixer.music.set_endevent(pygame.USEREVENT)
else:
print("No MIDI file loaded")
threading.Thread(target=play).start()
def pause(self) -> None:
pygame.mixer.music.pause()
self.playing = False
def stop(self) -> None:
pygame.mixer.music.stop()
self.playing = False
def next_song(self) -> None:
#print("Debug: next_song() called", self.playlist) debug line
if self.playlist:
self.current_index = (self.current_index + 1) % len(self.playlist)
filepath = self.playlist[self.current_index]
# If a new MIDI was loaded before the last one ended, respect that as the new playlist start
if self.current_midi and self.playing:
# print("Debug: New MIDI loaded before last one ended") # debug line
self.load_midi(filepath)
self.play_midi()
# print("Debug: Filepath:", filepath) # debug line
# print("Debug: Current MIDI:", self.current_midi) # debug line
if __name__ == '__main__':
app = QApplication([])
player_gui = MidPlayGUI()
player_gui.show()
running = True
while True:
for event in pygame.event.get():
if event.type == pygame.USEREVENT:
player_gui.player.next_song()
if event.type == pygame.QUIT:
running = False
break
app.exec_()

213
ScanOrg.py Executable file
View File

@ -0,0 +1,213 @@
#Path: ScanOrg.py
# Description: A class to scan and organize music files
import concurrent.futures
import threading
import queue
import zipfile
import py7zr
import rarfile
import os
import mutagen
from PyQt5.QtCore import Qt, QSortFilterProxyModel, QAbstractTableModel, QModelIndex, QVariant, QAbstractItemModel, QFileInfo, QDir, QMimeDatabase, QMimeData, QUrl, QItemSelectionModel, QItemSelection, QItemSelectionRange, QObject, QThread, QTimer, QEventLoop, QCoreApplication, QUrl, pyqtSignal
# Directory Filter Proxy Model
class DirectoryFilterProxyModel(QSortFilterProxyModel):
def __init__(self):
super().__init__()
self.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.setFilterKeyColumn(0)
def filterAcceptsRow(self, source_row, source_parent):
index = self.sourceModel().index(source_row, 0, source_parent)
return self.sourceModel().isDir(index)
# File Filter Proxy Model
class FileFilterProxyModel(QSortFilterProxyModel):
def __init__(self):
super().__init__()
self.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.setFilterKeyColumn(0)
self.allowed_extensions = ['.zip', '.mp3', '.wav', '.flac', '.mid', '.midi', '.aiff', '.aif', '.aifc', '.au', '.snd', '.wv', '.wma', '.m4a']
def filterAcceptsRow(self, source_row, source_parent):
index = self.sourceModel().index(source_row, 0, source_parent)
if self.sourceModel().isDir(index):
return True
else:
return self.sourceModel().fileName(index).endswith(tuple(self.allowed_extensions))
# File Scan and Organize
class file_scanner:
def __init__(self):
self.file_list = []
self.cache = {}
def scan(self, path):
def background_scan(self, path):
if path in self.cache:
return self.cache[path]
file_list = []
dirs_queue = queue.Queue()
dirs_queue.put(path)
while not dirs_queue.empty():
current_path = dirs_queue.get()
try:
for root, dirs, files in os.walk(current_path):
for dir in dirs:
dirs_queue.put(os.path.join(root, dir))
for file in files:
if file.endswith(('.mp3', '.wav', '.flac', '.mid', '.midi', '.aiff', '.aif', '.aifc', '.au', '.snd', '.wv', '.wma', '.m4a')):
file_list.append(os.path.join(root, file))
self.cache[current_path] = file_list
except (IOError, PermissionError, FileNotFoundError, OSError) as e:
print(f"Error Scanning Files: {e}")
return file_list
file_list = []
thread = threading.Thread(target=background_scan, args=(path, file_list))
thread.start()
return file_list
def get_file_list(self):
return self.file_list
def clear_file_list(self):
self.file_list = []
class Extractor:
def zipviewer(self, index, file_filter_model, list_model, extraction_directory):
if index.isValid() and extraction_directory is not None:
index = file_filter_model.mapToSource(index)
file_path = list_model.filePath(index)
try:
if file_path.endswith(".zip"):
with zipfile.ZipFile(file_path, 'r') as zip_ref:
self._extract_files(zip_ref, extraction_directory)
elif file_path.endswith(".rar"):
with rarfile.RarFile(file_path, 'r') as rar_ref:
self._extract_files(rar_ref, extraction_directory)
elif file_path.endswith(".7z"):
with py7zr.SevenZipFile(file_path, 'r') as sevenzip_ref:
self._extract_files(sevenzip_ref, extraction_directory)
else:
print(f"Unsupported file format: {file_path}")
except (zipfile.BadZipFile, zipfile.LargeZipFile) as e:
print(f"ZIP Extraction Error: {e}")
except (rarfile.RarFileException, rarfile.NotRARFile) as e:
print(f"RAR Extraction Error: {e}")
except py7zr.exceptions.SevenZipException as e:
print(f"7z Extraction Error: {e}")
except OSError as e:
print(f"Extraction Error: {e}")
def _extract_files(self, archive_ref, extraction_directory):
for filename in archive_ref.namelist():
destination = os.path.join(extraction_directory, filename)
if os.path.isfile(destination):
print(f"File already exists: {destination}")
else:
os.makedirs(os.path.dirname(destination), exist_ok=True)
with open(destination, 'wb') as file:
file.write(archive_ref.read(filename))
print(f"Extracted: {filename}")
class organizer:
global metadata_queue
metadata_queue = queue.Queue()
def __init__(self):
self.file_list = []
self.artist_list = []
self.album_list = []
self.genre_list = []
self.year_list = []
self.file_scanner = file_scanner()
self.file_info_cache = {}
def scan(self, path):
if path in self.file_scanner.cache:
self.file_list = self.file_scanner.cache[path]
else:
self.file_list = self.file_scanner.scan(path)
def get_file_list(self):
return self.file_list
def clear_file_list(self):
self.file_list = []
def get_artist_list(self):
return self.artist_list
def get_album_list(self):
return self.album_list
def get_genre_list(self):
return self.genre_list
def get_year_list(self):
return self.year_list
def clear_artist_list(self):
self.artist_list = []
def clear_album_list(self):
self.album_list = []
def clear_genre_list(self):
self.genre_list = []
def clear_year_list(self):
self.year_list = []
def organize(self):
results_queue = queue.Queue()
metadata = pyqtSignal(dict)
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = []
for file in self.file_list:
futures.append(executor.submit(self.get_file_info, file, results_queue))
for future in concurrent.futures.as_completed(futures):
try:
metadata = future.result()
if metadata['artist'] not in self.artist_list:
self.artist_list.append(metadata['artist'])
if metadata['album'] not in self.album_list:
self.album_list.append(metadata['album'])
if metadata['genre'] not in self.genre_list:
self.genre_list.append(metadata['genre'])
if metadata['year'] not in self.year_list:
self.year_list.append(metadata['year'])
except mutagen.mp3.HeaderNotFoundError:
print('Error: ' + file)
continue
while not metadata_queue.put(metadata):
pass
def get_file_info(self, file, results_queue):
try:
audio = mutagen.File(file)
artist = audio['artist'][0]
album = audio['album'][0]
genre = audio['genre'][0]
year = audio['date'][0]
if artist not in self.artist_list:
self.artist_list.append(artist)
if album not in self.album_list:
self.album_list.append(album)
if genre not in self.genre_list:
self.genre_list.append(genre)
if year not in self.year_list:
self.year_list.append(year)
metadata = {
'artist': artist,
'album': album,
'genre': genre,
'year': year
}
self.metadata_extracted.emit(metadata)
except Exception as e:
results_queue.put(None)
print('Error: ' + file)
if os.path.splitext(file)[1] == ('.mp3', '.wav', '.flac', '.m4a', '.wma', 'mid', '.midi'):
self.organize_audio()
audio = mutagen.File(file)

Binary file not shown.

Binary file not shown.

94
compression.py Executable file
View File

@ -0,0 +1,94 @@
# imports
import os
import zipfile
import rarfile
import py7zr
import shutil
import tarfile
import argparse
import tqdm
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import pool
# File Compressor
class Compressor:
def __init__(self):
pass
def _compress_folder(self, source_path, archive_file, archive_format):
for root, _, files in os.walk(source_path):
for file in files:
file_path = os.path.join(root, file)
archive_path = os.path.relpath(file_path, source_path)
self._compress_file(file_path, archive_file, archive_path, archive_format)
def _compress_file(self, file_path, archive_file, archive_path, archive_format):
if archive_format == "zip":
with open(file_path, 'rb') as file:
for chunk in iter(lambda: file.read(1024 * 1024), b''):
archive_file.writestr(archive_path, chunk)
elif archive_format == "tar":
archive_file.add(file_path, arcname=archive_path)
elif archive_format == "7z":
archive_file.write(file_path, archive_path)
else:
raise ValueError(f"Unsupported archive format: {archive_format}")
def compress(self, source_path, archive_name, archive_format="zip"):
pbar = tqdm.tqdm(total=100, unit="B", unit_scale=True, desc="Compressing")
supported_formats = ["zip", "tar", "7z"]
if archive_format not in supported_formats:
raise ValueError(f"Unsupported archive format: {archive_format}")
archive_path = os.path.join(os.path.dirname(source_path), f"{archive_name}.{archive_format}")
# Check if source path exists
if not os.path.exists(source_path):
print(f"Source path does not exist: {source_path}")
return
# Check if archive path already exists
if os.path.exists(archive_path):
print(f"Archive path already exists: {archive_path}")
return
# Open archive file based on format
if archive_format == "zip":
archive_file = zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED)
elif archive_format == "tar":
archive_file = tarfile.open(archive_path, mode="w")
elif archive_format == "7z":
archive_file = py7zr.SevenZipFile(archive_path, mode="w")
# Compress the source path
try:
if os.path.isdir(source_path):
self._compress_folder(source_path, archive_file, archive_format)
pbar.update(1)
else:
if os.path.isfile(source_path):
self._compress_file(source_path, archive_file, "", archive_format)
pbar.update(1)
else:
print(f"Source path is not a file or directory: {source_path}")
return
except Exception as e:
print(f"Compressed to: {archive_path} error:{e}")
finally:
archive_file.close() # Ensure closing the archive file
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Compress files")
parser.add_argument("source", help="Path to the file or folder to compress")
parser.add_argument("archive_name", help="Name for the compressed archive")
parser.add_argument("-f", "--format", choices=["zip", "tar", "7z"], default="zip", help="Archive format")
args = parser.parse_args()
compressor = Compressor()
compressor.compress(args.source, args.archive_name, args.format)

11
docker-compose.debug.yml Executable file
View File

@ -0,0 +1,11 @@
version: '3.4'
services:
fbrowser:
image: fbrowser
build:
context: .
dockerfile: ./Dockerfile
command: ["sh", "-c", "pip install debugpy -t /tmp && python /tmp/debugpy --wait-for-client --listen 0.0.0.0:5678 Fbrowser.py "]
ports:
- 5678:5678

8
docker-compose.yml Executable file
View File

@ -0,0 +1,8 @@
version: '3.4'
services:
fbrowser:
image: fbrowser
build:
context: .
dockerfile: ./Dockerfile

74
extraction.py Executable file
View File

@ -0,0 +1,74 @@
# extractor.py
import os
import zipfile
import rarfile
import py7zr
import argparse
from tqdm import tqdm
from concurrent.futures import ThreadPoolExecutor
class Extractor:
def zipviewer(self, source, destination):
print(f"checking if {source} exists")
if not os.path.exists(source):
print(f"Error: Archive file not found: {source}")
return
try:
print(f"checking if {destination} exists")
if not os.path.exists(destination):
print(f"{destination} does not exist, creating {destination}")
os.makedirs(destination)
print(f"{destination} created")
else:
print(f"{destination} exists")
print(f"checking if {source} is a valid archive file")
if source.endswith(".zip"):
print(f"Extracting all files from {source} to {destination}")
with zipfile.ZipFile(source, 'r') as zip_ref:
zip_ref.extractall(destination)
print(f"Extracted all files from {source} to {destination}")
elif source.endswith(".rar, .tar.gz, .tar.bz2, .tar.xz, .tar.zst"):
with rarfile.RarFile(source, 'r') as rar_ref:
rar_ref.extractall(destination)
print(f"Extracted all files from {source} to {destination}")
elif source.endswith(".7z"):
with py7zr.SevenZipFile(source, 'r') as sevenzip_ref:
sevenzip_ref.extractall(destination)
print(f"Extracted all files from {source} to {destination}")
else:
print(f"Unsupported file format: {source}")
except (zipfile.BadZipFile, zipfile.LargeZipFile) as e:
print(f"ZIP Extraction Error: {e}")
except (rarfile.RarFileException, rarfile.NotRARFile) as e:
print(f"RAR Extraction Error: {e}")
except py7zr.exceptions.SevenZipException as e:
print(f"7z Extraction Error: {e}")
except OSError as e:
print(f"Extraction Error: {e}")
def main():
print("Welcome to the Archive Extractor!")
parser = argparse.ArgumentParser(description="Compress or extract files")
subparsers = parser.add_subparsers(title="Command", dest="command")
# Subparser for extraction
extract_parser = subparsers.add_parser("extract")
extract_parser.add_argument("source", help="Path to the archive file")
extract_parser.add_argument("destination", help="Extraction directory")
args = parser.parse_args()
if args.command == "extract":
print(f"Extracting {args.source} to {args.destination}")
extractor = Extractor()
extractor.zipviewer(args.source, args.destination)
if __name__ == "__main__":
main()

BIN
paq-8l_intel.exe Executable file

Binary file not shown.

102
paq7asm-x86_64.asm Executable file
View File

@ -0,0 +1,102 @@
; YASM x86-64 assembly language code for PAQ7/8 ver. 2, Jan 18, 2007
;
; (C) 2005-2007, Matt Mahoney, Matthew Fite.
; This is free software under GPL, http://www.gnu.org/licenses/gpl.txt
;
; This code was tested on an Athlon-64 under Ubuntu Linux 2.6.15.27.amd64-generic
; with paq8f and paq8jd. It should work with any PAQ version since paq7,
; because all versions use the same paq7asm.asm code for 32 bit Windows/Linux
; versions. To compile e.g. paq8jd in Linux:
;
; yasm paq7asm-x86_64.asm -f elf -m amd64
; g++ -O3 -s -fomit-frame-pointer -DUNIX paq8jd.cpp paq7asm-x86_64.o -o paq8jd
;
; This code has not been tested in Windows. (You would need XP Professional
; 64 bit edition and a 64 bit compiler).
section .text
BITS 64
; Vector product a*b of n signed words, returning signed dword scaled
; down by 8 bits. n is rounded up to a multiple of 8.
global dot_product ; (short* a, short* b, int n)
align 16
dot_product:
mov rcx, rdx ; n
mov rax, rdi ; a
mov rdx, rsi ; b
add rcx, 7 ; n rounding up
and rcx, -8
jz .done
sub rax, 16
sub rdx, 16
pxor xmm0, xmm0 ; sum = 0
.loop: ; each loop sums 4 products
movdqa xmm1, [rax+rcx*2] ; put parital sums of vector product in xmm1
pmaddwd xmm1, [rdx+rcx*2]
psrad xmm1, 8
paddd xmm0, xmm1
sub rcx, 8
ja .loop
movdqa xmm1, xmm0 ; add 4 parts of xmm0 and return in eax
psrldq xmm1, 8
paddd xmm0, xmm1
movdqa xmm1, xmm0
psrldq xmm1, 4
paddd xmm0, xmm1
movd rax, xmm0
.done
ret
; Train n neural network weights w[n] on inputs t[n] and err.
; w[i] += (t[i]*err*2 >> 16)+1 >> 1 bounded to +- 32K.
; n is rounded up to a multiple of 8.
;1st arg rdi -> *t
;2nd arg rsi -> *w
;3rd arg rdx -> n
;4th arg rcx -> err (signed 16 bits)
global train ; (short* t, short* w, int n, int err)
BITS 64
align 16
train:
mov rax, rcx ; err
and rax, 0xffff ; put 8 copies of err in xmm0
movd xmm0, rax
movd xmm1, rax
pslldq xmm1, 2
por xmm0, xmm1
movdqa xmm1, xmm0
pslldq xmm1, 4
por xmm0, xmm1
movdqa xmm1, xmm0
pslldq xmm1, 8
por xmm0, xmm1;
pcmpeqb xmm1, xmm1 ; 8 copies of 1 in xmm1
psrlw xmm1, 15
mov rcx, rdx ; n
mov rax, rdi ; t
mov rdx, rsi ; w
add rcx, 7 ; n/8 rounding up
and rcx, -8
sub rax, 16
sub rdx, 16
jz .done
align 16
.loop: ; each iteration adjusts 8 weights
movdqa xmm2, [rdx+rcx*2] ; w[i]
movdqa xmm3, [rax+rcx*2] ; t[i]
paddsw xmm3, xmm3 ; t[i]*2
pmulhw xmm3, xmm0 ; t[i]*err*2 >> 16
paddsw xmm3, xmm1 ; (t[i]*err*2 >> 16)+1
psraw xmm3, 1 ; (t[i]*err*2 >> 16)+1 >> 1
paddsw xmm2, xmm3 ; w[i] + xmm3
movdqa [rdx+rcx*2], xmm2
sub rcx, 8
ja .loop
.done:
ret

140
paq7asm.asm Executable file
View File

@ -0,0 +1,140 @@
; NASM assembly language code for PAQ7.
; (C) 2005, Matt Mahoney.
; This is free software under GPL, http://www.gnu.org/licenses/gpl.txt
;
; MINGW g++: nasm paq7asm.asm -f win32 --prefix _
; DJGPP g++: nasm paq7asm.asm -f coff --prefix _
; Borland, Mars: nasm paq7asm.asm -f obj --prefix _
; Linux: nasm paq7asm.asm -f elf
;
; For other Windows compilers try -f win32 or -f obj. Some old versions
; of Linux should use -f aout instead of -f elf.
;
; This code will only work on a Pentium-MMX or higher. It doesn't
; use extended (Katmai/SSE) instructions. It won't work
; in 64-bit mode.
section .text use32 class=CODE
; Reset after MMX
global do_emms
do_emms:
emms
ret
; Vector product a*b of n signed words, returning signed dword scaled
; down by 8 bits. n is rounded up to a multiple of 8.
global dot_product ; (short* a, short* b, int n)
align 16
dot_product:
mov eax, [esp+4] ; a
mov edx, [esp+8] ; b
mov ecx, [esp+12] ; n
add ecx, 7 ; n rounding up
and ecx, -8
jz .done
sub eax, 8
sub edx, 8
pxor mm0, mm0 ; sum = 0
.loop: ; each loop sums 4 products
movq mm1, [eax+ecx*2] ; put halves of vector product in mm0
pmaddwd mm1, [edx+ecx*2]
movq mm2, [eax+ecx*2-8]
pmaddwd mm2, [edx+ecx*2-8]
psrad mm1, 8
psrad mm2, 8
paddd mm0, mm1
paddd mm0, mm2
sub ecx, 8
ja .loop
movq mm1, mm0 ; add 2 halves of mm0 and return in eax
psrlq mm1, 32
paddd mm0, mm1
movd eax, mm0
emms
.done
ret
; This should work on a Pentium 4 or higher in 32-bit mode,
; but it isn't much faster than the MMX version so I don't use it.
global dot_product_sse2 ; (short* a, short* b, int n)
align 16
dot_product_sse2:
mov eax, [esp+4] ; a
mov edx, [esp+8] ; b
mov ecx, [esp+12] ; n
add ecx, 7 ; n rounding up
and ecx, -8
jz .done
sub eax, 16
sub edx, 16
pxor xmm0, xmm0 ; sum = 0
.loop: ; each loop sums 4 products
movdqa xmm1, [eax+ecx*2] ; put parital sums of vector product in xmm0
pmaddwd xmm1, [edx+ecx*2]
psrad xmm1, 8
paddd xmm0, xmm1
sub ecx, 8
ja .loop
movdqa xmm1, xmm0 ; add 4 parts of xmm0 and return in eax
psrldq xmm1, 8
paddd xmm0, xmm1
movdqa xmm1, xmm0
psrldq xmm1, 4
paddd xmm0, xmm1
movd eax, xmm0
.done
ret
; Train n neural network weights w[n] on inputs t[n] and err.
; w[i] += t[i]*err*2+1 >> 17 bounded to +- 32K.
; n is rounded up to a multiple of 8.
global train ; (short* t, short* w, int n, int err)
align 16
train:
mov eax, [esp+16] ; err
and eax, 0xffff ; put 4 copies of err in mm0
movd mm0, eax
movd mm1, eax
psllq mm1, 16
por mm0, mm1
movq mm1, mm0
psllq mm1, 32
por mm0, mm1
pcmpeqb mm1, mm1 ; 4 copies of 1 in mm1
psrlw mm1, 15
mov eax, [esp+4] ; t
mov edx, [esp+8] ; w
mov ecx, [esp+12] ; n
add ecx, 7 ; n/8 rounding up
and ecx, -8
sub eax, 8
sub edx, 8
jz .done
.loop: ; each iteration adjusts 8 weights
movq mm2, [edx+ecx*2] ; w[i]
movq mm3, [eax+ecx*2] ; t[i]
movq mm4, [edx+ecx*2-8] ; w[i]
movq mm5, [eax+ecx*2-8] ; t[i]
paddsw mm3, mm3
paddsw mm5, mm5
pmulhw mm3, mm0
pmulhw mm5, mm0
paddsw mm3, mm1
paddsw mm5, mm1
psraw mm3, 1
psraw mm5, 1
paddsw mm2, mm3
paddsw mm4, mm5
movq [edx+ecx*2], mm2
movq [edx+ecx*2-8], mm4
sub ecx, 8
ja .loop
.done:
emms
ret

93
paq7asmsse.asm Executable file
View File

@ -0,0 +1,93 @@
; NASM assembly language code for PAQ7.
; (C) 2005, Matt Mahoney.
; train - written by wowtiger, Jan. 30, 2007
;
; This is free software under GPL, http://www.gnu.org/licenses/gpl.txt
;
; This code is a replacement for paq7asm.asm for newer processors
; supporting SSE2 instructions. It is about 1% faster than the
; equivalent MMX code. It can be linked with any version of paq7*
; or paq8*. Assemble as below, then link following the instructions
; in the C++ source code, replacing paq7asm.obj with paq7asmsse.obj.
; No C++ code changes are needed.
;
; MINGW g++: nasm paq7asmsse.asm -f win32 --prefix _
; DJGPP g++: nasm paq7asmsse.asm -f coff --prefix _
; Borland, Mars: nasm paq7asmsse.asm -f obj --prefix _
; Linux: nasm paq7asmsse.asm -f elf
;
section .text use32 class=CODE
; Vector product a*b of n signed words, returning signed dword scaled
; down by 8 bits. n is rounded up to a multiple of 8.
global dot_product ; (short* a, short* b, int n)
align 16
dot_product:
mov eax, [esp+4] ; a
mov edx, [esp+8] ; b
mov ecx, [esp+12] ; n
add ecx, 7 ; n rounding up
and ecx, -8
jz .done
sub eax, 16
sub edx, 16
pxor xmm0, xmm0 ; sum = 0
.loop: ; each loop sums 4 products
movdqa xmm1, [eax+ecx*2] ; put parital sums of vector product in xmm0
pmaddwd xmm1, [edx+ecx*2]
psrad xmm1, 8
paddd xmm0, xmm1
sub ecx, 8
ja .loop
movdqa xmm1, xmm0 ; add 4 parts of xmm0 and return in eax
psrldq xmm1, 8
paddd xmm0, xmm1
movdqa xmm1, xmm0
psrldq xmm1, 4
paddd xmm0, xmm1
movd eax, xmm0
.done
ret
; Train n neural network weights w[n] on inputs t[n] and err.
; w[i] += t[i]*err*2+1 >> 17 bounded to +- 32K.
; n is rounded up to a multiple of 8.
; Train for SSE2
; Use this code to get some performance...
global train ; (short* t, short* w, int n, int err)
align 16
train:
mov eax, [esp+4] ; t
mov edx, [esp+8] ; w
mov ecx, [esp+12] ; n
add ecx, 7 ; n/8 rounding up
and ecx, -8
jz .done
sub eax, 16
sub edx, 16
movd xmm0, [esp+16]
pshuflw xmm0,xmm0,0
punpcklqdq xmm0,xmm0
.loop: ; each iteration adjusts 8 weights
movdqa xmm3, [eax+ecx*2] ; t[i]
movdqa xmm2, [edx+ecx*2] ; w[i]
paddsw xmm3, xmm3 ; t[i]*2
pmulhw xmm3, xmm0 ; t[i]*err*2 >> 16
paddsw xmm3, [_mask] ; (t[i]*err*2 >> 16)+1
psraw xmm3, 1 ; (t[i]*err*2 >> 16)+1 >> 1
paddsw xmm2, xmm3 ; w[i] + xmm3
movdqa [edx+ecx*2], xmm2
sub ecx, 8
ja .loop
.done:
ret
align 16
_mask dd 10001h,10001h,10001h,10001h ; 8 copies of 1 in xmm1

3575
paq8l.cpp Executable file

File diff suppressed because it is too large Load Diff

BIN
paq8l.exe Executable file

Binary file not shown.

1222
paq9a.cpp Executable file

File diff suppressed because it is too large Load Diff

BIN
paq9a.exe Executable file

Binary file not shown.

178
paqtest.py Executable file
View File

@ -0,0 +1,178 @@
import os
import argparse
import subprocess
from multiprocessing import Pool
from sys import platform
def set_archive_filename(output: str, paq8l_version: str) -> str:
basename, ext = os.path.splitext(output)
if ext == 'paq8l{}'.format(paq8l_version):
return output
if ext == 'paq8l':
return output + paq8l_version
else:
return output + '.paq8l' + paq8l_version
def compress_file(file: str, output: str, exe_filename: str, compression_arg: str, paq8l_version: str) -> None:
output = set_archive_filename(output, paq8l_version)
if platform == "win32":
cmd = [exe_filename, compression_arg, file, output]
else:
cmd = "{} {} \"{}\" \"{}\"".format(exe_filename, compression_arg, file, output)
print(cmd)
subprocess.run(cmd, shell=True)
def test_archive(input_location: str, archive: str, exe_filename: str, paq8l_version: str) -> None:
archive = set_archive_filename(archive, paq8l_version)
if platform == "win32":
cmd = [exe_filename, '-t', archive]
else:
cmd = "{} -t \"{}\" \"{}\"".format(exe_filename, archive, input_location)
print(cmd)
subprocess.run(cmd, shell=True)
def create_text_file(filelist: list, input_location: str, filename: str) -> str:
if filelist:
filelist_path = os.path.join(input_location, filename + '.txt')
print("Writing filelist.txt")
txt_file = open(filelist_path, 'w')
txt_file.write('\n')
for file in filelist:
if not os.path.isdir(file):
txt_file.write(file + '\n')
txt_file.close()
return '@' + filelist_path
else:
return input_location
def compression_args(args: argparse) -> str:
if not args.level:
level = '9'
else:
level = args.level
def get_output_location(args: argparse) -> str:
if not args.output:
output_location = args.input
else:
output_location = args.output
return output_location
def parse_action(args: argparse) -> tuple:
action = "compress"
action_finished = "Compression"
if args.test and not args.test_only:
action += " and test"
action_finished += " and testing"
if args.test_only:
action = "test"
action_finished = "Testing"
return action, action_finished
def single_threaded_compression(args: argparse, input_location: str, output_location: str, filename: str,
exe_filename: str, paq8l_version: str, compression_args: str) -> None:
filelist = []
action, _ = parse_action(args)
if os.path.isdir(input_location):
print("Listing files to {}".format(action))
for dir_, _, files in os.walk(input_location):
for fileName in sorted(files):
rel_file = os.path.join(fileName)
filelist.append(rel_file)
print(rel_file)
single_file = False
else:
print("file to {}".format(action), filename)
single_file = True
if (filelist or single_file) and not args.test_only:
filename = create_text_file(filelist, input_location, filename)
print("\nStarting compression...\n")
compress_file(filename, output_location, exe_filename, compression_args)
if args.test or args.test_only:
print("\nVerifying archive...\n")
test_archive(input_location, output_location, exe_filename)
def multithreaded_compression(args: argparse, input_location: str, output_location: str, filename: str,
exe_filename: str, compression_args: str) -> None:
if os.path.isdir(input_location):
print("Compressing each file separately")
pool = Pool()
for file in sorted(os.listdir(input_location)):
file_path = os.path.join(input_location, file)
pool.apply_async(compress_file, (file_path, file_path, exe_filename, compression_args))
pool.close()
pool.join()
else:
print("file to compress:", filename)
print("\nStarting compression...\n")
compress_file(input_location, output_location, exe_filename, compression_args)
if args.test or args.test_only:
print("\nVerifying archive is not yet implemented for multi-threaded individual file compression...\n")
def main() -> None:
parser = argparse.ArgumentParser(description='This script will generate a filelist file which will be used by '
'paq8l_v207 for compressing. It is also used for testing if you '
'use the -t or -to argument')
required = parser.add_argument_group('required arguments')
optional = parser.add_argument_group('optional arguments')
required.add_argument('-i', '--input', help="Input file or folder to compress. REQUIRED", required=True)
optional.add_argument('-v', '--version', help='Version of paq8l to use. Example: 207. Default is 207',
required=False, default='207')
optional.add_argument('-l', '--level', help="Compression level and switches. Example: 9a to compress using level 9 "
"and with the 'Adaptive learning rate' switch. Default is 9",
required=False, default='9')
optional.add_argument('-o', '--output', help="Output file to use. If not used, the archive will be saved at the "
"root of the parent folder where the file/folder to compress is "
"located. Do not provide extension", required=False, default=None)
optional.add_argument('-t', '--test', help="Optional flag to test the archive after compressing it. It is "
"recommended to use this option. Default is not to test",
required=False, action='store_true')
optional.add_argument('-to', '--test-only', help="Skip compression and just test the archive.",
required=False, action='store_true')
optional.add_argument('-r', '--remove', help="Deletes the filelist text file. Not recommended unless you plan not "
"to test the archive later. Default is not to remove", required=False,
default=False, action='store_true')
optional.add_argument('-mt', '--multithread', help="Compresses each file on a separate thread. This creates "
"individual archives with just one file", required=False,
default=False, action='store_true')
optional.add_argument('-n', '--nativecpu', help="Use the native CPU version. "
"These versions usually ends with _nativecpu and may provide "
"performane improvements on your machine over the generic version",
required=False,
default=False, action='store_true')
args = parser.parse_args()
# Variables:
exe_filename = "/home/stan/Documents/Dev/Fbroswer/paq8l"
compression_args = '-' + args.level
input_location = args.input
output_location = get_output_location(args)
filename = os.path.basename(input_location)
# Compression
if not args.multithread:
single_threaded_compression(args, input_location, output_location, filename,
exe_filename, compression_args)
else:
multithreaded_compression(args, input_location, output_location, filename,
exe_filename)
# Remove file list if not in multithreaded mode.
if args.remove and not args.multithread:
print("\nRemoving the filelist file")
os.remove(os.path.join(input_location, filename + '.txt'))
_, action_finished = parse_action(args)
print("\n{} finished!".format(action_finished))
if __name__ == "__main__":
main()

0
pypaqtest.paq Executable file
View File

43
readme.txt Executable file
View File

@ -0,0 +1,43 @@
paq8l is an open source (GPL) file compressor and archiver.
Last update Mar. 18, 2007 by Matt Mahoney.
Contents of paq8l.zip:
readme.txt - this file
paq8l.exe - Win32 (MinGW g++) executable for Pentium MMX and higher
paq-8l_intel.exe - Faster Win32 executable (compiled by Johan de Bock with Intel C++ from http://uclc.info )
paq8l - Linux executable (by Giorgio Tani, Mar. 18, 2007)
paq8l.cpp - C++ source code for all versions (Mar. 8, 2007)
paq7asm.asm - NASM/YASM assembler code for Pentium MMX or higher
paq7asmsse.asm - NASM/YASM for Pentium 4 (SSE2) or higher in 32 bit mode
paq7asm-x86_64.asm - YASM for x86-64 bit processors (tested in 64 bit Linux)
paq8l can be compiled for other processors without the assembler
code using the -DNOASM option (but it will run slower).
The assembler code is the same for all paq7/8 versions.
paq8l was written by Matt Mahoney (as paq8f) with improvements by
Bill Pettis (based on improvements by Alexander Ratushnyak and
Przemyslaw Skibinski in the paq8hp* series) and Serge Osnach (additional
models), and Andrew Paterson (Borland port). The assembler code was ported
to 64 bit by Matthew Fite and 32 bit SSE2 by wowtiger.
Other contributors to the PAQ project: Berto Destasio (tuning earlier
models for better compression), Johan de Bock (benchmarking, compiling
fast exectuables), David A. Scott (arithmetic coder improvements),
Fabio Buffoni (speed optimizations), Jason Schmidt (compression
improvements), Rudi Cilibrasi (text modeling), and Pavel L. Holoborodko
(PGM image modeling), and Jari Aalto (licensing/distribution).
This work would not be possible without the benchmarking efforts of
Marcus Hutter (Hutter prize), Werner Bergmans (maximumcompression.com)
Johan de Bock (UCLC), Berto Destasio (Emilcont benchmark), Stephan Busch
(Squeeze Chart), Leonid A. Broukhis (Calgary Corpus Challenge),
and Black Fox.
A similar (but rewritten) context mixing algorithm is used in
WinRK 3.0.3 (pwcm mode) by Malcolm Taylor. Modified versions of
PAQ (faster but less compression) are used in UDA and WinUDA by dwing,
and in xml-wrt by Przemyslaw Skibinski.

6
requirements.txt Executable file
View File

@ -0,0 +1,6 @@
# To ensure app dependencies are ported from your virtual environment/host machine into your container, run 'pip freeze > requirements.txt' in the terminal to overwrite this file
py7zr
rarfile
tqdm
PyQt5
mutagen

152
stanzip.py Executable file
View File

@ -0,0 +1,152 @@
# stanzip.py
# Description: Can Compress and Extract files using Various libraries more compression methods are going to be added
#
import os
import zipfile
import rarfile
import py7zr
import shutil
import argparse
import tqdm
from concurrent.futures import ThreadPoolExecutor
# File Extractor
class Extractor:
def zipviewer(self, source, destination):
if not os.path.exists(source):
print(f"Error: Archive file not found: {source}")
return
try:
pbar = tqdm.tqdm(total=100, desc="Extracting Archive file")
if not os.path.exists(destination):
os.makedirs(destination)
pbar.update(1)
if source.endswith(".zip"):
with zipfile.ZipFile(source, 'r') as zip_ref:
with tqdm.tqdm(total=len(zipfile.ZipFile(source).namelist()), desc="Extracting ZIP files") as pbar:
for filename in zip_ref.namelist():
zip_ref.extract(filename, destination)
pbar.update(1)
print(f"Extracted all files from {source} to {destination}")
elif source.endswith(".rar, .tar.gz, .tar.bz2, .tar.xz, .tar.zst"):
with rarfile.RarFile(source, 'r') as rar_ref:
with tqdm.tqdm(total=len(rar_ref.namelist()), desc="Extracting RAR files") as pbar:
for filename in rar_ref.namelist():
rar_ref.extractall(filename, destination)
pbar.update(1)
print(f"Extracted all files from {source} to {destination}")
elif source.endswith(".7z"):
with py7zr.SevenZipFile(source, 'r') as sevenzip_ref:
with tqdm.tqdm(total=len(sevenzip_ref.namelist()), desc="Extracting 7z files") as pbar:
for filename in sevenzip_ref.namelist():
sevenzip_ref.extractall(filename, destination)
pbar.update(1)
print(f"Extracted all files from {source} to {destination}")
else:
print(f"Unsupported file format: {source}")
except (zipfile.BadZipFile, zipfile.LargeZipFile) as e:
print(f"ZIP Extraction Error: {e}")
except (rarfile.RarFileException, rarfile.NotRARFile) as e:
print(f"RAR Extraction Error: {e}")
except py7zr.exceptions.SevenZipException as e:
print(f"7z Extraction Error: {e}")
except OSError as e:
print(f"Extraction Error: {e}")
# File Compressor
class Compressor:
def __init__(self):
pass
def _compress_folder(self, source_path, zip_file):
for root, _, files in os.walk(source_path):
for file in files:
file_path = os.path.join(root, file)
archive_path = os.path.relpath(file_path, source_path)
self._compress_file(file_path, zip_file, archive_path)
def _compress_file(self, file_path, zip_file, archive_path=None):
if not archive_path:
archive_path = os.path.basename(file_path)
if archive_path.endswith(".zip"):
return
with open(file_path, 'rb') as file:
for chunk in iter(lambda: file.read(1024 * 1024), b''):
zip_file.writestr(archive_path, chunk)
def compress(self, source_path, archive_name, archive_format="zip"):
if archive_format != "zip":
raise ValueError(f"Unsupported archive format: {archive_format}")
archive_path = os.path.join(os.path.dirname(source_path), f"{archive_name}.{archive_format}")
# Check if source path exists
if not os.path.exists(source_path):
print(f"Source path does not exist: {source_path}")
return
# Compress the source path
with zipfile.ZipFile(archive_path, 'w', zipfile.ZIP_DEFLATED) as zip_file:
if os.path.isdir(source_path):
print(f"Compressing folder: {source_path}")
self._compress_folder(source_path, zip_file)
else:
print(f"Compressing file: {source_path}")
self._compress_file(source_path, zip_file)
print(f"Compressed to: {archive_path}")
if os.path.isdir(source_path):
file_list = []
for root, _, files in os.walk(source_path):
for file in files:
file_path = os.path.join(root, file)
file_list.append(file_path)
# Use thread pool
with ThreadPoolExecutor(max_workers=4) as executor:
for file_path in file_list:
executor.submit(self._compress_file, file_path, zip_file)
executor.shutdown(wait=True)
def main():
parser = argparse.ArgumentParser(description="Compress or extract files")
subparsers = parser.add_subparsers(title="Command", dest="command")
# Subparser for extraction
extract_parser = subparsers.add_parser("extract")
extract_parser.add_argument("source", help="Path to the archive file")
extract_parser.add_argument("destination", help="Extraction directory")
# Subparser for compression
compress_parser = subparsers.add_parser("compress")
compress_parser.add_argument("source", help="Path to the file or folder to compress")
compress_parser.add_argument("archive_name", help="Name for the compressed archive")
compress_parser.add_argument("-f", "--format", choices=["zip"], default="zip", help="Archive format (default: zip)")
args = parser.parse_args()
if args.command == "extract":
extractor = Extractor()
extractor.zipviewer(args.source, args.destination)
if args.command == "compress":
compressor = Compressor()
compressor.compress(args.source, args.archive_name, args.format)
else:
print("Invalid command. Use 'extract' or 'compress'")
if __name__ == "__main__":
main()

BIN
test.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

168
test.py Executable file
View File

@ -0,0 +1,168 @@
import pretty_midi
import random
import tkinter as tk
from tkinter import ttk, filedialog
import pygame
import pypianoroll # type: ignore
from icecream import ic # type: ignore
class midgen:
def __init__(self, status_label: ttk.Label):
self.status_label = status_label
self.scales = self.scales()
def scales(self):
scales = {
"Major": [0, 2, 4, 5, 7, 9, 11],
"Minor": [0, 2, 3, 5, 7, 8, 10],
"Pentatonic": [0, 2, 4, 7, 9],
"Blues": [0, 3, 5, 6, 7, 10],
"Whole Tone": [0, 2, 4, 6, 8, 10],
"Chromatic": [i for i in range(12)],
"Octatonic": [0, 1, 3, 4, 6, 7, 9, 10],
"Harmonic Minor": [0, 2, 3, 5, 7, 8, 11],
"Melodic Minor": [0, 2, 3, 5, 7, 9, 11],
"Dorian": [0, 2, 3, 5, 7, 9, 10],
"Phrygian": [0, 1, 3, 5, 7, 8, 10],
"Lydian": [0, 2, 4, 6, 7, 9, 11],
"Mixolydian": [0, 2, 4, 5, 7, 9, 10],
"Locrian": [0, 1, 3, 5, 6, 8, 10],
"Diminished": [0, 2, 3, 5, 6, 8, 9, 11],
"Whole Half Diminished": [0, 2, 3, 5, 6, 8, 9, 11],
"Arabian": [0, 2, 4, 5, 6, 8, 10],
"Hungarian Minor": [0, 2, 3, 6, 7, 8, 11],
"Enigmatic": [0, 1, 4, 6, 8, 10, 11],
"Neapolitan Major": [0, 1, 3, 5, 7, 9, 11],
"Neapolitan Minor": [0, 1, 3, 5, 7, 8, 11],
"Bluesy": [0, 3, 5, 6, 7, 10],
"Hawaiian": [0, 2, 3, 7, 9],
"Japanese": [0, 1, 5, 7, 8],
"Chinese": [0, 4, 6, 7, 11],
"Gypsy": [0, 2, 3, 6, 7, 8, 10],
"Hirojoshi": [0, 2, 3, 7, 8],
"In Sen": [0, 1, 5, 7, 10],
"Iwato": [0, 1, 5, 6, 10],
"Kumoi": [0, 2, 3, 7, 9],
"Pelog": [0, 1, 3, 7, 8],
"Ryukyu": [0, 4, 5, 7, 11],
"Spanish": [0, 1, 3, 4, 5, 6, 8, 10],
"Todi": [0, 1, 3, 6, 7, 8, 11],
"Yo": [0, 2, 5, 7, 9]
}
return scales
def generate_midi(self):
self.status_label.config(text='Generating MIDI...')
try:
midi = pretty_midi.PrettyMIDI()
instrument = pretty_midi.Instrument(0)
scale = random.choice(list(self.scales.keys()))
scale_notes = self.scales[scale]
ic(f"Using scale: {scale}")
ic(f"Using notes: {scale_notes}")
for start, end in zip(range(0, 100, 10), range(10, 110, 10)):
note = pretty_midi.Note(
velocity=100, pitch=random.choice(scale_notes),
start=start, end=end
)
instrument.notes.append(note)
midi.instruments.append(instrument)
filepath = filedialog.asksaveasfilename(defaultextension='.mid')
if filepath:
midi.write(filepath)
track = pypianoroll.Multitrack(filepath)
track.plot()
self.status_label.config(text='MIDI generated successfully!')
except Exception as e:
self.status_label.config(text=f"Error generating MIDI: {e}")
class MidPlay:
"""A class to handle MIDI file playback."""
def __init__(self):
self.playlist = []
self.current_midi = None
self.playing = False
pygame.mixer.init()
def load_midi(self, filepath: str) -> None:
try:
self.current_midi = pretty_midi.PrettyMIDI(filepath)
pygame.mixer.music.load(filepath)
except Exception as e:
print(f"Error loading MIDI: {e}")
def add_to_playlist(self, filepath: str) -> None:
"""Adds a MIDI file to the playlist.
Args:
filepath: The path to the MIDI file.
"""
self.playlist.append(filepath)
def clear_playlist(self) -> None:
"""Clears the playlist."""
self.playlist = []
def play_midi(self) -> None:
"""Starts or resumes playback of the current MIDI file."""
if self.current_midi:
self.current_midi.instruments[0].synthesize()
pygame.mixer.music.play()
self.playing = True
else:
print("No MIDI file loaded")
def pause(self) -> None:
"""Pauses playback."""
pygame.mixer.music.pause()
self.playing = False
def stop(self) -> None:
"""Stops playback."""
pygame.mixer.music.stop()
self.playing = False
class UserInterface:
def __init__(self):
self.root = tk.Tk()
self.root.title("MIDI Generator")
self.root.geometry("400x200")
self.root.resizable(True, True)
self.status_label = ttk.Label(self.root, text="")
self.status_label.pack()
self.midi_generator = midgen(self.status_label)
self.midi_player = MidPlay()
self.filepath = None
self.midi = None
self.generate_button = ttk.Button(self.root, text="Generate MIDI", command=self.midi_generator.generate_midi)
self.generate_button.pack()
self.load_button = ttk.Button(self.root, text="Load MIDI", command=lambda: self.midi_player.load_midi(self.filepath))
self.load_button.pack()
self.play_button = ttk.Button(self.root, text="Play MIDI", command=lambda: self.midi_player.play_midi())
self.play_button.pack()
self.exit_button = ttk.Button(self.root, text="Exit", command=self.root.quit)
self.exit_button.pack()
window = tk.Tk()
window.title("MIDI Generator")
self.root.mainloop()
if __name__ == "__main__":
ui = UserInterface()

37
test_Fbrowser.py Executable file
View File

@ -0,0 +1,37 @@
import unittest
from unittest.mock import MagicMock
from PyQt5.QtWidgets import QApplication
from fbrowser import SampleMusicBrowser
class TestSampleMusicBrowser(unittest.TestCase):
def setUp(self):
self.app = QApplication([])
self.browser = SampleMusicBrowser()
def tearDown(self):
self.app.quit()
def test_player_error(self):
# Mock QMediaPlayer and set error code
self.browser.player.error = MagicMock(return_value=1)
self.browser.player.errorString = MagicMock(return_value="Test Error")
self.browser.player_error(1)
# Assert that the error message is printed
self.assertIn("An error occurred: Code:1 Test Error", self.browser.console_output)
def test_player_media_status_changed(self):
# Mock QMediaPlayer and set media status
self.browser.player_media_status_changed(2)
# Assert that the media status is printed
self.assertIn("Media Status: 2", self.browser.console_output)
def test_play_file(self):
# Mock QFileSystemModel and set file path
self.browser.list_model.filePath = MagicMock(return_value="/path/to/file.mp3")
# Call play_file method
self.browser.play_file(None)
# Assert that the player is playing the correct media
self.assertEqual(self.browser.playlist.media(0).canonicalUrl().toString(), "file:///path/to/file.mp3")
if __name__ == '__main__':
unittest.main()

159
testmidi.py Normal file
View File

@ -0,0 +1,159 @@
import sys
import os
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QFileSystemModel, QTreeView, QLabel, QComboBox
from PyQt5.QtCore import QDir, Qt, QThread, pyqtSignal
import mido
import pygame
import numpy as np
from mingus.midi import fluidsynth
from mingus.containers import Note
import soundfile as sf
class MidiPlayerThread(QThread):
update_signal = pyqtSignal(str)
def __init__(self, file_path):
super().__init__()
self.file_path = file_path
self.playing = True
def run(self):
midi_file = mido.MidiFile(self.file_path)
for msg in midi_file.play():
if not self.playing:
break
if not msg.is_meta:
if msg.type == 'note_on':
fluidsynth.play_Note(Note(msg.note), msg.channel, msg.velocity)
elif msg.type == 'note_off':
fluidsynth.stop_Note(Note(msg.note), msg.channel)
elif msg.type == 'control_change':
fluidsynth.control_change(msg.channel, msg.control, msg.value)
self.update_signal.emit(f"Playing: {msg}")
def stop(self):
self.playing = False
class AudioPlayerThread(QThread):
update_signal = pyqtSignal(str)
def __init__(self, file_path):
super().__init__()
self.file_path = file_path
self.playing = True
def run(self):
pygame.mixer.music.load(self.file_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy() and self.playing:
pygame.time.Clock().tick(10)
self.update_signal.emit(f"Playing audio: {pygame.mixer.music.get_pos() / 1000:.2f} seconds")
def stop(self):
self.playing = False
pygame.mixer.music.stop()
class MidiPlayer(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("MIDI Player and Audio File Browser")
self.setGeometry(100, 100, 800, 600)
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.layout = QHBoxLayout(self.central_widget)
# File Browser
self.model = QFileSystemModel()
self.model.setRootPath(QDir.rootPath())
self.model.setNameFilters(["*.mid", "*.midi", "*.mp3", "*.wav", "*.sf2"])
self.model.setNameFilterDisables(False)
self.tree = QTreeView()
self.tree.setModel(self.model)
self.tree.setRootIndex(self.model.index(QDir.homePath()))
self.tree.setColumnWidth(0, 250)
self.tree.setAnimated(False)
self.tree.setIndentation(20)
self.tree.setSortingEnabled(True)
self.tree.setWindowTitle("File Browser")
self.tree.clicked.connect(self.on_file_clicked)
# Player controls
self.player_widget = QWidget()
self.player_layout = QVBoxLayout(self.player_widget)
self.file_label = QLabel("No file selected")
self.player_layout.addWidget(self.file_label)
self.play_button = QPushButton("Play")
self.play_button.clicked.connect(self.play_file)
self.player_layout.addWidget(self.play_button)
self.stop_button = QPushButton("Stop")
self.stop_button.clicked.connect(self.stop_file)
self.player_layout.addWidget(self.stop_button)
self.soundfont_combo = QComboBox()
self.soundfont_combo.currentIndexChanged.connect(self.change_soundfont)
self.player_layout.addWidget(self.soundfont_combo)
self.status_label = QLabel("")
self.player_layout.addWidget(self.status_label)
# Add widgets to main layout
self.layout.addWidget(self.tree)
self.layout.addWidget(self.player_widget)
# Initialize pygame mixer
pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=1024)
# Initialize FluidSynth
fluidsynth.init(sf2="/path/to/default/soundfont.sf2") # Adjust this path as needed
self.player_thread = None
def on_file_clicked(self, index):
file_path = self.model.filePath(index)
self.file_label.setText(os.path.basename(file_path))
if file_path.lower().endswith('.sf2'):
self.load_soundfont(file_path)
def load_soundfont(self, sf2_path):
try:
fluidsynth.init(sf2=sf2_path)
self.soundfont_combo.clear()
self.soundfont_combo.addItems([f"Instrument {i}" for i in range(128)]) # MIDI has 128 standard instruments
except Exception as e:
print(f"Error loading soundfont: {e}")
def change_soundfont(self, index):
fluidsynth.set_instrument(0, index) # Set instrument for channel 0
def play_file(self):
file_path = self.model.filePath(self.tree.currentIndex())
if file_path.lower().endswith(('.mid', '.midi')):
self.player_thread = MidiPlayerThread(file_path)
elif file_path.lower().endswith(('.mp3', '.wav')):
self.player_thread = AudioPlayerThread(file_path)
else:
return
self.player_thread.update_signal.connect(self.update_status)
self.player_thread.start()
def stop_file(self):
if self.player_thread and self.player_thread.isRunning():
self.player_thread.stop()
self.player_thread.wait()
pygame.mixer.stop()
fluidsynth.stop_everything()
def update_status(self, status):
self.status_label.setText(status)
if __name__ == "__main__":
app = QApplication(sys.argv)
player = MidiPlayer()
player.show()
sys.exit(app.exec_())