Update app.py
Browse files
app.py
CHANGED
|
@@ -1,166 +1,92 @@
|
|
| 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 |
-
df = df.sort_values(by="_raw_size", ascending=False)
|
| 91 |
-
elif sort_by == "Size (Smallest)":
|
| 92 |
-
df = df.sort_values(by="_raw_size", ascending=True)
|
| 93 |
-
|
| 94 |
-
# Clean up raw columns and enforce column order
|
| 95 |
-
df = df.drop(columns=["_raw_date", "_raw_size"])
|
| 96 |
-
df = df[["File Name", "Date Created", "Size"]]
|
| 97 |
-
|
| 98 |
-
file_names = df["File Name"].tolist() if not df.empty else []
|
| 99 |
-
return df, gr.update(choices=file_names)
|
| 100 |
-
|
| 101 |
-
except Exception as e:
|
| 102 |
-
print(f"Error fetching files: {e}")
|
| 103 |
-
return pd.DataFrame(columns=["File Name", "Date Created", "Size"]), gr.update(choices=[])
|
| 104 |
-
|
| 105 |
-
def download_file(filename):
|
| 106 |
-
"""Download the selected file from the bucket to a temporary local file."""
|
| 107 |
-
if not filename:
|
| 108 |
-
return None
|
| 109 |
-
|
| 110 |
-
try:
|
| 111 |
-
fs = get_fs()
|
| 112 |
-
src_path = f"{HF_BUCKET}/{filename}"
|
| 113 |
-
|
| 114 |
-
temp_dir = tempfile.mkdtemp()
|
| 115 |
-
dest_path = os.path.join(temp_dir, filename)
|
| 116 |
-
|
| 117 |
-
print(f"๐ฅ Downloading {filename}...")
|
| 118 |
-
fs.get(src_path, dest_path)
|
| 119 |
-
print("โ
Download complete!")
|
| 120 |
-
|
| 121 |
-
return dest_path
|
| 122 |
-
except Exception as e:
|
| 123 |
-
print(f"โ ๏ธ Error downloading {filename}: {e}")
|
| 124 |
-
return None
|
| 125 |
-
|
| 126 |
-
# --- UI LAYOUT ---
|
| 127 |
-
with gr.Blocks() as app:
|
| 128 |
-
gr.Markdown("# ๐๏ธ AudioNovel Bucket Explorer")
|
| 129 |
-
gr.Markdown("View, sort, and download your processed audio files. Use the dropdown to sort your files perfectly.")
|
| 130 |
-
|
| 131 |
-
with gr.Row():
|
| 132 |
-
with gr.Column(scale=2):
|
| 133 |
-
refresh_btn = gr.Button("๐ Refresh Bucket contents", variant="secondary")
|
| 134 |
-
with gr.Column(scale=1):
|
| 135 |
-
sort_dropdown = gr.Dropdown(
|
| 136 |
-
label="Sort Files By",
|
| 137 |
-
choices=["Name (A-Z)", "Name (Z-A)", "Date (Newest)", "Date (Oldest)", "Size (Largest)", "Size (Smallest)"],
|
| 138 |
-
value="Date (Newest)",
|
| 139 |
-
interactive=True
|
| 140 |
-
)
|
| 141 |
-
|
| 142 |
-
# Interactive table (Headers updated)
|
| 143 |
-
file_table = gr.Dataframe(
|
| 144 |
-
headers=["File Name", "Date Created", "Size"],
|
| 145 |
-
interactive=False,
|
| 146 |
-
label="Bucket Contents"
|
| 147 |
-
)
|
| 148 |
-
|
| 149 |
-
gr.Markdown("---")
|
| 150 |
-
gr.Markdown("### ๐ฅ Download a File")
|
| 151 |
-
|
| 152 |
-
with gr.Row():
|
| 153 |
-
with gr.Column(scale=2):
|
| 154 |
-
file_dropdown = gr.Dropdown(label="Select File to Download", choices=[])
|
| 155 |
-
with gr.Column(scale=1):
|
| 156 |
-
download_btn = gr.Button("๐ Prepare Download", variant="primary")
|
| 157 |
-
|
| 158 |
-
file_output = gr.File(label="Ready for Download")
|
| 159 |
-
|
| 160 |
-
# --- EVENT TRIGGERS ---
|
| 161 |
-
refresh_btn.click(fn=list_files, inputs=[sort_dropdown], outputs=[file_table, file_dropdown])
|
| 162 |
-
sort_dropdown.change(fn=list_files, inputs=[sort_dropdown], outputs=[file_table, file_dropdown])
|
| 163 |
-
app.load(fn=list_files, inputs=[sort_dropdown], outputs=[file_table, file_dropdown])
|
| 164 |
|
| 165 |
if __name__ == "__main__":
|
| 166 |
-
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Webnovel.com NTR keyword scraper
|
| 3 |
+
Searches Webnovel's public search API for titles/descriptions
|
| 4 |
+
containing "netorase", "netorare", or "ntr" and saves results to CSV.
|
| 5 |
+
|
| 6 |
+
Run with: python webnovel_ntr_scraper.py
|
| 7 |
+
Requires: pip install requests
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import requests
|
| 11 |
+
import csv
|
| 12 |
+
import time
|
| 13 |
+
import json
|
| 14 |
+
|
| 15 |
+
KEYWORDS = ["netorase", "netorare", "ntr"]
|
| 16 |
+
OUTPUT_FILE = "webnovel_ntr_results.csv"
|
| 17 |
+
|
| 18 |
+
# Webnovel's internal search API (subject to change)
|
| 19 |
+
SEARCH_URL = "https://www.webnovel.com/go/pcm/search/result"
|
| 20 |
+
|
| 21 |
+
HEADERS = {
|
| 22 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
| 23 |
+
"(KHTML, like Gecko) Chrome/124.0 Safari/537.36",
|
| 24 |
+
"Accept": "application/json, text/plain, */*",
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
def search_keyword(keyword, page_size=20, max_pages=20, delay=1.5):
|
| 28 |
+
"""Query Webnovel's search endpoint for a keyword, paginating through results."""
|
| 29 |
+
results = []
|
| 30 |
+
for page in range(1, max_pages + 1):
|
| 31 |
+
params = {
|
| 32 |
+
"keywords": keyword,
|
| 33 |
+
"pageIndex": page,
|
| 34 |
+
"pageSize": page_size,
|
| 35 |
+
}
|
| 36 |
+
try:
|
| 37 |
+
resp = requests.get(SEARCH_URL, params=params, headers=HEADERS, timeout=10)
|
| 38 |
+
resp.raise_for_status()
|
| 39 |
+
data = resp.json()
|
| 40 |
+
except (requests.RequestException, json.JSONDecodeError) as e:
|
| 41 |
+
print(f" [!] Error on page {page} for '{keyword}': {e}")
|
| 42 |
+
break
|
| 43 |
+
|
| 44 |
+
# Adjust this path based on actual API response structure
|
| 45 |
+
items = (
|
| 46 |
+
data.get("data", {}).get("items")
|
| 47 |
+
or data.get("data", {}).get("bookItems")
|
| 48 |
+
or []
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
if not items:
|
| 52 |
+
break
|
| 53 |
+
|
| 54 |
+
for item in items:
|
| 55 |
+
title = item.get("bookName") or item.get("title", "")
|
| 56 |
+
desc = item.get("description") or item.get("bookDesc", "")
|
| 57 |
+
book_id = item.get("bookId", "")
|
| 58 |
+
url = f"https://www.webnovel.com/book/{book_id}" if book_id else ""
|
| 59 |
+
results.append({"title": title, "description": desc, "url": url})
|
| 60 |
+
|
| 61 |
+
print(f" Keyword '{keyword}' page {page}: {len(items)} items")
|
| 62 |
+
time.sleep(delay)
|
| 63 |
+
|
| 64 |
+
return results
|
| 65 |
+
|
| 66 |
+
def matches_ntr(title, description, keywords):
|
| 67 |
+
text = f"{title} {description}".lower()
|
| 68 |
+
return any(k.lower() in text for k in keywords)
|
| 69 |
+
|
| 70 |
+
def main():
|
| 71 |
+
all_matches = {}
|
| 72 |
+
|
| 73 |
+
for kw in KEYWORDS:
|
| 74 |
+
print(f"Searching for keyword: {kw}")
|
| 75 |
+
items = search_keyword(kw)
|
| 76 |
+
for item in items:
|
| 77 |
+
if matches_ntr(item["title"], item["description"], KEYWORDS):
|
| 78 |
+
key = item["url"] or item["title"]
|
| 79 |
+
all_matches[key] = item
|
| 80 |
+
|
| 81 |
+
print(f"\nFound {len(all_matches)} unique matching novels.")
|
| 82 |
+
|
| 83 |
+
with open(OUTPUT_FILE, "w", newline="", encoding="utf-8") as f:
|
| 84 |
+
writer = csv.DictWriter(f, fieldnames=["title", "description", "url"])
|
| 85 |
+
writer.writeheader()
|
| 86 |
+
for item in all_matches.values():
|
| 87 |
+
writer.writerow(item)
|
| 88 |
+
|
| 89 |
+
print(f"Saved results to {OUTPUT_FILE}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
|
| 91 |
if __name__ == "__main__":
|
| 92 |
+
main()
|