Skip to content

Commit

Permalink
Merge pull request #24 from e-lie/http_dl_improvment
Browse files Browse the repository at this point in the history
Fix samples download and prepare for future asset collection download
  • Loading branch information
e-lie authored Nov 9, 2024
2 parents 66f86b4 + d6d32ae commit 0600de5
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 222 deletions.
4 changes: 1 addition & 3 deletions FoxDotEditor/FoxDotEditor/SampleChart.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
from .tkimport import *
from .Format import *
from renardo_lib.Settings import *
from renardo_lib.runtime import spack_manager
from renardo_gatherer.samples_download import nonalpha
from renardo_gatherer import SAMPLES_DIR_PATH
from renardo_gatherer.collections import nonalpha, SAMPLES_DIR_PATH

try:
from playsound import playsound
Expand Down
4 changes: 2 additions & 2 deletions FoxDotEditor/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
author_email='eliegavoty@free.fr',
license='cc-by-sa-4.0',
url='http://foxdot.org/',
packages=['FoxDotEditor', 'FoxDotEditor.Settings'],
packages=['FoxDotEditor'],
long_description=long_description,
long_description_content_type="text/markdown",
entry_points={'gui_scripts': ['FoxDotEditor = FoxDotEditor.__init__:main']},
Expand All @@ -22,6 +22,6 @@
install_requires=[
'renardo-lib==0.9.13.dev3',
'psutil',
'playsound',
'ttkbootstrap==1.10.1',
],
)
2 changes: 0 additions & 2 deletions renardo/renardo/RenardoApp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from renardo.supercollider_mgt.sclang_instances_mgt import SupercolliderInstance
from .pulsar_mgt import PulsarInstance
from .RenardoTUI import RenardoTUI
from renardo_gatherer.samples_download import SPackManager

import argparse
import time
Expand All @@ -11,7 +10,6 @@ class RenardoApp:

