File size: 9,640 Bytes
a54f3a3
 
 
 
 
 
 
 
 
 
 
75c0e17
a54f3a3
75c0e17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a54f3a3
75c0e17
a54f3a3
75c0e17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a54f3a3
75c0e17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a54f3a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75c0e17
 
 
 
 
 
 
 
 
 
 
 
 
 
a54f3a3
 
 
 
 
 
 
75c0e17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a54f3a3
 
 
 
 
 
 
 
 
75c0e17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a54f3a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75c0e17
a54f3a3
 
75c0e17
a54f3a3
75c0e17
a54f3a3
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
"""Deploy the AnomalyMachine-50K demo to Hugging Face Spaces."""

import argparse
import os
import shutil
from pathlib import Path

from dotenv import load_dotenv
from huggingface_hub import HfApi, upload_folder


def ensure_hf_token(project_root: Path) -> str:
    """Load the Hugging Face token from environment variables."""
    # Try loading from project root .env file first
    env_file = project_root / ".env"
    env_loaded = False
    
    if env_file.exists():
        print(f"Loading .env from: {env_file}")
        load_dotenv(env_file, override=True)
        env_loaded = True
    else:
        # Fallback: try current directory
        print("Project root .env not found, trying current directory...")
        load_dotenv()
    
    # Also try spaces_demo/.env if it exists
    spaces_env = project_root / "spaces_demo" / ".env"
    if spaces_env.exists():
        print(f"Also loading .env from: {spaces_env}")
        load_dotenv(spaces_env, override=True)
    
    # Check environment variable
    token = os.getenv("HF_TOKEN")
    
    if not token:
        print("\n" + "="*60)
        print("ERROR: HF_TOKEN not found!")
        print("="*60)
        print(f"\nChecked locations:")
        print(f"  1. {env_file} {'βœ“' if env_file.exists() else 'βœ— (not found)'}")
        print(f"  2. {spaces_env} {'βœ“' if spaces_env.exists() else 'βœ— (not found)'}")
        print(f"  3. Environment variable: {'βœ“' if os.getenv('HF_TOKEN') else 'βœ— (not set)'}")
        print("\nTo fix this:")
        print("1. Create or edit .env file in the project root:")
        print(f"   {env_file}")
        print("2. Add your Hugging Face token (must start with 'hf_'):")
        print("   HF_TOKEN=hf_your_actual_token_here")
        print("\nOr set it as an environment variable:")
        print("   $env:HF_TOKEN='hf_your_token'  # PowerShell")
        print("   export HF_TOKEN='hf_your_token'  # Bash")
        print("\nGet your token from: https://huggingface.co/settings/tokens")
        print("="*60 + "\n")
        raise EnvironmentError("HF_TOKEN is not set in environment or .env file.")
    
    # Remove any quotes or whitespace
    token = token.strip().strip('"').strip("'").strip()
    
    # Show first few characters for debugging (without exposing full token)
    token_preview = token[:10] + "..." if len(token) > 10 else token[:len(token)]
    print(f"Token loaded: {token_preview} (length: {len(token)})")
    
    # Basic validation
    if not token.startswith("hf_"):
        print("\n" + "="*60)
        print("ERROR: Invalid token format!")
        print("="*60)
        print(f"\nYour token starts with: '{token[:5]}...'")
        print("But it should start with: 'hf_'")
        print("\nCommon issues:")
        print("1. Token might have extra spaces or quotes")
        print("2. Token might be incomplete")
        print("3. You might be using an old token format")
        print("\nGet a new token from: https://huggingface.co/settings/tokens")
        print("Make sure to copy the FULL token (it should start with 'hf_')")
        print("="*60 + "\n")
        raise ValueError("HF_TOKEN must start with 'hf_'. Please check your token.")
    
    return token


def copy_example_audio_files(

    source_output_dir: Path, target_examples_dir: Path, num_per_machine: int = 1

) -> None:
    """Copy example audio files from generated dataset to examples directory."""
    source_audio_dir = source_output_dir / "audio"
    if not source_audio_dir.exists():
        print(f"Warning: Source audio directory not found at {source_audio_dir}")
        return

    target_examples_dir.mkdir(parents=True, exist_ok=True)

    machine_types = ["fan", "pump", "compressor", "conveyor_belt", "electric_motor", "valve"]

    copied_count = 0
    for machine in machine_types:
        # Find anomalous examples for this machine
        pattern = f"{machine}_*_anomalous_*.wav"
        matching_files = list(source_audio_dir.glob(pattern))

        if matching_files:
            # Copy first N anomalous examples
            for audio_file in matching_files[:num_per_machine]:
                dest_path = target_examples_dir / audio_file.name
                shutil.copy2(audio_file, dest_path)
                copied_count += 1
                print(f"Copied: {audio_file.name}")

    print(f"Copied {copied_count} example audio files to {target_examples_dir}")


