def compress_project(self, set_hash=True): Log("Compressing project..", "notice") # Clean out temp project if it exists Log("Cleaning out temp folder") Folder.delete(self.get_temp_dir()) # Extract cache if it exists if self.is_cached(): Tar.extract(filepath=self.get_cache_file(), destination=self.get_temp_dir().parent) else: Folder.create(self.get_temp_dir()) # Create folders if they dont exist self.create_required_folders(temp=True, clean=False) # Copy from 'extracted_songs' to 'temp' if not self.move_extracted_song_to_temp(): Log("Could not move project from 'extracted_songs' to 'temp'..", "warning") Log.press_enter() return False # Compress project to *.lof Tar.compress(folderpath=self.get_temp_dir(), destination=self.get_cache_file()) if set_hash: # Set new local hash Hash.set_project_hash(self, Hash.create_hash_from_project(self)) return True
def move_temp_to_extracted_songs(self): # Convert mp3's to wav's for location in ["Media", "Bounces"]: Log(f'Converting "{location}" mp3\'s to wav\'s') if not Audio.folder_to_wav(self.get_temp_dir() / location, self.get_root_dir() / location): return False # Copy over dummy.json Log(f'Copying over "{location}" dummy.json') if self.get_dummy_db(location, temp=True).exists(): File.recursive_overwrite( self.get_dummy_db(location, temp=True), self.get_dummy_db(location, temp=False), ) # Copy over the previous mixdowns Log("Copying over 'Mixdown' mp3's") Folder.copy(self.get_temp_dir() / "Mixdown", self.get_root_dir() / "Mixdown") # Copy over the song file Log("Copying over Studio One project file") File.recursive_overwrite(self.get_song_file(temp=True), self.get_song_file(temp=False)) File.recursive_overwrite( self.get_song_file(temp=True), self.get_song_file(version="original", temp=False)) return True
def initialize(): Database.cloud_id = Database.get_cloud_id() # Clean out temp folder Folder.clear_temp() # Download a fresh version of the database Database.refresh()
def delete_project(self): options = [] if self.is_local(): options.append("local") if self.is_remote(): options.append("remote") if self.is_local() and self.is_remote(): options.append("both") options.append("back") dialog = Dialog( title=f'Deleting Project "{self.entry.name}"', body=[ f'Would you like to delete the local or remote version?', f'\n', f'\n', f'Warning: This is not reversible!', ]) ans = dialog.get_mult_choice(options) if ans == "back": return True if ans == "local" or ans == "both": # Remove extracted folder and cached file File.delete(self.get_cache_file()) Folder.delete(self.get_root_dir()) # Remove hash from local db Hash.remove_project_hash(self) Menu.notice = f'Project "{self.entry.name}" deleted locally!' if ans == "remote" or ans == "both": if self.is_remote(): # Delete compressed file project_id = self.entry.data["id"] if project_id: Drive.delete(project_id) # Delete folder with scratch tracks and mixdowns folder_id = Drive.get_id(self.entry.name) if folder_id: Drive.delete(folder_id) # Remove entry from remote db self.entry.destroy() Menu.notice = f'Project "{self.entry.name}" deleted remotely!' if ans == "both": Menu.notice = f'Project "{self.entry.name}" completely deleted!' return True
def remove_dummy_files(self): # Remove any wav files that are 0 bytes in size for folder in FOLDERS: folder = self.get_root_dir() / folder wavs = Folder.ls_files(folder, "wav") for wav in wavs: if wav.exists() and wav.stat().st_size == 0: wav.unlink()
def clean_out_cache(): dialog = Dialog( title="Clean Out Cache", body=[ f'This will remove all pre-downloaded *.lof compressed files.', f'\n', f'\n', f'This should not harm any saved songs you have! This only', f'removes the pre-downloaded compressed cache.', f'\n', f'\n', ]) if dialog.confirm(): Folder.clear(f'compressed_songs') Menu.notice = "Cache is cleaned out!" return True
def to_wav(self, folderpath): folderpath = Path(folderpath) Folder.create(folderpath) mp3 = self.filepath wav = Path(f'{folderpath.absolute()}/{mp3.stem}.wav') stem, num, ext = File.split_name(wav.name) if wav.is_file(): # File already exists locally, do not do anything Log(f'File "{wav.name}" already exists, keeping local file.') return True Run.ffmpeg(args="-i", source=mp3.absolute(), destination=wav.absolute(), codec="-c:a pcm_s24le") return True
def folder_to_mp3(folderpath, destination): folderpath = Path(folderpath) destination = Path(destination) wavs = glob(f"{folderpath.absolute()}/*.wav") username_ignore = False Folder.create(destination) for wav in wavs: result = Audio(wav).to_mp3(folderpath=destination, username_ignore=username_ignore) if isinstance(result, dict): username_ignore = result["username_ignore"] else: if not result: return False return True
def extract_project(self): Log("Extracting project..") # Create required folders in 'extracted_songs' self.create_required_folders() # Extract cache to 'temp' folder Log("Extracting project") Tar.extract(self.get_cache_file(), self.get_temp_dir().parent) # Copy and convert from 'temp' to 'extracted_songs' if not self.move_temp_to_extracted_songs(): # If function fails, remove broken extracted project # to prevent issues trying again. Folder.delete(self.get_root_dir()) Log("Could not move project from 'temp' to 'extracted_songs'..", "warning") Log.press_enter() return False return True
def clear_all_local_projects(): result = Dialog("Clear Local Projects", [ f'This will clear all locally cached projects to save', f'space on your hard drive.', f'\n', f'This will NOT:', f'\n', f' - Delete any songs on the cloud', f'\n', f' - Delete any dirty projects on your computer', f'\n', f' that have not been uploaded yet', f'\n', f' - Remove any projects that have never been', f'\n', f' uploaded to the cloud yet', f'\n', ]).confirm() if result: for project in ProjectIndex.get_all_projects(): if not project.is_dirty() and project.is_remote(): # Just in case we have it locked project.remove_lock() # Remove extracted folder and cached file File.delete(project.get_cache_file()) Folder.delete(project.get_root_dir()) # Remove hash from local db Hash.remove_project_hash(project) Menu.notice = f'Local projects cleared!' else: Menu.notice = f'No local projects were cleared..' return True
def duplicate(self): if not self.dialog_copy_confirm(): return False new_name = self.dialog_copy_new_name() new_path = self.get_root_dir().parent / new_name Log(f'Duplicating "{self.entry.name}" to "{new_name}"..') Folder.copy(self.get_root_dir(), new_path) Log(f'Renaming song file', 'sub') File.rename(new_path / (self.get_song_file().name), new_path / f'{new_name}.song') if self.get_song_file(version="original").exists(): Log(f'Renaming *original song file', 'sub') File.rename( new_path / (self.get_song_file(version="original").name), new_path / f'{new_name}_original.song') Menu.notice = f'Created Project "{new_name}"!' return True
def dialog_copy_new_name(self): dialog = Dialog( title=f'Make Duplicate of "{self.entry.name}"', body="Please enter a new name for your duplicate project.") new_name = dialog.get_result("New Name").lower() project_names = [ x.name.lower() for x in Folder.ls_folders(self.get_root_dir().parent) ] if not new_name in project_names: return new_name Log("That project name already exists.. Please try again!") Log.press_enter() return self.dialog_copy_new_name()
def set_dummy_files(self): # double check that the dummy.json files exist self.create_dummy_json() for folder in FOLDERS: folder = self.get_root_dir() / folder dummy_json = folder / "dummy.json" data = File.get_json(dummy_json) wavs = Folder.ls_files(folder, "wav") # Lets find the max wav numbers for wav in wavs: file_stem, num, ext = File.split_name(wav) if not file_stem in data["max"]: data["max"][file_stem] = num else: if num > data["max"][file_stem]: data["max"][file_stem] = num # Now lets create the dummy files if they don't already exist for file_stem in data["max"]: num = data["max"][file_stem] for i in range(num): if i == 0: path = folder / f'{file_stem}.wav' else: path = folder / f'{file_stem}({i}).wav' path.touch() # Lets also get rid of the "dummy" key that is unused now if "dummy" in data: data.pop("dummy") # Lastly, lets update the dummy.json file File.set_json(dummy_json, data)
def exit(): Folder.clear_temp() # 0 = there was a problem # 1 = exit gracefully sys.exit(1)
def move_extracted_song_to_temp(self): # Make sure we don't have any dummy files self.remove_dummy_files() for location in ["Media", "Bounces"]: # Remove unused cached audio from location # Garbage collector for unused audio files mp3s = Folder.ls_files(self.get_temp_dir() / location, "mp3") wav_names = [ x.stem for x in Folder.ls_files(self.get_root_dir() / location, "wav") ] for mp3 in mp3s: if mp3.stem not in wav_names: File.delete(mp3) # Convert and move wavs to mp3s Log(f'Converting "{location}" wav\'s to mp3\'s') if not Audio.folder_to_mp3(self.get_root_dir() / location, self.get_temp_dir() / location): return False # Copy over dummy.json Log(f'Copying over "{location}" dummy.json') if self.get_dummy_db(location, temp=False).exists(): File.recursive_overwrite( self.get_dummy_db(location, temp=False), self.get_dummy_db(location, temp=True), ) # Upload scratch files and mixdowns to the cloud mp3s = Folder.ls_files(self.get_temp_dir() / "Media", "mp3", "Scratch*") mp3s.extend( Folder.ls_files(self.get_temp_dir() / "Media", "mp3", "SCRATCH*")) mp3s.extend(Folder.ls_files(self.get_root_dir() / "Mixdown", "mp3")) for mp3 in mp3s: mix_folder_id = Drive.get_id(self.entry.name) if not Drive.get_id(mp3.name, mix_folder_id): Log(f'Uploading "{mp3.name}" to the cloud', "sub") mp3_id = Drive.upload(filepath=mp3, mimeType=Drive.mimeType["mp3"], parent=mix_folder_id) audio_type = "Scratch" if mp3.parent.name == "Media" else "#MIXDOWN#" Slack.send_link( link_name= f'{audio_type} for {self.entry.name}, "{mp3.name}"', ID=mp3_id) else: Log(f'Audio file "{mp3.name}" already exists on the cloud!', "sub") # Copy over mixdowns to temp Log("Copying over 'Mixdown' mp3's") Folder.copy(self.get_root_dir() / "Mixdown", self.get_temp_dir() / "Mixdown") # Lastly, copy over the song file File.recursive_overwrite(self.get_song_file(temp=False), self.get_song_file(temp=True)) return True
def get_local_projects(): local = Folder.ls_folders(EXTRACTED) return [Project(x.name) for x in local]
import traceback,sys from src.Slack import Slack from src.Update import Update from src.Settings import Settings from src.TERMGUI.Log import Log from src.FileManagement.Folder import Folder from src.menus.menu_main import menu_main # MAIN FUNCTION if __name__ == '__main__': # Create file structure if it doesn't exist Folder.create("compressed_songs") Folder.create("extracted_songs") Folder.create("temp") Folder.create("templates") # Create settings file if it doesn't exist Settings.create() # Start main menu try: menu_main() except Exception as e: Log(traceback.format_exc(),"warning") # 0 = there was a problem # 1 = exit gracefully sys.exit(0)
def create_required_folders(self, temp=False, clean=True): # 'temp=True' will focus on the temp/<song> directory # 'temp=False' will focus on the extracted_songs/<song> directory # 'clean=True' will delete the folders first, then recreate them # Need to make sure these folders exist Log("Creating necessary folders") if temp: # Clean out temp dir if clean: Folder.delete(self.get_temp_dir()) Folder.create(self.get_temp_dir()) else: # Create new extracted dir, remove if exists if clean: Folder.delete(self.get_root_dir()) Folder.create(self.get_root_dir()) for location in ["Media", "Bounces", "Mixdown"]: if temp: Folder.create(self.get_temp_dir() / location) Folder.create(self.get_root_dir() / location) return True