File size: 8,120 Bytes
8c225b8
 
4e4dc42
8c225b8
 
 
 
 
4e4dc42
8c225b8
 
 
 
 
 
 
765d5d3
 
 
 
 
 
 
 
 
 
 
 
 
4e4dc42
765d5d3
 
 
 
8c225b8
 
 
 
 
 
 
 
 
8563cae
8c225b8
 
8563cae
8c225b8
 
 
 
4e4dc42
8c225b8
 
 
 
4e4dc42
8c225b8
 
 
 
 
 
 
 
 
 
 
 
765d5d3
8c225b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4e4dc42
8c225b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4e4dc42
 
 
8c225b8
4e4dc42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import tempfile
import gradio as gr
from huggingface_hub import create_repo, upload_folder, whoami
import logging
from pathlib import Path
from PIL import Image
from keylock import core as keylock_core
from repo_to_md.core import markdown_to_files

logging.basicConfig(
    level=logging.INFO, 
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)

def get_username_from_token(api_token: str):
    if not api_token:
        logger.info("API token is empty, cannot fetch username.")
        return ""
    try:
        logger.info("Attempting to fetch username from provided API token.")
        user_info = whoami(token=api_token)
        username = user_info.get('name')
        if username:
            logger.info(f"Successfully fetched username: {username}")
            return username
        else:
            logger.warning("Token was valid, but no username found in whoami() response.")
            return ""
    except Exception as e:
        logger.error(f"Failed to validate token and fetch username: {e}")
        return ""

def _get_api_token(ui_token_from_textbox=None):
    env_token = os.getenv('HF_TOKEN')
    if env_token: return env_token, None
    if ui_token_from_textbox: return ui_token_from_textbox, None
    return None, "Error: Hugging Face API token not provided."

def extract_markdown_from_image(repo_image: Image.Image, image_password: str):
    if not repo_image:
        return None, "Error: Please upload a repository image."
    password_to_use = image_password if image_password is not None else ""
    try:
        logger.info("Starting extraction of markdown from image...")
        result = keylock_core.extract_repo_from_image(repo_image, password_to_use)
        markdown_content = result.get('markdown')
        if not markdown_content:
            return None, "Error: Extraction succeeded, but no markdown content was found in the image."
        logger.info("Successfully extracted markdown content.")
        return markdown_content, result.get('status', "Extraction successful.")
    except Exception as e:
        logger.error(f"Failed to extract from image: {e}")
        return None, f"Error during extraction: {e}"

def parse_markdown_for_build(markdown_input):
    space_info = {"files": []}
    current_file_path = None; current_file_content_lines = []
    in_file_definition = False; in_code_block = False
    lines = markdown_input.strip().split("\n")
    for line_content_orig in lines:
        line_content_stripped = line_content_orig.strip()
        if line_content_stripped.startswith("### File:"):
            if current_file_path and in_file_definition:
                space_info["files"].append({"path": current_file_path, "content": "\n".join(current_file_content_lines)})
            current_file_path = line_content_stripped.replace("### File:", "").strip()
            current_file_content_lines = []; in_file_definition = True; in_code_block = False
            continue
        if not in_file_definition: continue
        if line_content_stripped.startswith("```"):
            in_code_block = not in_code_block
            continue
        current_file_content_lines.append(line_content_orig)
    if current_file_path and in_file_definition:
        space_info["files"].append({"path": current_file_path, "content": "\n".join(current_file_content_lines)})
    space_info["files"] = [f for f in space_info["files"] if f.get("path")]
    return space_info

def _determine_repo_id(ui_api_token_from_textbox, space_name_ui, owner_ui):
    if not space_name_ui: return None, "Error: Space Name cannot be empty."
    if "/" in space_name_ui: return None, "Error: Space Name should not contain '/'. Use the Owner field."
    final_owner = owner_ui
    if not final_owner:
        resolved_api_token, token_err = _get_api_token(ui_api_token_from_textbox)
        if token_err: return None, token_err
        try:
            user_info = whoami(token=resolved_api_token)
            final_owner = user_info.get('name')
        except Exception as e:
            return None, f"Error retrieving username from token: {e}. Please specify the Owner manually."
    if not final_owner: return None, "Error: Owner could not be determined."
    return f"{final_owner}/{space_name_ui}", None

def create_space(ui_api_token_from_textbox, space_name_ui, owner_ui, sdk_ui, markdown_input):
    repo_id, err = _determine_repo_id(ui_api_token_from_textbox, space_name_ui, owner_ui)
    if err: return err
    try:
        resolved_api_token, token_err = _get_api_token(ui_api_token_from_textbox)
        if token_err: return token_err
        space_info = parse_markdown_for_build(markdown_input)
        if not space_info["files"]: return "Error: No files were found after parsing the markdown from the image."
        with tempfile.TemporaryDirectory() as temp_dir:
            repo_staging_path = Path(temp_dir) / "repo_staging"
            repo_staging_path.mkdir()
            for file_info in space_info["files"]:
                file_path_abs = repo_staging_path / file_info["path"]
                file_path_abs.parent.mkdir(parents=True, exist_ok=True)
                with open(file_path_abs, "w", encoding="utf-8") as f:
                    f.write(file_info["content"])
            create_repo(repo_id=repo_id, token=resolved_api_token, repo_type="space", space_sdk=sdk_ui, exist_ok=True)
            upload_folder(
                repo_id=repo_id,
                folder_path=str(repo_staging_path),
                path_in_repo=".",
                token=resolved_api_token,
                repo_type="space",
                commit_message="Initial Space creation from repo-embedded image"
            )
            return f"**Success!** Space created or updated: [{repo_id}](https://huggingface.co/spaces/{repo_id})"
    except Exception as e:
        logger.exception(f"Error in create_space for {repo_id}:")
        return f"Error during Space creation: {str(e)}"

def build_space_from_image(api_token, repo_image, image_password, new_space_name, owner, sdk):
    markdown_string, status_msg = extract_markdown_from_image(repo_image, image_password)
    if not markdown_string:
        return status_msg
    result = create_space(api_token, new_space_name, owner, sdk, markdown_string)
    return result

def preview_image_contents(repo_image, image_password):
    markdown_string, status_msg = extract_markdown_from_image(repo_image, image_password)
    if not markdown_string:
        return status_msg, gr.update(visible=False), gr.update(choices=[], value=None), "", []
    try:
        file_data, _ = markdown_to_files(markdown_string)
        if isinstance(file_data, str) or not file_data:
            raise ValueError("Markdown parsing failed or returned no files.")
        
        filenames = [f['filename'] for f in file_data]
        if not filenames:
            status = "βœ… Preview generated, but no files were found in the markdown structure."
            return status, gr.update(visible=False), gr.update(choices=[], value=None), "", []

        first_filename = filenames
        first_content = file_data[0]['content']
        
        status = f"βœ… Preview generated successfully. Found {len(filenames)} file(s)."
        
        return (
            status,
            gr.update(visible=True),
            gr.update(choices=filenames, value=first_filename),
            first_content,
            file_data
        )
    except Exception as e:
        logger.error(f"Error parsing markdown for preview: {e}", exc_info=True)
        error_msg = f"❌ Error: Could not parse files from the image's content. {e}"
        return error_msg, gr.update(visible=False), gr.update(choices=[], value=None), "", []

def update_file_preview(selected_filename, all_file_data):
    if not selected_filename or not all_file_data:
        return ""
    selected_file = next((f for f in all_file_data if f['filename'] == selected_filename), None)
    if selected_file:
        return selected_file.get('content', 'File content not found.')
    return f"Select file to view contents."