updated repos to latest versions
This commit is contained in:
parent
18ca545440
commit
d78288b614
BIN
Fbroswer.tar.gz
Normal file
BIN
Fbroswer.tar.gz
Normal file
Binary file not shown.
205
Fbrowser.py
205
Fbrowser.py
@ -4,18 +4,30 @@
|
||||
# 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
|
||||
|
||||
from PyQt5.QtGui import QStandardItem , QStandardItemModel
|
||||
from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, 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 #QAbstractItemModel, QAbstractProxyModel, QModelIndex, QItemSelectionModel, QItemSelection, QItemSelectionRange, QItemSelectionModel, QItemSelection, QItemSelectionRange
|
||||
|
||||
|
||||
"""
|
||||
# Audio Format
|
||||
audio_format = QAudioFormat()
|
||||
audio_format.setSampleRate(44100)
|
||||
audio_format.setChannelCount(2)
|
||||
audio_format.setSampleSize(16)
|
||||
audio_format.setCodec('audio/pcm')
|
||||
audio_format.setByteOrder(QAudioFormat.LittleEndian)
|
||||
audio_format.setSampleType(QAudioFormat.SignedInt)
|
||||
# Audio Device Info
|
||||
device_info = QAudioDeviceInfo.defaultOutputDevice()
|
||||
if not device_info.isFormatSupported(audio_format):
|
||||
print('Raw audio format not supported by backend, cannot play audio.')
|
||||
audio_format = device_info.nearestFormat(audio_format)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# Sample Music Browser Main Class
|
||||
@ -23,7 +35,6 @@ class SampleMusicBrowser(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.organizer = organizer()
|
||||
self.extractor = extractor()
|
||||
self.file_model = QStandardItemModel()
|
||||
self.player = QMediaPlayer()
|
||||
self.playlist = QMediaPlaylist()
|
||||
@ -31,102 +42,68 @@ class SampleMusicBrowser(QWidget):
|
||||
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}")
|
||||
if error == QMediaPlayer.NoError:
|
||||
return
|
||||
print('Error: ' + self.player.errorString())
|
||||
|
||||
# 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()
|
||||
layout = QHBoxLayout()
|
||||
label = QLabel('Sample Music Browser')
|
||||
buttons_layout = QHBoxLayout()
|
||||
layout.addWidget(label)
|
||||
|
||||
|
||||
#self.midi_player = MidPlay()
|
||||
button = QPushButton('Exit')
|
||||
button.clicked.connect(self.show_exit_popup)
|
||||
layout.addWidget(button)
|
||||
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)
|
||||
|
||||
back_button = QPushButton('Back')
|
||||
back_button.clicked.connect(self.go_back_directory)
|
||||
layout.addWidget(back_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)
|
||||
|
||||
|
||||
play_button = QPushButton('Play')
|
||||
play_button.clicked.connect(self.player.play)
|
||||
layout.addWidget(play_button)
|
||||
pause_button = QPushButton('Pause')
|
||||
pause_button.clicked.connect(self.player.pause)
|
||||
layout.addWidget(pause_button)
|
||||
stop_button = QPushButton('Stop')
|
||||
stop_button.clicked.connect(self.player.stop)
|
||||
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)
|
||||
self.player.setVolume(50)
|
||||
volume_slider = QSlider(Qt.Horizontal)
|
||||
volume_slider.setRange(0, 100)
|
||||
@ -139,9 +116,6 @@ class SampleMusicBrowser(QWidget):
|
||||
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)))
|
||||
@ -165,45 +139,19 @@ class SampleMusicBrowser(QWidget):
|
||||
except Exception as e:
|
||||
print(f"Error Populating File Tree: {e}")
|
||||
|
||||
def closeEvent(self, event):
|
||||
|
||||
def show_exit_popup(self):
|
||||
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()
|
||||
sys.exit()
|
||||
|
||||
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}")
|
||||
index = self.file_filter_model.mapToSource(index)
|
||||
file_path = self.list_model.filePath(index)
|
||||
media = QMediaContent(QUrl.fromLocalFile(file_path))
|
||||
self.playlist.addMedia(media)
|
||||
self.player.play()
|
||||
|
||||
def player_state_changed(self, state):
|
||||
if state == QMediaPlayer.StoppedState:
|
||||
@ -235,39 +183,34 @@ class SampleMusicBrowser(QWidget):
|
||||
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))
|
||||
index = self.file_model.index(index)
|
||||
index = index.parent()
|
||||
index = self.file_filter_model.mapFromSource(index)
|
||||
self.folder_contents_view.setRootIndex(index)
|
||||
self.current_dir_label.setText(self.model.filePath(index))
|
||||
|
||||
def go_back_directory(self):
|
||||
index = self.folder_contents_view.rootIndex()
|
||||
index = self.file_filter_model.mapToSource(index)
|
||||
index = self.file_model.index(index)
|
||||
index = index.parent()
|
||||
index = self.file_filter_model.mapFromSource(index)
|
||||
self.folder_contents_view.setRootIndex(index)
|
||||
self.current_dir_label.setText(self.model.filePath(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))
|
||||
index = self.file_model.index(index)
|
||||
index = index.parent()
|
||||
index = self.file_filter_model.mapFromSource(index)
|
||||
self.folder_contents_view.setRootIndex(index)
|
||||
self.current_dir_label.setText(self.model.filePath(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_())
|
||||
BIN
Fbrowser.zip
Executable file
BIN
Fbrowser.zip
Executable file
Binary file not shown.
44
MidPlay.py
44
MidPlay.py
@ -1,25 +1,10 @@
|
||||
#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 mido
|
||||
import fluidsynth
|
||||
import sys
|
||||
import os
|
||||
from PyQt5.QtWidgets import (QApplication, QLabel, QListWidget, QFileDialog, QMessageBox, QWidget, QPushButton, QHBoxLayout,
|
||||
QVBoxLayout,
|
||||
@ -29,6 +14,18 @@ from PyQt5.QtCore import QTimer, Qt
|
||||
import threading
|
||||
import cProfile # profiler remove for production
|
||||
|
||||
#profiler remove for production
|
||||
def start_profiling():
|
||||
global pr
|
||||
pr = cProfile.Profile()
|
||||
pr.enable()
|
||||
|
||||
def stop_profiling():
|
||||
pr.disable()
|
||||
pr.dump_stats('midi_profile.out')
|
||||
|
||||
# The pygame.mixer.init() call is necessary to initialize the mixer module
|
||||
# before any sound can be played. The pygame.init() call is necessary maybe.
|
||||
pygame.mixer.init()
|
||||
pygame.init()
|
||||
|
||||
@ -51,10 +48,17 @@ class MidPlayGUI(QWidget):
|
||||
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()
|
||||
total_time = self.calculate_midi_duration(self.player.current_midi)
|
||||
progress = current_time / total_time * 100
|
||||
self.progress_bar.setValue(int(progress))
|
||||
|
||||
def calculate_midi_duration(self, midi_file):
|
||||
total_duration = 0
|
||||
for track in midi_file.tracks:
|
||||
track_duration = max([msg.time for msg in track]) if track else 0
|
||||
total_duration = max(total_duration, track_duration)
|
||||
return total_duration
|
||||
|
||||
def handle_song_end(self):
|
||||
if self.player.playing and not pygame.mixer.music.get_busy():
|
||||
self.player.next_song()
|
||||
@ -194,7 +198,7 @@ class MidPlay:
|
||||
def load_midi(self, filepath: str) -> None:
|
||||
def load():
|
||||
try:
|
||||
self.current_midi = pretty_midi.PrettyMIDI(filepath)
|
||||
self.current_midi = mido.MidiFile(filepath)
|
||||
pygame.mixer.music.load(filepath)
|
||||
except Exception as e:
|
||||
print(f"Error loading MIDI: {e}")
|
||||
@ -240,6 +244,7 @@ class MidPlay:
|
||||
# print("Debug: Filepath:", filepath) # debug line
|
||||
# print("Debug: Current MIDI:", self.current_midi) # debug line
|
||||
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
player_gui = MidPlayGUI()
|
||||
@ -252,4 +257,5 @@ if __name__ == '__main__':
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
break
|
||||
app.exec_()
|
||||
app.exec_()
|
||||
"""
|
||||
54
ScanOrg.py
54
ScanOrg.py
@ -9,13 +9,13 @@ 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
|
||||
from PyQt6.QtCore import Qt, QSortFilterProxyModel, pyqtSignal
|
||||
|
||||
# Directory Filter Proxy Model
|
||||
class DirectoryFilterProxyModel(QSortFilterProxyModel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setFilterCaseSensitivity(Qt.CaseInsensitive)
|
||||
self.setFilterCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
|
||||
self.setFilterKeyColumn(0)
|
||||
def filterAcceptsRow(self, source_row, source_parent):
|
||||
index = self.sourceModel().index(source_row, 0, source_parent)
|
||||
@ -25,7 +25,7 @@ class DirectoryFilterProxyModel(QSortFilterProxyModel):
|
||||
class FileFilterProxyModel(QSortFilterProxyModel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setFilterCaseSensitivity(Qt.CaseInsensitive)
|
||||
self.setFilterCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
|
||||
self.setFilterKeyColumn(0)
|
||||
self.allowed_extensions = ['.zip', '.mp3', '.wav', '.flac', '.mid', '.midi', '.aiff', '.aif', '.aifc', '.au', '.snd', '.wv', '.wma', '.m4a']
|
||||
|
||||
@ -77,44 +77,21 @@ class file_scanner:
|
||||
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:
|
||||
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"):
|
||||
if file_path.endswith(('.zip', '.rar', '.7z')):
|
||||
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:
|
||||
for filename in zip_ref.namelist():
|
||||
destination = os.path.join(extraction_directory, filename)
|
||||
zip_ref.extract(filename, extraction_directory)
|
||||
except (zipfile.BadZipFile, OSError, zipfile.LargeZipFile, zipfile.LargeZipFile) 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}")
|
||||
return
|
||||
|
||||
|
||||
class organizer:
|
||||
global metadata_queue
|
||||
@ -207,7 +184,4 @@ class organizer:
|
||||
print('Error: ' + file)
|
||||
if os.path.splitext(file)[1] == ('.mp3', '.wav', '.flac', '.m4a', '.wma', 'mid', '.midi'):
|
||||
self.organize_audio()
|
||||
audio = mutagen.File(file)
|
||||
|
||||
|
||||
|
||||
audio = mutagen.File(file)
|
||||
69
ScanOrg100.py
Normal file
69
ScanOrg100.py
Normal file
@ -0,0 +1,69 @@
|
||||
import os
|
||||
from PyQt6.QtCore import QThread, pyqtSignal
|
||||
|
||||
class FileScanner(QThread):
|
||||
items_found = pyqtSignal(list)
|
||||
scan_complete = pyqtSignal()
|
||||
|
||||
def __init__(self, path):
|
||||
super().__init__()
|
||||
self.path = path
|
||||
self.stop_requested = False
|
||||
self.allowed_extensions = {
|
||||
'.mid', '.midi', '.mp3', '.wav', '.ogg', '.flac', '.aac', '.m4a', '.wma',
|
||||
'.flp', '.als', '.logic', '.logicx', '.ptx', '.pts', '.cpr', '.rpp',
|
||||
'.reason', '.sng', '.ardour', '.bwproject'
|
||||
}
|
||||
|
||||
def run(self):
|
||||
self.scan_directory(self.path)
|
||||
self.scan_complete.emit()
|
||||
|
||||
def scan_directory(self, path):
|
||||
try:
|
||||
items = []
|
||||
with os.scandir(path) as entries:
|
||||
for entry in entries:
|
||||
if self.stop_requested:
|
||||
return
|
||||
if entry.is_dir():
|
||||
items.append((entry.path, True))
|
||||
elif entry.is_file() and entry.name.lower().endswith(tuple(self.allowed_extensions)):
|
||||
items.append((entry.path, False))
|
||||
self.items_found.emit(items)
|
||||
except PermissionError:
|
||||
print(f"Permission denied: {path}")
|
||||
except OSError as e:
|
||||
print(f"Error accessing {path}: {e}")
|
||||
|
||||
def stop(self):
|
||||
self.stop_requested = True
|
||||
|
||||
class Organizer:
|
||||
def __init__(self):
|
||||
self.file_list = []
|
||||
self.dir_list = []
|
||||
self.scanner = None
|
||||
|
||||
def start_scan(self, path):
|
||||
self.file_list.clear()
|
||||
self.dir_list.clear()
|
||||
self.scanner = FileScanner(path)
|
||||
self.scanner.items_found.connect(self.add_items)
|
||||
self.scanner.scan_complete.connect(self.scan_finished)
|
||||
self.scanner.start()
|
||||
|
||||
def add_items(self, items):
|
||||
for path, is_dir in items:
|
||||
if is_dir:
|
||||
self.dir_list.append(path)
|
||||
else:
|
||||
self.file_list.append(path)
|
||||
|
||||
def scan_finished(self):
|
||||
print(f"Scan complete. Found {len(self.dir_list)} directories and {len(self.file_list)} files.")
|
||||
|
||||
def stop_scan(self):
|
||||
if self.scanner:
|
||||
self.scanner.stop()
|
||||
self.scanner.wait()
|
||||
147
maintest.py
Normal file
147
maintest.py
Normal file
@ -0,0 +1,147 @@
|
||||
import warnings
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
|
||||
import sys
|
||||
import os
|
||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton,
|
||||
QTreeView, QLabel, QStyle, QFileDialog, QHBoxLayout, QLineEdit, QProgressBar)
|
||||
from PyQt6.QtCore import Qt, QDir, QTimer
|
||||
from PyQt6.QtGui import QStandardItemModel, QStandardItem
|
||||
from testmid import MidPlay
|
||||
from ScanOrg100 import Organizer
|
||||
from timer_m import Timer_Ui
|
||||
|
||||
class Fbrowser(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.midplay = None
|
||||
self.current_path = QDir.homePath()
|
||||
self.organizer = Organizer()
|
||||
self.init_ui()
|
||||
self.timer = Timer_Ui()
|
||||
|
||||
def init_ui(self):
|
||||
self.setWindowTitle('File Browser')
|
||||
central_widget = QWidget()
|
||||
layout = QVBoxLayout(central_widget)
|
||||
|
||||
# Address bar
|
||||
address_layout = QHBoxLayout()
|
||||
self.address_bar = QLineEdit(self.current_path)
|
||||
self.address_bar.returnPressed.connect(self.navigate_to_address)
|
||||
address_layout.addWidget(self.address_bar)
|
||||
|
||||
# Navigation buttons
|
||||
back_button = QPushButton('Back')
|
||||
back_button.clicked.connect(self.go_back)
|
||||
address_layout.addWidget(back_button)
|
||||
|
||||
layout.addLayout(address_layout)
|
||||
|
||||
# Add a progress bar
|
||||
self.progress_bar = QProgressBar()
|
||||
layout.addWidget(self.progress_bar)
|
||||
|
||||
# File view
|
||||
self.file_model = QStandardItemModel()
|
||||
self.file_tree = QTreeView()
|
||||
self.file_tree.setModel(self.file_model)
|
||||
self.file_tree.doubleClicked.connect(self.on_item_double_clicked)
|
||||
layout.addWidget(self.file_tree)
|
||||
|
||||
# MidPlay button
|
||||
open_midplay_button = QPushButton('Open MidPlay')
|
||||
open_midplay_button.clicked.connect(self.open_midplay)
|
||||
layout.addWidget(open_midplay_button)
|
||||
|
||||
self.setCentralWidget(central_widget)
|
||||
self.resize(800, 600)
|
||||
|
||||
# Timer Button
|
||||
self.timer_button = QPushButton('Start Timer')
|
||||
self.timer_button.clicked.connect(self.open_timer)
|
||||
layout.addWidget(self.timer_button)
|
||||
|
||||
|
||||
self.scan_current_directory()
|
||||
|
||||
def scan_current_directory(self):
|
||||
self.organizer.start_scan(self.current_path)
|
||||
self.progress_bar.setRange(0, 0) # Indeterminate progress
|
||||
self.organizer.scanner.scan_complete.connect(self.scan_finished)
|
||||
|
||||
def scan_finished(self):
|
||||
self.progress_bar.setRange(0, 100)
|
||||
self.progress_bar.setValue(100)
|
||||
self.update_file_tree()
|
||||
|
||||
def navigate_to_address(self):
|
||||
new_path = self.address_bar.text()
|
||||
if os.path.isdir(new_path):
|
||||
self.current_path = new_path
|
||||
self.scan_current_directory()
|
||||
else:
|
||||
self.address_bar.setText(self.current_path)
|
||||
|
||||
def update_file_tree(self):
|
||||
self.file_model.clear()
|
||||
self.file_model.setHorizontalHeaderLabels(['Name'])
|
||||
|
||||
root = self.file_model.invisibleRootItem()
|
||||
|
||||
# Add directories
|
||||
for path in self.organizer.dir_list:
|
||||
name = os.path.basename(path)
|
||||
item = QStandardItem(name)
|
||||
item.setData(path, Qt.ItemDataRole.UserRole)
|
||||
item.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon))
|
||||
root.appendRow(item)
|
||||
|
||||
# Add files
|
||||
for path in self.organizer.file_list:
|
||||
name = os.path.basename(path)
|
||||
item = QStandardItem(name)
|
||||
item.setData(path, Qt.ItemDataRole.UserRole)
|
||||
item.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon))
|
||||
root.appendRow(item)
|
||||
|
||||
self.file_tree.sortByColumn(0, Qt.SortOrder.AscendingOrder)
|
||||
|
||||
def on_item_double_clicked(self, index):
|
||||
item = self.file_model.itemFromIndex(index)
|
||||
path = item.data(Qt.ItemDataRole.UserRole)
|
||||
if os.path.isdir(path):
|
||||
self.current_path = path
|
||||
self.address_bar.setText(path)
|
||||
self.scan_current_directory()
|
||||
else:
|
||||
self.play_file(path)
|
||||
def playingcheck_and_stop(self):
|
||||
if self.midplay.is_playing():
|
||||
self.midplay.stop()
|
||||
def play_file(self, file_path):
|
||||
if not self.midplay:
|
||||
self.midplay = MidPlay()
|
||||
#self.midplay.showUI()
|
||||
self.midplay.add_to_playlist(file_path)
|
||||
|
||||
def go_back(self):
|
||||
parent_dir = os.path.dirname(self.current_path)
|
||||
if parent_dir != self.current_path:
|
||||
self.current_path = parent_dir
|
||||
self.address_bar.setText(parent_dir)
|
||||
self.scan_current_directory()
|
||||
|
||||
def open_midplay(self):
|
||||
if not self.midplay:
|
||||
self.midplay = MidPlay()
|
||||
self.midplay.showUI()
|
||||
|
||||
def open_timer(self):
|
||||
self.timer.showUI()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
ex = Fbrowser()
|
||||
ex.show()
|
||||
sys.exit(app.exec())
|
||||
19
requirements.txt
Executable file → Normal file
19
requirements.txt
Executable file → Normal file
@ -1,6 +1,13 @@
|
||||
# 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
|
||||
PyQT5
|
||||
PyQt5-tools
|
||||
PyQt6
|
||||
PyQt6-tools
|
||||
matplotlib
|
||||
rarfile
|
||||
py7zr
|
||||
pygame
|
||||
mido
|
||||
numpy
|
||||
crypto
|
||||
django
|
||||
pyFluidSynth
|
||||
2
test.py
2
test.py
@ -1,4 +1,4 @@
|
||||
import pretty_midi
|
||||
import mido as pretty_midi
|
||||
import random
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from fbrowser import SampleMusicBrowser
|
||||
from Fbrowser import SampleMusicBrowser
|
||||
|
||||
class TestSampleMusicBrowser(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
||||
136
testmid.py
Normal file
136
testmid.py
Normal file
@ -0,0 +1,136 @@
|
||||
import os
|
||||
import subprocess
|
||||
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel, QFileDialog, QSlider, QListWidget
|
||||
from PyQt6.QtCore import Qt, QUrl
|
||||
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
|
||||
|
||||
DEFAULT_SOUNDFONT = '/home/stan/Documents/Dev/Tests/MidPlay/SoundFonts/FinalFantasyVI.sf2'
|
||||
|
||||
class MidPlay(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.current_file = None
|
||||
self.current_soundfont = DEFAULT_SOUNDFONT
|
||||
self.process = None
|
||||
self.media_player = QMediaPlayer()
|
||||
self.audio_output = QAudioOutput()
|
||||
self.media_player.setAudioOutput(self.audio_output)
|
||||
self.playlist = []
|
||||
self.current_index = 0
|
||||
self.current_volume = 50
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout()
|
||||
|
||||
self.playlist_widget = QListWidget()
|
||||
self.playlist_widget.itemDoubleClicked.connect(self.play_selected)
|
||||
layout.addWidget(self.playlist_widget)
|
||||
|
||||
self.play_button = QPushButton("Play")
|
||||
self.play_button.clicked.connect(self.play)
|
||||
layout.addWidget(self.play_button)
|
||||
|
||||
self.stop_button = QPushButton("Stop")
|
||||
self.stop_button.clicked.connect(self.stop)
|
||||
layout.addWidget(self.stop_button)
|
||||
|
||||
self.next_button = QPushButton("Next")
|
||||
self.next_button.clicked.connect(self.play_next)
|
||||
layout.addWidget(self.next_button)
|
||||
|
||||
self.volume_slider = QSlider(Qt.Orientation.Horizontal)
|
||||
self.volume_slider.setRange(0, 100)
|
||||
self.volume_slider.setValue(self.current_volume)
|
||||
self.volume_slider.valueChanged.connect(self.set_volume)
|
||||
layout.addWidget(self.volume_slider)
|
||||
|
||||
self.status_label = QLabel("No file loaded")
|
||||
layout.addWidget(self.status_label)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def add_to_playlist(self, file_path):
|
||||
self.playlist.append(file_path)
|
||||
self.playlist_widget.addItem(os.path.basename(file_path))
|
||||
if not self.current_file:
|
||||
self.current_file = file_path
|
||||
self.play()
|
||||
|
||||
def play_selected(self, item):
|
||||
index = self.playlist_widget.row(item)
|
||||
self.current_index = index
|
||||
self.current_file = self.playlist[index]
|
||||
self.play()
|
||||
|
||||
def play(self):
|
||||
if not self.current_file:
|
||||
self.status_label.setText("No file selected")
|
||||
return
|
||||
|
||||
self.stop() # Stop any current playback
|
||||
|
||||
if self.current_file.lower().endswith(('.mid', '.midi')):
|
||||
self.play_midi()
|
||||
else:
|
||||
self.play_audio()
|
||||
|
||||
def play_midi(self):
|
||||
self.media_player.stop()
|
||||
command = [
|
||||
"fluidsynth",
|
||||
"-a", "pulseaudio",
|
||||
"-g", str(self.current_volume / 50),
|
||||
self.current_soundfont,
|
||||
self.current_file
|
||||
]
|
||||
self.process = subprocess.Popen(command)
|
||||
self.status_label.setText(f"Playing MIDI: {os.path.basename(self.current_file)}")
|
||||
|
||||
def play_audio(self):
|
||||
if self.process:
|
||||
self.process.terminate()
|
||||
self.process = None
|
||||
|
||||
self.media_player.setSource(QUrl.fromLocalFile(self.current_file))
|
||||
self.media_player.play()
|
||||
self.status_label.setText(f"Playing Audio: {os.path.basename(self.current_file)}")
|
||||
self.media_player.mediaStatusChanged.connect(self.handle_media_status_change)
|
||||
|
||||
def stop(self):
|
||||
if self.process:
|
||||
self.process.terminate()
|
||||
self.process = None
|
||||
self.media_player.stop()
|
||||
self.status_label.setText("Playback stopped")
|
||||
|
||||
def set_volume(self, value):
|
||||
self.current_volume = value
|
||||
self.audio_output.setVolume(value / 50)
|
||||
if self.process and self.current_file.lower().endswith(('.mid', '.midi')):
|
||||
self.update_midi_volume()
|
||||
|
||||
def update_midi_volume(self):
|
||||
pass
|
||||
|
||||
def play_next(self):
|
||||
if self.playlist:
|
||||
self.current_index = (self.current_index + 1) % len(self.playlist)
|
||||
self.current_file = self.playlist[self.current_index]
|
||||
self.play()
|
||||
|
||||
def handle_media_status_change(self, status):
|
||||
if status == QMediaPlayer.MediaStatus.EndOfMedia:
|
||||
self.play_next()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.stop()
|
||||
super().closeEvent(event)
|
||||
|
||||
def showUI(self):
|
||||
self.setWindowTitle("MidPlay")
|
||||
super().show()
|
||||
|
||||
def close(self):
|
||||
self.stop()
|
||||
super().close()
|
||||
141
timer_m.py
Normal file
141
timer_m.py
Normal file
@ -0,0 +1,141 @@
|
||||
import time
|
||||
import threading
|
||||
import subprocess
|
||||
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel, QHBoxLayout, QLineEdit
|
||||
from PyQt6.QtCore import QTimer, QUrl
|
||||
from PyQt6.QtMultimedia import QSoundEffect
|
||||
|
||||
class Timer:
|
||||
def __init__(self):
|
||||
self.remaining = 0
|
||||
self.running = False
|
||||
self.timer_thread = None
|
||||
|
||||
def set(self, hours, minutes, seconds):
|
||||
self.remaining = hours * 3600 + minutes * 60 + seconds
|
||||
|
||||
def start(self):
|
||||
if self.remaining > 0:
|
||||
self.running = True
|
||||
self.timer_thread = threading.Thread(target=self._run_timer)
|
||||
self.timer_thread.start()
|
||||
|
||||
def _run_timer(self):
|
||||
while self.running and self.remaining > 0:
|
||||
time.sleep(1)
|
||||
self.remaining -= 1
|
||||
if self.running:
|
||||
self._alarm()
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
if self.timer_thread:
|
||||
self.timer_thread.join()
|
||||
|
||||
def get_remaining_time(self):
|
||||
return self.remaining
|
||||
|
||||
def _alarm(self):
|
||||
print("Time's up!")
|
||||
try:
|
||||
for _ in range(3):
|
||||
subprocess.run(["aplay", "/usr/share/sounds/sound-icons/glass.wav"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
check=True)
|
||||
except subprocess.CalledProcessError:
|
||||
print("Error playing alarm sound")
|
||||
self._fallback_alarm()
|
||||
|
||||
def _fallback_alarm(self):
|
||||
sound = QSoundEffect()
|
||||
sound.setSource(QUrl.fromLocalFile("/usr/share/sounds/sound-icons/glass.wav"))
|
||||
sound.play()
|
||||
|
||||
|
||||
class Timer_Ui(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Timer")
|
||||
self.width = 400
|
||||
self.height = 200
|
||||
self.timer = Timer()
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
self.timer_label = QLabel('Timer: Not Set')
|
||||
layout.addWidget(self.timer_label)
|
||||
|
||||
timer_input_layout = QHBoxLayout()
|
||||
self.hours_input = QLineEdit()
|
||||
self.minutes_input = QLineEdit()
|
||||
self.seconds_input = QLineEdit()
|
||||
timer_input_layout.addWidget(QLabel('Hours:'))
|
||||
timer_input_layout.addWidget(self.hours_input)
|
||||
timer_input_layout.addWidget(QLabel('Minutes:'))
|
||||
timer_input_layout.addWidget(self.minutes_input)
|
||||
timer_input_layout.addWidget(QLabel('Seconds:'))
|
||||
timer_input_layout.addWidget(self.seconds_input)
|
||||
layout.addLayout(timer_input_layout)
|
||||
|
||||
self.set_timer_button = QPushButton('Set Timer')
|
||||
self.set_timer_button.clicked.connect(self.set_timer)
|
||||
layout.addWidget(self.set_timer_button)
|
||||
|
||||
self.start_timer_button = QPushButton('Start Timer')
|
||||
self.start_timer_button.clicked.connect(self.start_timer)
|
||||
layout.addWidget(self.start_timer_button)
|
||||
|
||||
self.stop_timer_button = QPushButton('Stop Timer')
|
||||
self.stop_timer_button.clicked.connect(self.stop_timer)
|
||||
layout.addWidget(self.stop_timer_button)
|
||||
|
||||
self.update_timer = QTimer()
|
||||
self.update_timer.timeout.connect(self.update_timer_display)
|
||||
self.update_timer.start(1000)
|
||||
|
||||
def set_timer(self):
|
||||
try:
|
||||
hours = int(self.hours_input.text() or 0)
|
||||
minutes = int(self.minutes_input.text() or 0)
|
||||
seconds = int(self.seconds_input.text() or 0)
|
||||
self.timer.set(hours, minutes, seconds)
|
||||
self.timer_label.setText(f'Timer: Set to {hours:02d}:{minutes:02d}:{seconds:02d}')
|
||||
except ValueError:
|
||||
self.timer_label.setText('Timer: Invalid input')
|
||||
|
||||
def start_timer(self):
|
||||
if self.timer.get_remaining_time() > 0:
|
||||
self.timer.start()
|
||||
else:
|
||||
self.timer_label.setText('Timer: Not set')
|
||||
|
||||
def stop_timer(self):
|
||||
self.timer.stop()
|
||||
self.timer_label.setText('Timer: Stopped')
|
||||
|
||||
def update_timer_display(self):
|
||||
if self.timer.running:
|
||||
remaining = self.timer.get_remaining_time()
|
||||
hours, remainder = divmod(remaining, 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
self.timer_label.setText(f'Timer: {hours:02d}:{minutes:02d}:{seconds:02d}')
|
||||
elif self.timer.get_remaining_time() == 0 and self.timer_label.text() != 'Timer: Not Set':
|
||||
self.timer_label.setText('Timer: Time\'s up!')
|
||||
|
||||
def showUI(self):
|
||||
self.setWindowTitle("Timer")
|
||||
self.setGeometry(100, 100, self.width, self.height)
|
||||
super().show()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
Timerui = Timer_Ui()
|
||||
Timerui.showUI()
|
||||
sys.exit(app.exec())
|
||||
Loading…
x
Reference in New Issue
Block a user