def __init__(self):
self.sc_instance = None
self.spack_manager = SPackManager()
self.args = RenardoApp.parse_args()
self.sc_instance = SupercolliderInstance()
self.pulsar_instance = PulsarInstance()
Expand Down
14 changes: 6 additions & 8 deletions renardo/renardo/RenardoTUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from textual.binding import Binding
from textual.css.query import NoMatches
from renardo.supercollider_mgt.sc_classes_files import is_renardo_sc_classes_initialized, write_sc_renardo_files_in_user_config
from renardo_gatherer.collections import is_default_spack_initialized, download_default_sample_pack
from textual.containers import Horizontal, Vertical, Center, Grid
from textual.widgets import (
Header,
Expand Down Expand Up @@ -39,7 +40,7 @@ def __init__(self, renardo_app, *args, **kwargs):
self.right_pane_mode = self.get_right_pane_mode()
self.right_pane_maximized = None # maximization feature TODO ?
self.renardo_sc_class_initialized = is_renardo_sc_classes_initialized()
self.base_sample_pack_downloaded = self.renardo_app.spack_manager.is_default_spack_initialized()
self.base_sample_pack_downloaded = is_default_spack_initialized()
self.sc_backend_started = self.test_sclang_connection()
self.supercollider_found = self.renardo_app.sc_instance.supercollider_ready
self.pulsar_found = self.renardo_app.pulsar_instance.pulsar_ready
Expand Down Expand Up @@ -68,7 +69,7 @@ def compose(self) -> ComposeResult:

def update_app_state(self):
self.renardo_sc_class_initialized = is_renardo_sc_classes_initialized()
self.base_sample_pack_downloaded = self.renardo_app.spack_manager.is_default_spack_initialized()
self.base_sample_pack_downloaded = is_default_spack_initialized()
self.sc_backend_started = self.test_sclang_connection()
self.supercollider_found = self.renardo_app.sc_instance.supercollider_ready
self.pulsar_found = self.renardo_app.pulsar_instance.pulsar_ready
Expand Down Expand Up @@ -129,16 +130,13 @@ def quit(self):
# self.renardo_app.args.boot = True if event.radio_set.pressed_index == 1 else False





################ Background Jobs

@work(exclusive=True, thread=True)
def dl_samples_background(self) -> None:
log_output_widget = self.query_one("#spack-dl-log-output", Log)
self.renardo_app.spack_manager.set_logger(log_output_widget)
self.renardo_app.spack_manager.init_default_spack()
log_output_widget = self.query_one("#collection-dl-log-output", Log)
#log_output_widget = None
download_default_sample_pack(logger=log_output_widget)
self.update_app_state()

@work(exclusive=True, thread=True)
Expand Down
2 changes: 1 addition & 1 deletion renardo/renardo/widgets/RightPane.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def compose(self) -> ComposeResult:
if not self.app.base_sample_pack_downloaded:
yield Label("Default samples pack needs to be downloaded")
yield Button("Download renardo default samples pack", id="dl-renardo-samples-btn")
yield Log(id="spack-dl-log-output")
yield Log(id="collection-dl-log-output")
else:
yield Label("The default sample pack is downloaded")

Expand Down
2 changes: 1 addition & 1 deletion renardo/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
'FoxDotEditor==0.9.13.dev3',
'renardo_gatherer==0.1.3',
'psutil',
'textual',
'textual==0.79.1',
],
entry_points={
'console_scripts': [
Expand Down
2 changes: 1 addition & 1 deletion renardo_gatherer/renardo_gatherer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from renardo_gatherer.config_dir import SAMPLES_DIR_PATH
from renardo_gatherer.samples_download import DEFAULT_SAMPLES_PACK_NAME
from renardo_gatherer.collections import DEFAULT_SAMPLES_PACK_NAME


def main():
Expand Down
157 changes: 157 additions & 0 deletions renardo_gatherer/renardo_gatherer/collections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import os
import requests
import time
from datetime import datetime
from renardo_gatherer.config_dir import SAMPLES_DIR_PATH
from urllib.parse import urljoin, urlparse
from concurrent.futures import ThreadPoolExecutor, as_completed

SAMPLES_DOWNLOAD_SERVER = 'https://collections.renardo.org/samples'
# DEFAULT_SAMPLES_PACK_NAME = '0_foxdot_default_testing'
DEFAULT_SAMPLES_PACK_NAME = '0_foxdot_default'
LOOP_SUBDIR = '_loop_'


nonalpha = {"&": "ampersand",
"*": "asterix",
"@": "at",
"\\": "backslash",
"|": "bar",
"^": "caret",
":": "colon",
"$": "dollar",
"=": "equals",
"!": "exclamation",
"/": "forwardslash",
"#": "hash",
"-": "hyphen",
"<": "lessthan",
"%": "percent",
"+": "plus",
"?": "question",
";": "semicolon",
"~": "tilde",
",": "comma",
"0": "0",
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9"}


def ensure_renardo_samples_directory():
if not SAMPLES_DIR_PATH.exists():
SAMPLES_DIR_PATH.mkdir(parents=True, exist_ok=True)

def download_file_in_pool(url, dest_path, retries=5, delay=1, logger=None):
filename = os.path.basename(urlparse(url).path)
for attempt in range(retries):
try:
response = requests.get(url, stream=True)
response.raise_for_status()
with open(dest_path, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
if logger:
logger.write_line(f"Downloaded {filename} to {dest_path}")
return True
except requests.RequestException as e:
if logger:
logger.write_line(f"Error downloading {url}: {e}")
if attempt < retries - 1:
if logger:
logger.write_line(f"Retrying ({attempt + 1}/{retries})...")
time.sleep(delay)
else:
if logger:
logger.write_line(f"Failed to download {url} after {retries} attempts")
return False


def download_files_from_json_index_concurrent(json_url, download_dir, max_workers=3, logger=None):
def download_json_index_from_url(url, logger=logger):
try:
response = requests.get(url)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.write_line(f"Error downloading collection JSON index: {e}")
return None

def process_node(node, base_url="", current_dir=""):
tasks = []
if "url" in node:
# Full file download URL
file_url = urljoin(base_url, node["url"])
# Full local path including any subdirectory structure
file_path = os.path.join(download_dir, current_dir, os.path.basename(node["path"]))
tasks.append((file_url, file_path))
os.makedirs(os.path.dirname(file_path), exist_ok=True)
if "children" in node:
for child in node["children"]:
# For each child, pass its directory structure down the chain
child_dir = os.path.join(current_dir, os.path.basename(node["path"]))
tasks.extend(process_node(child, base_url, child_dir))
os.makedirs(child_dir, exist_ok=True)
return tasks

# Ensure the download directory exists
os.makedirs(download_dir, exist_ok=True)

# Download JSON content from URL
file_tree = download_json_index_from_url(json_url)

# Generate list of all files to download
download_tasks = process_node(file_tree, json_url)

# Use ThreadPoolExecutor to download files concurrently
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [
executor.submit(download_file_in_pool, url, path, 5, 1, logger)
for url, path in download_tasks
]
for future in as_completed(futures):
# Handle each completed download here if needed (e.g., check for success)
result = future.result()
if not result:
if logger:
logger.write_line("A download failed.")


def download_default_sample_pack(logger=None):

logger.write_line(f"Downloading Default Sample Pack {DEFAULT_SAMPLES_PACK_NAME} from {SAMPLES_DOWNLOAD_SERVER}\n")
download_files_from_json_index_concurrent(
json_url=f'{SAMPLES_DOWNLOAD_SERVER}/{DEFAULT_SAMPLES_PACK_NAME}/collection_index.json',
download_dir=SAMPLES_DIR_PATH,
logger=logger
)

try:
with open(SAMPLES_DIR_PATH / DEFAULT_SAMPLES_PACK_NAME / 'downloaded_at.txt', mode="w") as file:
file.write(str(datetime.now()))
except Exception as e:
print(e)


def is_default_spack_initialized():
return (SAMPLES_DIR_PATH / DEFAULT_SAMPLES_PACK_NAME / 'downloaded_at.txt').exists()

def sample_path_from_symbol(symbol, spack_path=SAMPLES_DIR_PATH/DEFAULT_SAMPLES_PACK_NAME):
""" Return the sample search directory for a symbol """
sample_path = None
if symbol.isalpha():
low_up_dirname = 'upper' if symbol.isupper() else 'lower'
sample_path = spack_path / symbol.lower() / low_up_dirname
elif symbol in nonalpha:
longname = nonalpha[symbol]
sample_path = spack_path / '_' / longname
return sample_path

def default_loop_path():
return SAMPLES_DIR_PATH/DEFAULT_SAMPLES_PACK_NAME/LOOP_SUBDIR
Loading

0 comments on commit 0600de5

Please sign in to comment.