def deploy_space(

    spaces_demo_dir: Path,

    output_dir: Path,

    repo_id: str,

    token: str,

    private: bool = False,

) -> None:
    """Deploy the demo to Hugging Face Spaces."""
    api = HfApi(token=token)

    # Verify token works by checking user info
    try:
        user_info = api.whoami(token=token)
        print(f"Authenticated as: {user_info.get('name', 'Unknown')}")
    except Exception as e:
        print(f"\n❌ Authentication failed: {e}")
        print("\nTroubleshooting:")
        print("1. Your HF_TOKEN may be invalid or expired")
        print("2. Get a new token at: https://huggingface.co/settings/tokens")
        print("3. Make sure the token has 'write' permissions")
        print("4. Update your .env file with the new token")
        raise

    # Check if repo exists
    try:
        api.repo_info(repo_id, repo_type="space", token=token)
        print(f"Space '{repo_id}' already exists. Updating...")
    except Exception:
        print(f"Creating new Space '{repo_id}'...")
        try:
            api.create_repo(
                repo_id=repo_id,
                token=token,
                repo_type="space",
                exist_ok=True,
                space_sdk="gradio",
            )
            print(f"βœ… Space created successfully!")
        except Exception as create_error:
            print(f"\n❌ Error creating Space: {create_error}")
            print("\nTroubleshooting:")
            print("1. Verify your HF_TOKEN is valid and not expired")
            print("2. Check that the token has 'write' permissions")
            print("3. Get a new token at: https://huggingface.co/settings/tokens")
            username = repo_id.split('/')[0]
            print(f"4. Make sure your username '{username}' is correct")
            print(f"5. Check if you have permission to create Spaces under '{username}'")
            raise

    # Copy example audio files from generated dataset
    examples_dir = spaces_demo_dir / "examples"
    if output_dir.exists():
        print("Copying example audio files from generated dataset...")
        copy_example_audio_files(output_dir, examples_dir, num_per_machine=1)
    else:
        print(f"Warning: Output directory not found at {output_dir}. Skipping example files.")

    # Ensure README.md doesn't have emoji field (fix encoding issues)
    readme_path = spaces_demo_dir / "README.md"
    if readme_path.exists():
        print("Ensuring README.md has correct format...")
        with open(readme_path, "r", encoding="utf-8") as f:
            content = f.read()
        
        # Remove emoji line if it exists (to avoid encoding issues)
        lines = content.split("\n")
        filtered_lines = []
        for line in lines:
            if line.strip().startswith("emoji:"):
                print(f"  Removing emoji line: {line.strip()}")
                continue
            filtered_lines.append(line)
        
        # Write back without emoji
        content_fixed = "\n".join(filtered_lines)
        with open(readme_path, "w", encoding="utf-8", newline="\n") as f:
            f.write(content_fixed)
        print("README.md cleaned and saved with UTF-8 encoding.")

    # Upload all files in spaces_demo directory
    print(f"Uploading files from {spaces_demo_dir}...")
    upload_folder(
        folder_path=str(spaces_demo_dir),
        repo_id=repo_id,
        repo_type="space",
        token=token,
        ignore_patterns=[".git", "__pycache__", "*.pyc", ".env"],
    )

    space_url = f"https://huggingface.co/spaces/{repo_id}"
    print(f"\nβœ… Space deployed successfully!")
    print(f"🌐 Live at: {space_url}")


def parse_args() -> argparse.Namespace:
    """Parse command-line arguments."""
    parser = argparse.ArgumentParser(
        description="Deploy AnomalyMachine-50K demo to Hugging Face Spaces."
    )
    parser.add_argument(
        "--output_dir",
        type=str,
        default=os.path.join("..", "output"),
        help="Path to generated dataset output directory.",
    )
    parser.add_argument(
        "--repo_id",
        type=str,
        default="mandipgoswami/AnomalyMachine-Demo",
        help="Target Hugging Face Space repository id.",
    )
    parser.add_argument(
        "--private",
        action="store_true",
        help="Create the Space as private.",
    )
    return parser.parse_args()


def main() -> None:
    """Entry point for CLI execution."""
    args = parse_args()

    # Determine paths
    script_dir = Path(__file__).resolve().parent
    spaces_demo_dir = script_dir
    project_root = script_dir.parent
    output_dir = Path(args.output_dir).resolve()
    if not output_dir.is_absolute():
        output_dir = project_root / args.output_dir

    token = ensure_hf_token(project_root)

    deploy_space(
        spaces_demo_dir=spaces_demo_dir,
        output_dir=output_dir,
        repo_id=args.repo_id,
        token=token,
        private=args.private,
    )


if __name__ == "__main__":
    main()