11import os
2- import uuid
32import json
3+ import shutil
44from datetime import datetime , timezone
5- from typing import Callable , Dict , List , Optional
6- from src .api .api import Namespace , Progress
7-
5+ from typing import List , Optional
86
97class NamespaceService :
10- """Service class for managing namespaces using the filesystem."""
8+ """Manages namespaces on the filesystem."""
119
1210 def __init__ (self , base_dir : Optional [str ] = None ):
13- """
14- Initialize the NamespaceService.
15-
16- Args:
17- base_dir (str): Base directory for namespace storage. Defaults to `$HOME/easevoice_trainer_namespaces`.
18- """
19- if base_dir is None :
20- home_dir = os .path .expanduser ("~" )
21- base_dir = os .path .join (home_dir , "easevoice_trainer_namespaces" )
22- self .base_dir = base_dir
11+ self .base_dir = base_dir or os .path .join (os .path .expanduser ("~" ), "easevoice_trainer_namespaces" )
2312 os .makedirs (self .base_dir , exist_ok = True )
2413
25- def _namespace_metadata_path (self , namespace_id : str ) -> str :
26- """Get the path to the namespace metadata file."""
27- return os .path .join (self .base_dir , namespace_id , ".metadata.json" )
28-
29- def create_namespace (self , name : str ) -> Namespace :
30- """Create a new namespace."""
31- namespace_id = str (uuid .uuid4 ())
32- namespace_name = f"Namespace-{ namespace_id [:8 ]} "
33- created_at = datetime .now (timezone .utc )
34- home_path = os .path .join (self .base_dir , namespace_id )
35- os .makedirs (home_path , exist_ok = True )
36- os .makedirs (os .path .join (home_path , "voices" ), exist_ok = True )
14+ def _namespace_metadata_path (self , name : str ) -> str :
15+ return os .path .join (self .base_dir , name , ".metadata.json" )
3716
38- namespace = Namespace (
39- namespaceID = namespace_id ,
40- name = namespace_name if name == "" else name ,
41- createdAt = created_at ,
42- homePath = home_path ,
43- )
17+ def create_namespace (self , name : str ) -> dict :
18+ """Create a new namespace, raising FileExistsError if it already exists."""
19+ home_path = os .path .join (self .base_dir , name )
20+ if os .path .exists (home_path ):
21+ raise FileExistsError ("Namespace already exists" )
4422
23+ os .makedirs (os .path .join (home_path , "voices" ), exist_ok = True )
24+ namespace = {"name" : name , "createdAt" : datetime .now (timezone .utc ).isoformat (), "homePath" : home_path }
4525 self ._save_namespace_metadata (namespace )
46-
4726 return namespace
4827
49- def get_namespaces (self ) -> List [Namespace ]:
50- """Get all namespaces."""
28+ def get_namespaces (self ) -> List [dict ]:
29+ """List all namespaces."""
5130 namespaces = []
52- for namespace_id in os .listdir (self .base_dir ):
53- namespace_path = os .path .join (self .base_dir , namespace_id )
31+ for name in os .listdir (self .base_dir ):
32+ namespace_path = os .path .join (self .base_dir , name )
5433 if os .path .isdir (namespace_path ):
5534 try :
56- namespace = self ._load_namespace_metadata (namespace_id )
57- namespaces .append (namespace )
35+ namespaces .append (self ._load_namespace_metadata (name ))
5836 except FileNotFoundError :
59- pass # Skip invalid namespaces
37+ pass
6038 return namespaces
6139
62- def update_namespace (self , namespace_id : str , name : str ) -> Namespace :
63- """Update an existing namespace."""
64- namespace = self ._load_namespace_metadata (namespace_id )
65- namespace .name = name
40+ def update_namespace (self , old_name : str , new_name : str ) -> dict :
41+ """Rename a namespace, raising FileExistsError if the new name is taken."""
42+ old_home_path = os .path .join (self .base_dir , old_name )
43+ new_home_path = os .path .join (self .base_dir , new_name )
44+
45+ if not os .path .exists (old_home_path ):
46+ raise ValueError ("Namespace not found" )
47+
48+ if os .path .exists (new_home_path ):
49+ raise FileExistsError ("Target namespace already exists" )
50+
51+ namespace = self ._load_namespace_metadata (old_name )
52+
53+ os .rename (old_home_path , new_home_path )
54+
55+ namespace ["name" ] = new_name
56+ namespace ["homePath" ] = new_home_path
6657 self ._save_namespace_metadata (namespace )
6758 return namespace
6859
69- def delete_namespace (self , namespace_id : str ):
70- """Delete a namespace."""
71- namespace = self ._load_namespace_metadata (namespace_id )
72- self ._delete_directory (namespace .homePath )
73-
74- def _save_namespace_metadata (self , namespace : Namespace ):
75- """Save namespace metadata to a file."""
76- metadata_path = self ._namespace_metadata_path (namespace .namespaceID )
77- print (f"""Saving metadata to { metadata_path } """ )
78- with open (metadata_path , "w" ) as f :
79- json .dump (namespace .model_dump (), f , default = str )
80-
81- def _load_namespace_metadata (self , namespace_id : str ) -> Namespace :
82- """Load namespace metadata from a file."""
83- metadata_path = self ._namespace_metadata_path (namespace_id )
60+ def delete_namespace (self , name : str ):
61+ """Delete a namespace, raising ValueError if it does not exist."""
62+ home_path = os .path .join (self .base_dir , name )
63+ if not os .path .exists (home_path ):
64+ raise ValueError ("Namespace not found" )
65+
66+ shutil .rmtree (home_path )
67+
68+ def _save_namespace_metadata (self , namespace : dict ):
69+ with open (self ._namespace_metadata_path (namespace ["name" ]), "w" ) as f :
70+ json .dump (namespace , f )
71+
72+ def _load_namespace_metadata (self , name : str ) -> dict :
73+ metadata_path = self ._namespace_metadata_path (name )
8474 if not os .path .exists (metadata_path ):
85- raise ValueError (f "Namespace with ID { namespace_id } not found. " )
75+ raise ValueError ("Namespace not found" )
8676 with open (metadata_path , "r" ) as f :
87- data = json .load (f )
88- data ["createdAt" ] = datetime .fromisoformat (data ["createdAt" ])
89- return Namespace (** data )
90-
91- def _delete_directory (self , directory_path : str ):
92- """Delete a directory and its contents."""
93- if os .path .exists (directory_path ):
94- for root , dirs , files in os .walk (directory_path , topdown = False ):
95- for file in files :
96- os .remove (os .path .join (root , file ))
97- for dir in dirs :
98- os .rmdir (os .path .join (root , dir ))
99- os .rmdir (directory_path )
77+ return json .load (f )
0 commit comments