Update app.py
Browse files
app.py
CHANGED
|
@@ -1,12 +1,41 @@
|
|
| 1 |
import gradio as gr
|
|
|
|
|
|
|
| 2 |
import re
|
| 3 |
import warnings
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
warnings.filterwarnings("ignore")
|
| 5 |
|
| 6 |
-
class
|
| 7 |
def __init__(self):
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
def preprocess_persian_text(self, text):
|
| 12 |
"""Clean and preprocess Persian text"""
|
|
@@ -18,102 +47,400 @@ class LightweightPersianSummarizer:
|
|
| 18 |
|
| 19 |
return text
|
| 20 |
|
| 21 |
-
def
|
| 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 |
-
for i, sentence in enumerate(sentences):
|
| 57 |
-
score = self.score_sentence(sentence, i, len(sentences))
|
| 58 |
-
scored_sentences.append((sentence, score, i))
|
| 59 |
|
| 60 |
-
#
|
| 61 |
-
|
|
|
|
| 62 |
|
| 63 |
-
#
|
| 64 |
-
|
| 65 |
-
|
|
|
|
|
|
|
|
|
|
| 66 |
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
truncated = sentence[:target_length-10] + "..."
|
| 75 |
-
selected_sentences.append((truncated, original_index))
|
| 76 |
break
|
| 77 |
|
| 78 |
-
#
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
-
#
|
| 82 |
-
|
| 83 |
|
| 84 |
-
|
| 85 |
-
|
| 86 |
|
| 87 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
try:
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
except Exception as e:
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
# Initialize the summarizer
|
| 116 |
-
persian_summarizer =
|
| 117 |
|
| 118 |
def summarize_persian_text(text, summary_length):
|
| 119 |
"""Main function to handle summarization requests"""
|
|
@@ -129,21 +456,21 @@ def summarize_persian_text(text, summary_length):
|
|
| 129 |
# Sample Persian texts for demonstration
|
| 130 |
sample_texts = {
|
| 131 |
"خبر سیاسی": """
|
| 132 |
-
مجلس شورای اسلامی ایران در جلسه علنی روز گذشته لایحه بودجه سال آینده را بررسی کرد. نمایندگان مجلس در این جلسه به بحث و بررسی جزئیات بودجه پرداختند و پیشنهادات مختلفی برای بهبود آن ارائه دادند. وزیر اقتصاد نیز در این جلسه حضور یافت و به سوالات نمایندگان پاسخ داد. بر اساس این لایحه، بودجه عمومی کشور نسبت به سال جاری افزایش قابل توجهی خواهد داشت. همچنین اعتبارات ویژهای برای توسعه زیرساختهای کشور در نظر گرفته شده است.
|
| 133 |
""",
|
| 134 |
|
| 135 |
"مقاله علمی": """
|
| 136 |
-
هوش مصنوعی در دهههای اخیر به یکی از مهمترین فناوریهای نوین تبدیل شده است. این فناوری کاربردهای گستردهای در زمینههای مختلف نظیر پزشکی، آموزش، حمل و نقل و صنعت دارد. یادگیری ماشین که بخش مهمی از هوش مصنوعی محسوب میشود، امکان تحلیل دادههای پیچیده و الگویابی را فراهم میکند. شبکههای ع
|
| 137 |
""",
|
| 138 |
|
| 139 |
"متن ادبی": """
|
| 140 |
-
در باغ گلهای سرخ، پیرمردی با موهای سفید نشسته بود و به آسمان آبی نگاه میکرد. نسیم ملایم صبحگاهی برگهای درختان را به رقص درآورده بود. صدای آب نهری که از دور میآمد، آرامش خاصی به فضا میبخشید. پیرمرد در دل خود به یاد روزهای جوانیاش بود، زمانی که این باغ را با دستان خود کاشته بود. اکنون پس از سالها، میوه زحماتش را میدید. گلهای رنگارنگ، درختان سایهدار و آرامش این مکان، همه نشان از عشق و دلبستگی او به این باغ داشت.
|
| 141 |
"""
|
| 142 |
}
|
| 143 |
|
| 144 |
# Create Gradio interface
|
| 145 |
with gr.Blocks(
|
| 146 |
-
title="خلاصهساز متن فارسی
|
| 147 |
theme=gr.themes.Soft(),
|
| 148 |
css="""
|
| 149 |
.persian-text {
|
|
@@ -162,8 +489,9 @@ with gr.Blocks(
|
|
| 162 |
gr.HTML("""
|
| 163 |
<div class="main-header">
|
| 164 |
<h1>🤖 خلاصهساز هوشمند متن فارسی</h1>
|
| 165 |
-
<p><strong>Persian Text Summarization Tool
|
| 166 |
-
<p>این ابزار با
|
|
|
|
| 167 |
</div>
|
| 168 |
""")
|
| 169 |
|
|
@@ -171,14 +499,43 @@ with gr.Blocks(
|
|
| 171 |
with gr.Column(scale=2):
|
| 172 |
gr.Markdown("## 📝 متن ورودی")
|
| 173 |
|
| 174 |
-
#
|
| 175 |
-
gr.HTML(
|
| 176 |
-
|
| 177 |
-
<strong>
|
| 178 |
-
<strong>
|
| 179 |
-
<
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
# Sample text selector
|
| 184 |
sample_selector = gr.Dropdown(
|
|
@@ -228,23 +585,55 @@ with gr.Blocks(
|
|
| 228 |
gr.Markdown("""
|
| 229 |
### نحوه استفاده:
|
| 230 |
1. **متن خود را وارد کنید**: متن فارسی خود را در قسمت "متن ورودی" بنویسید یا کپی کنید
|
| 231 |
-
2. **
|
| 232 |
-
3. **
|
| 233 |
-
4. **
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
- ✅
|
| 240 |
-
- ✅ رو
|
| 241 |
-
- ✅ ا
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
|
| 243 |
### نکات:
|
| 244 |
- متن ورودی باید حداقل 100 کاراکتر باشد
|
| 245 |
-
-
|
| 246 |
-
- ا
|
| 247 |
-
- ه
|
|
|
|
|
|
|
|
|
|
| 248 |
""")
|
| 249 |
|
| 250 |
# Event handlers
|
|
@@ -270,6 +659,21 @@ with gr.Blocks(
|
|
| 270 |
|
| 271 |
return summary, orig_len, summ_len, ratio
|
| 272 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
# Connect events
|
| 274 |
sample_selector.change(
|
| 275 |
fn=load_sample_text,
|
|
@@ -277,6 +681,24 @@ with gr.Blocks(
|
|
| 277 |
outputs=[text_input]
|
| 278 |
)
|
| 279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
summarize_btn.click(
|
| 281 |
fn=process_summarization,
|
| 282 |
inputs=[text_input, summary_length],
|
|
@@ -284,8 +706,8 @@ with gr.Blocks(
|
|
| 284 |
)
|
| 285 |
|
| 286 |
clear_btn.click(
|
| 287 |
-
fn=lambda: ("", "", 0, 0, 0),
|
| 288 |
-
outputs=[text_input, summary_output, original_length, summary_length_num, compression_ratio]
|
| 289 |
)
|
| 290 |
|
| 291 |
# Launch the app
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import torch
|
| 3 |
+
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM, pipeline
|
| 4 |
import re
|
| 5 |
import warnings
|
| 6 |
+
import io
|
| 7 |
+
import requests
|
| 8 |
+
from bs4 import BeautifulSoup
|
| 9 |
+
from urllib.parse import urlparse, parse_qs
|
| 10 |
warnings.filterwarnings("ignore")
|
| 11 |
|
| 12 |
+
class PersianSummarizer:
|
| 13 |
def __init__(self):
|
| 14 |
+
# Using mT5 which is more stable and supports Persian well
|
| 15 |
+
self.model_name = "google/mt5-small"
|
| 16 |
+
self.tokenizer = None
|
| 17 |
+
self.model = None
|
| 18 |
+
self.load_model()
|
| 19 |
+
|
| 20 |
+
def load_model(self):
|
| 21 |
+
"""Load the multilingual T5 model for Persian summarization"""
|
| 22 |
+
try:
|
| 23 |
+
print("Loading mT5 model for Persian summarization...")
|
| 24 |
+
self.tokenizer = AutoTokenizer.from_pretrained(self.model_name)
|
| 25 |
+
self.model = AutoModelForSeq2SeqLM.from_pretrained(self.model_name)
|
| 26 |
+
print("mT5 model loaded successfully!")
|
| 27 |
+
except Exception as e:
|
| 28 |
+
print(f"Error loading model: {e}")
|
| 29 |
+
# Fallback to pipeline approach
|
| 30 |
+
try:
|
| 31 |
+
from transformers import pipeline
|
| 32 |
+
self.pipeline = pipeline("summarization", model="facebook/bart-large-cnn")
|
| 33 |
+
self.model = None
|
| 34 |
+
print("Fallback pipeline loaded!")
|
| 35 |
+
except Exception as e2:
|
| 36 |
+
print(f"All models failed: {e2}")
|
| 37 |
+
self.model = None
|
| 38 |
+
self.pipeline = None
|
| 39 |
|
| 40 |
def preprocess_persian_text(self, text):
|
| 41 |
"""Clean and preprocess Persian text"""
|
|
|
|
| 47 |
|
| 48 |
return text
|
| 49 |
|
| 50 |
+
def summarize_text(self, text, max_length=150, min_length=50):
|
| 51 |
+
"""Summarize Persian text keeping output in Persian"""
|
| 52 |
+
if not text or len(text.strip()) < 50:
|
| 53 |
+
return "❌ متن ورودی کوتاه است. لطفاً متن طولانیتری وارد کنید."
|
| 54 |
+
|
| 55 |
+
try:
|
| 56 |
+
# Preprocess text
|
| 57 |
+
clean_text = self.preprocess_persian_text(text)
|
| 58 |
+
|
| 59 |
+
# Check text length
|
| 60 |
+
if len(clean_text) < 100:
|
| 61 |
+
return "❌ متن پس از پردازش کوتاه است. متن بلندتری وارد کنید."
|
| 62 |
+
|
| 63 |
+
if self.model is None:
|
| 64 |
+
# Use fallback methods
|
| 65 |
+
if hasattr(self, 'pipeline') and self.pipeline:
|
| 66 |
+
# Try with pipeline but post-process to stay Persian
|
| 67 |
+
result = self.pipeline(clean_text, max_length=max_length, min_length=min_length)
|
| 68 |
+
return self.ensure_persian_output(result[0]['summary_text'], clean_text)
|
| 69 |
+
else:
|
| 70 |
+
# Pure extractive method
|
| 71 |
+
return self.extractive_summary(clean_text, max_length)
|
| 72 |
+
|
| 73 |
+
# Use mT5 with Persian-specific prompting
|
| 74 |
+
# Add Persian instruction to encourage Persian output
|
| 75 |
+
persian_prompt = f"خلاصه کنید: {clean_text}"
|
| 76 |
+
|
| 77 |
+
# Tokenize with proper settings
|
| 78 |
+
inputs = self.tokenizer(
|
| 79 |
+
persian_prompt,
|
| 80 |
+
max_length=512, # Reduced for stability
|
| 81 |
+
padding=True,
|
| 82 |
+
truncation=True,
|
| 83 |
+
return_tensors="pt"
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
# Generate summary
|
| 87 |
+
with torch.no_grad():
|
| 88 |
+
summary_ids = self.model.generate(
|
| 89 |
+
inputs["input_ids"],
|
| 90 |
+
attention_mask=inputs.get("attention_mask"),
|
| 91 |
+
max_length=max_length,
|
| 92 |
+
min_length=min_length,
|
| 93 |
+
num_beams=2, # Reduced for speed
|
| 94 |
+
length_penalty=1.0,
|
| 95 |
+
repetition_penalty=1.1,
|
| 96 |
+
early_stopping=True,
|
| 97 |
+
do_sample=False
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
# Decode and clean
|
| 101 |
+
summary = self.tokenizer.decode(summary_ids[0], skip_special_tokens=True)
|
| 102 |
+
|
| 103 |
+
# Clean up the summary and ensure it's Persian
|
| 104 |
+
summary = self.clean_persian_summary(summary, clean_text)
|
| 105 |
+
|
| 106 |
+
return summary
|
| 107 |
+
|
| 108 |
+
# File processing functions
|
| 109 |
+
def extract_text_from_file(file_path):
|
| 110 |
+
"""Extract text from uploaded file"""
|
| 111 |
+
if file_path is None:
|
| 112 |
+
return None, "❌ فایلی انتخاب نشده است."
|
| 113 |
+
|
| 114 |
+
try:
|
| 115 |
+
file_extension = file_path.name.lower().split('.')[-1]
|
| 116 |
|
| 117 |
+
if file_extension == 'txt':
|
| 118 |
+
# Read plain text file
|
| 119 |
+
with open(file_path.name, 'r', encoding='utf-8') as f:
|
| 120 |
+
content = f.read()
|
| 121 |
+
return content, f"✅ فایل متنی با موفقیت خوانده شد. ({len(content)} کاراکتر)"
|
| 122 |
|
| 123 |
+
elif file_extension == 'docx':
|
| 124 |
+
# Try to read Word document
|
| 125 |
+
try:
|
| 126 |
+
import docx
|
| 127 |
+
doc = docx.Document(file_path.name)
|
| 128 |
+
content = '\n'.join([paragraph.text for paragraph in doc.paragraphs])
|
| 129 |
+
return content, f"✅ فایل Word با موفقیت خوانده شد. ({len(content)} کاراکتر)"
|
| 130 |
+
except ImportError:
|
| 131 |
+
return None, "❌ برای خواندن فایل Word نیاز به نصب python-docx دارید."
|
| 132 |
|
| 133 |
+
elif file_extension == 'pdf':
|
| 134 |
+
# Try to read PDF
|
| 135 |
+
try:
|
| 136 |
+
import PyPDF2
|
| 137 |
+
with open(file_path.name, 'rb') as file:
|
| 138 |
+
pdf_reader = PyPDF2.PdfReader(file)
|
| 139 |
+
content = ''
|
| 140 |
+
for page in pdf_reader.pages:
|
| 141 |
+
content += page.extract_text() + '\n'
|
| 142 |
+
return content, f"✅ فایل PDF با موفقیت خوانده شد. ({len(content)} کاراکتر)"
|
| 143 |
+
except ImportError:
|
| 144 |
+
return None, "❌ برای خواندن فایل PDF نیاز به نصب PyPDF2 دارید."
|
| 145 |
+
|
| 146 |
+
else:
|
| 147 |
+
# Try to read as plain text anyway
|
| 148 |
+
try:
|
| 149 |
+
with open(file_path.name, 'r', encoding='utf-8') as f:
|
| 150 |
+
content = f.read()
|
| 151 |
+
return content, f"✅ فایل به عنوان متن خوانده شد. ({len(content)} کاراکتر)"
|
| 152 |
+
except UnicodeDecodeError:
|
| 153 |
+
try:
|
| 154 |
+
with open(file_path.name, 'r', encoding='cp1256') as f:
|
| 155 |
+
content = f.read()
|
| 156 |
+
return content, f"✅ فایل با کدگذاری cp1256 خوانده شد. ({len(content)} کاراکتر)"
|
| 157 |
+
except:
|
| 158 |
+
return None, f"❌ قادر به خواندن فایل {file_extension} نیستم. فرمتهای پشتیبانی شده: txt, docx, pdf"
|
| 159 |
|
| 160 |
+
except Exception as e:
|
| 161 |
+
return None, f"❌ خطا در خواندن فایل: {str(e)}"
|
| 162 |
+
|
| 163 |
+
def process_uploaded_file(file):
|
| 164 |
+
"""Process uploaded file and return text content"""
|
| 165 |
+
if file is None:
|
| 166 |
+
return "", "📁 فایل خود را انتخاب کنید"
|
| 167 |
+
|
| 168 |
+
content, message = extract_text_from_file(file)
|
| 169 |
+
|
| 170 |
+
if content:
|
| 171 |
+
# Clean the content
|
| 172 |
+
content = persian_summarizer.preprocess_persian_text(content)
|
| 173 |
+
return content, message
|
| 174 |
+
else:
|
| 175 |
+
return "", message
|
| 176 |
+
|
| 177 |
+
# URL processing functions
|
| 178 |
+
def extract_text_from_url(url):
|
| 179 |
+
"""Extract text content from a URL"""
|
| 180 |
+
if not url or not url.strip():
|
| 181 |
+
return None, "❌ لطفاً آدرس وبسایت را وارد کنید."
|
| 182 |
+
|
| 183 |
+
# Add protocol if missing
|
| 184 |
+
if not url.startswith(('http://', 'https://')):
|
| 185 |
+
url = 'https://' + url
|
| 186 |
+
|
| 187 |
+
# Validate URL format
|
| 188 |
+
try:
|
| 189 |
+
parsed = urlparse(url)
|
| 190 |
+
if not parsed.netloc:
|
| 191 |
+
return None, "❌ آدرس وبسایت معتبر نیست."
|
| 192 |
+
except:
|
| 193 |
+
return None, "❌ آدرس وبسایت معتبر نیست."
|
| 194 |
+
|
| 195 |
+
try:
|
| 196 |
+
# Set headers to mimic a real browser
|
| 197 |
+
headers = {
|
| 198 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
| 199 |
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
| 200 |
+
'Accept-Language': 'fa,en-US,en;q=0.5',
|
| 201 |
+
'Accept-Encoding': 'gzip, deflate',
|
| 202 |
+
'Connection': 'keep-alive',
|
| 203 |
+
}
|
| 204 |
|
| 205 |
+
# Fetch the webpage
|
| 206 |
+
response = requests.get(url, headers=headers, timeout=10, verify=False)
|
| 207 |
+
response.raise_for_status()
|
| 208 |
|
| 209 |
+
# Parse HTML content
|
| 210 |
+
soup = BeautifulSoup(response.content, 'html.parser')
|
|
|
|
|
|
|
|
|
|
| 211 |
|
| 212 |
+
# Remove script and style elements
|
| 213 |
+
for script in soup(["script", "style", "nav", "footer", "header", "aside"]):
|
| 214 |
+
script.decompose()
|
| 215 |
|
| 216 |
+
# Extract text from common content areas
|
| 217 |
+
content_selectors = [
|
| 218 |
+
'article', 'main', '.content', '.post', '.article',
|
| 219 |
+
'.entry-content', '.post-content', '.article-content',
|
| 220 |
+
'.story-body', '.article-body', '.content-body'
|
| 221 |
+
]
|
| 222 |
|
| 223 |
+
extracted_text = ""
|
| 224 |
+
|
| 225 |
+
# Try to find content in specific areas first
|
| 226 |
+
for selector in content_selectors:
|
| 227 |
+
content_area = soup.select_one(selector)
|
| 228 |
+
if content_area:
|
| 229 |
+
extracted_text = content_area.get_text(separator=' ', strip=True)
|
|
|
|
|
|
|
| 230 |
break
|
| 231 |
|
| 232 |
+
# If no specific content area found, extract from body
|
| 233 |
+
if not extracted_text:
|
| 234 |
+
body = soup.find('body')
|
| 235 |
+
if body:
|
| 236 |
+
extracted_text = body.get_text(separator=' ', strip=True)
|
| 237 |
+
else:
|
| 238 |
+
extracted_text = soup.get_text(separator=' ', strip=True)
|
| 239 |
|
| 240 |
+
# Clean up the text
|
| 241 |
+
extracted_text = re.sub(r'\s+', ' ', extracted_text.strip())
|
| 242 |
|
| 243 |
+
if len(extracted_text) < 100:
|
| 244 |
+
return None, "❌ محتوای متنی کافی در این صفحه یافت نشد."
|
| 245 |
|
| 246 |
+
return extracted_text, f"✅ محتوای وبسایت با موفقیت استخراج شد. ({len(extracted_text)} کاراکتر)"
|
| 247 |
+
|
| 248 |
+
except requests.exceptions.Timeout:
|
| 249 |
+
return None, "❌ زمان اتصال به وبسایت تمام شد. لطفاً دوباره تلاش کنید."
|
| 250 |
+
except requests.exceptions.ConnectionError:
|
| 251 |
+
return None, "❌ خطا در اتصال به وبسایت. لطفاً اتصال اینترنت خود را بررسی کنید."
|
| 252 |
+
except requests.exceptions.HTTPError as e:
|
| 253 |
+
return None, f"❌ خطا در دریافت صفحه: {e.response.status_code}"
|
| 254 |
+
except Exception as e:
|
| 255 |
+
return None, f"❌ خطا در پردازش وبسایت: {str(e)}"
|
| 256 |
+
|
| 257 |
+
def process_url_input(url):
|
| 258 |
+
"""Process URL input and return text content"""
|
| 259 |
+
if not url or not url.strip():
|
| 260 |
+
return "", "🌐 آدرس وبسایت خود را وارد کنید"
|
| 261 |
|
| 262 |
+
content, message = extract_text_from_url(url.strip())
|
| 263 |
+
|
| 264 |
+
if content:
|
| 265 |
+
# Clean the content
|
| 266 |
+
content = persian_summarizer.preprocess_persian_text(content)
|
| 267 |
+
return content, message
|
| 268 |
+
else:
|
| 269 |
+
return "", message
|
| 270 |
+
|
| 271 |
+
# YouTube processing functions
|
| 272 |
+
def extract_youtube_video_id(url):
|
| 273 |
+
"""Extract YouTube video ID from URL"""
|
| 274 |
+
if not url:
|
| 275 |
+
return None
|
| 276 |
+
|
| 277 |
+
# Handle different YouTube URL formats
|
| 278 |
+
patterns = [
|
| 279 |
+
r'(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)',
|
| 280 |
+
r'youtube\.com\/watch\?.*v=([^&\n?#]+)'
|
| 281 |
+
]
|
| 282 |
+
|
| 283 |
+
for pattern in patterns:
|
| 284 |
+
match = re.search(pattern, url)
|
| 285 |
+
if match:
|
| 286 |
+
return match.group(1)
|
| 287 |
+
|
| 288 |
+
return None
|
| 289 |
+
|
| 290 |
+
def extract_youtube_transcript(url):
|
| 291 |
+
"""Extract transcript from YouTube video"""
|
| 292 |
+
if not url or not url.strip():
|
| 293 |
+
return None, "❌ لطفاً لینک یوتیوب را وارد کنید."
|
| 294 |
+
|
| 295 |
+
try:
|
| 296 |
+
# Import youtube-transcript-api
|
| 297 |
+
from youtube_transcript_api import YouTubeTranscriptApi
|
| 298 |
+
from youtube_transcript_api._errors import TranscriptsDisabled, NoTranscriptFound
|
| 299 |
+
|
| 300 |
+
# Extract video ID
|
| 301 |
+
video_id = extract_youtube_video_id(url.strip())
|
| 302 |
+
if not video_id:
|
| 303 |
+
return None, "❌ لینک یوتیوب معتبر نیست. مثال: https://youtube.com/watch?v=VIDEO_ID"
|
| 304 |
|
| 305 |
+
# Try to get transcript in Persian first, then English
|
| 306 |
+
transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)
|
| 307 |
+
|
| 308 |
+
transcript_text = ""
|
| 309 |
+
language_used = ""
|
| 310 |
+
|
| 311 |
+
# Try Persian first
|
| 312 |
try:
|
| 313 |
+
transcript = transcript_list.find_transcript(['fa', 'per'])
|
| 314 |
+
transcript_data = transcript.fetch()
|
| 315 |
+
language_used = "فارسی"
|
| 316 |
+
except:
|
| 317 |
+
# Try English
|
| 318 |
+
try:
|
| 319 |
+
transcript = transcript_list.find_transcript(['en'])
|
| 320 |
+
transcript_data = transcript.fetch()
|
| 321 |
+
language_used = "انگلیسی"
|
| 322 |
+
except:
|
| 323 |
+
# Try any available transcript
|
| 324 |
+
try:
|
| 325 |
+
transcript = transcript_list.find_manually_created_transcript(['fa', 'per', 'en'])
|
| 326 |
+
transcript_data = transcript.fetch()
|
| 327 |
+
language_used = "دستی"
|
| 328 |
+
except:
|
| 329 |
+
# Try auto-generated
|
| 330 |
+
try:
|
| 331 |
+
transcript = transcript_list.find_generated_transcript(['fa', 'per', 'en'])
|
| 332 |
+
transcript_data = transcript.fetch()
|
| 333 |
+
language_used = "خودکار"
|
| 334 |
+
except:
|
| 335 |
+
return None, "❌ این ویدیو زیرنویس قابل دسترس ندارد."
|
| 336 |
+
|
| 337 |
+
# Combine transcript text
|
| 338 |
+
transcript_text = ' '.join([entry['text'] for entry in transcript_data])
|
| 339 |
+
|
| 340 |
+
if len(transcript_text) < 100:
|
| 341 |
+
return None, "❌ متن زیرنویس کوتاه است یا کیفیت مناسبی ندارد."
|
| 342 |
+
|
| 343 |
+
return transcript_text, f"✅ زیرنویس یوتیوب استخراج شد ({language_used}). ({len(transcript_text)} کاراکتر)"
|
| 344 |
+
|
| 345 |
+
except ImportError:
|
| 346 |
+
return None, "❌ برای استخراج زیرنویس یوتیوب نیاز به نصب youtube-transcript-api دارید."
|
| 347 |
+
except TranscriptsDisabled:
|
| 348 |
+
return None, "❌ زیرنویس برای این ویدیو غیرفعال است."
|
| 349 |
+
except NoTranscriptFound:
|
| 350 |
+
return None, "❌ هیچ زیرنویسی برای این ویدیو یافت نشد."
|
| 351 |
+
except Exception as e:
|
| 352 |
+
return None, f"❌ خطا در استخراج زیرنویس: {str(e)}"
|
| 353 |
+
|
| 354 |
+
def process_youtube_input(url):
|
| 355 |
+
"""Process YouTube URL and return transcript content"""
|
| 356 |
+
if not url or not url.strip():
|
| 357 |
+
return "", "📺 لینک یوتیوب خود را وارد کنید"
|
| 358 |
+
|
| 359 |
+
content, message = extract_youtube_transcript(url.strip())
|
| 360 |
+
|
| 361 |
+
if content:
|
| 362 |
+
# Clean the content
|
| 363 |
+
content = persian_summarizer.preprocess_persian_text(content)
|
| 364 |
+
return content, message
|
| 365 |
+
else:
|
| 366 |
+
return "", message if summary else "❌ نتوانستم خلاصه مناسبی تولید کنم. لطفاً متن دیگری امتحان کنید."
|
| 367 |
|
| 368 |
except Exception as e:
|
| 369 |
+
print(f"Error in summarization: {e}")
|
| 370 |
+
# Always fallback to extractive
|
| 371 |
+
return self.extractive_summary(clean_text, max_length)
|
| 372 |
+
|
| 373 |
+
def ensure_persian_output(self, text, original_text):
|
| 374 |
+
"""Ensure output stays in Persian"""
|
| 375 |
+
# If the output seems to be English, use extractive method instead
|
| 376 |
+
english_ratio = len(re.findall(r'[a-zA-Z]', text)) / max(len(text), 1)
|
| 377 |
+
if english_ratio > 0.3: # If more than 30% English characters
|
| 378 |
+
return self.extractive_summary(original_text, 150)
|
| 379 |
+
return text
|
| 380 |
+
|
| 381 |
+
def clean_persian_summary(self, summary, original_text):
|
| 382 |
+
"""Clean and ensure Persian summary quality"""
|
| 383 |
+
# Remove the prompt prefix if it exists
|
| 384 |
+
summary = re.sub(r'^خلاصه کنید:?\s*', '', summary)
|
| 385 |
+
|
| 386 |
+
# Remove excessive English characters
|
| 387 |
+
summary = re.sub(r'[a-zA-Z]{3,}', '', summary)
|
| 388 |
+
|
| 389 |
+
# Check if summary is mostly Persian
|
| 390 |
+
persian_chars = len(re.findall(r'[\u0600-\u06FF]', summary))
|
| 391 |
+
total_chars = len(re.sub(r'\s', '', summary))
|
| 392 |
+
|
| 393 |
+
if total_chars == 0 or persian_chars / total_chars < 0.5:
|
| 394 |
+
# If not enough Persian content, use extractive method
|
| 395 |
+
return self.extractive_summary(original_text, 150)
|
| 396 |
+
|
| 397 |
+
# Clean whitespace
|
| 398 |
+
summary = re.sub(r'\s+', ' ', summary.strip())
|
| 399 |
+
|
| 400 |
+
return summary
|
| 401 |
+
|
| 402 |
+
def extractive_summary(self, text, max_length):
|
| 403 |
+
"""Simple extractive summarization fallback that keeps Persian"""
|
| 404 |
+
sentences = re.split(r'[.!?؟]', text)
|
| 405 |
+
sentences = [s.strip() for s in sentences if len(s.strip()) > 20]
|
| 406 |
+
|
| 407 |
+
if len(sentences) <= 2:
|
| 408 |
+
return text[:max_length] + ("..." if len(text) > max_length else "")
|
| 409 |
+
|
| 410 |
+
# Score sentences by position and length
|
| 411 |
+
scored_sentences = []
|
| 412 |
+
for i, sentence in enumerate(sentences):
|
| 413 |
+
# Give higher scores to earlier sentences and longer sentences
|
| 414 |
+
position_score = 1.0 - (i / len(sentences)) * 0.5
|
| 415 |
+
length_score = min(len(sentence) / 100, 1.0)
|
| 416 |
+
total_score = position_score + length_score
|
| 417 |
+
scored_sentences.append((sentence, total_score))
|
| 418 |
+
|
| 419 |
+
# Sort by score and take top sentences
|
| 420 |
+
scored_sentences.sort(key=lambda x: x[1], reverse=True)
|
| 421 |
+
|
| 422 |
+
# Select sentences until we reach desired length
|
| 423 |
+
summary_sentences = []
|
| 424 |
+
current_length = 0
|
| 425 |
+
|
| 426 |
+
for sentence, score in scored_sentences:
|
| 427 |
+
if current_length + len(sentence) < max_length:
|
| 428 |
+
summary_sentences.append(sentence)
|
| 429 |
+
current_length += len(sentence)
|
| 430 |
+
else:
|
| 431 |
+
break
|
| 432 |
+
|
| 433 |
+
# Maintain original order
|
| 434 |
+
original_order = []
|
| 435 |
+
for sentence in sentences:
|
| 436 |
+
if sentence in summary_sentences:
|
| 437 |
+
original_order.append(sentence)
|
| 438 |
+
|
| 439 |
+
summary = ". ".join(original_order)
|
| 440 |
+
return summary[:max_length] + ("..." if len(summary) > max_length else "")
|
| 441 |
|
| 442 |
# Initialize the summarizer
|
| 443 |
+
persian_summarizer = PersianSummarizer()
|
| 444 |
|
| 445 |
def summarize_persian_text(text, summary_length):
|
| 446 |
"""Main function to handle summarization requests"""
|
|
|
|
| 456 |
# Sample Persian texts for demonstration
|
| 457 |
sample_texts = {
|
| 458 |
"خبر سیاسی": """
|
| 459 |
+
مجلس شورای اسلامی ایران در جلسه علنی روز گذشته لایحه بودجه سال آینده را بررسی کرد. نمایندگان مجلس در این جلسه به بحث و بررسی جزئیات بودجه پرداختند و پیشنهادات مختلفی برای بهبود آن ارائه دادند. وزیر اقتصاد نیز در این جلسه حضور یافت و به سوالات نمایندگان پاسخ داد. بر اساس این لایحه، بودجه عمومی کشور نسبت به سال جاری افزایش قابل توجهی خواهد داشت. همچنین اعتبارات ویژهای برای توسعه زیرساختهای کشور در نظر گرفته شده است.
|
| 460 |
""",
|
| 461 |
|
| 462 |
"مقاله علمی": """
|
| 463 |
+
هوش مصنوعی در دهههای اخیر به یکی از مهمترین فناوریهای نوین تبدیل شده است. این فناوری کاربردهای گستردهای در زمینههای مختلف نظیر پزشکی، آموزش، حمل و نقل و صنعت دارد. یادگیری ماشین که بخش مهمی از هوش مصنوعی محسوب میشود، امکان تحلیل دادههای پیچیده و الگویابی را فراهم میکند. شبکههای عصبی مصنوعی نیز با الهام از مغز انسان طراحی شدهاند و قابلیتهای شگفتانگیزی در تشخیص الگو و پردازش تصویر دارند. با این حال، چالشهایی نظیر اخلاق در هوش مصنوعی و حفظ حریم خصوصی همچنان وجود دارد.
|
| 464 |
""",
|
| 465 |
|
| 466 |
"متن ادبی": """
|
| 467 |
+
در باغ گلهای سرخ، پیرمردی با موهای سفید نشسته بود و به آسمان آبی نگاه میکرد. نسیم ملایم صبحگاهی برگهای درختان را به رقص درآورده بود. صدای آب نهری که از دور میآمد، آرامش خاصی به فضا میبخشید. پیرمرد در دل خود به یاد روزهای جوانیاش بود، زمانی که این باغ را با دستان خود کاشته بود. اکنون پس از سالها، میوه زحماتش را میدید. گلهای رنگارنگ، درختان سایهدار و آرامش این مکان، همه نشان از عشق و دلبستگی او به این باغ داشت.
|
| 468 |
"""
|
| 469 |
}
|
| 470 |
|
| 471 |
# Create Gradio interface
|
| 472 |
with gr.Blocks(
|
| 473 |
+
title="خلاصهساز متن فارسی",
|
| 474 |
theme=gr.themes.Soft(),
|
| 475 |
css="""
|
| 476 |
.persian-text {
|
|
|
|
| 489 |
gr.HTML("""
|
| 490 |
<div class="main-header">
|
| 491 |
<h1>🤖 خلاصهساز هوشمند متن فارسی</h1>
|
| 492 |
+
<p><strong>Persian Text Summarization Tool</strong></p>
|
| 493 |
+
<p>این ابزار با استفاده از هوش مصنوعی، متنها، فایلها، وبسایتها و ویدیوهای یوتیوب را خلاصه میکند</p>
|
| 494 |
+
<p>📁 فایلها | 🌐 وبسایتها | 📺 یوتیوب (زیرنویس)</p>
|
| 495 |
</div>
|
| 496 |
""")
|
| 497 |
|
|
|
|
| 499 |
with gr.Column(scale=2):
|
| 500 |
gr.Markdown("## 📝 متن ورودی")
|
| 501 |
|
| 502 |
+
# Model status
|
| 503 |
+
model_status = gr.HTML(
|
| 504 |
+
f"""<div style="padding: 10px; background-color: #e8f5e8; border-radius: 5px; margin-bottom: 10px;">
|
| 505 |
+
<strong>🤖 مدل فعال:</strong> mT5-small (پشتیبانی از فارسی)<br>
|
| 506 |
+
<strong>📊 وضعیت:</strong> {'آماده برای خلاصهسازی فارسی' if persian_summarizer.model else 'حالت استخراجی (fallback)'}
|
| 507 |
+
</div>"""
|
| 508 |
+
)
|
| 509 |
+
|
| 510 |
+
# File upload section
|
| 511 |
+
with gr.Accordion("📁 آپلود فایل متنی", open=False):
|
| 512 |
+
file_upload = gr.File(
|
| 513 |
+
label="فایل متنی خود را انتخاب کنید",
|
| 514 |
+
file_types=[".txt", ".docx", ".pdf"],
|
| 515 |
+
type="filepath"
|
| 516 |
+
)
|
| 517 |
+
file_status = gr.HTML("📋 فرمتهای پشتیبانی شده: TXT, DOCX, PDF")
|
| 518 |
+
load_file_btn = gr.Button("📂 بارگذاری متن از فایل", variant="secondary")
|
| 519 |
+
|
| 520 |
+
# URL input section
|
| 521 |
+
with gr.Accordion("🌐 استخراج از وبسایت", open=False):
|
| 522 |
+
url_input = gr.Textbox(
|
| 523 |
+
label="آدرس وبسایت (URL)",
|
| 524 |
+
placeholder="https://example.com یا example.com",
|
| 525 |
+
lines=1
|
| 526 |
+
)
|
| 527 |
+
url_status = gr.HTML("🌍 وبسایتهای فارسی و انگلیسی پشتیبانی میشوند")
|
| 528 |
+
load_url_btn = gr.Button("🔗 استخراج متن از وبسایت", variant="secondary")
|
| 529 |
+
|
| 530 |
+
# YouTube input section
|
| 531 |
+
with gr.Accordion("📺 خلاصه ویدیو یوتیوب", open=False):
|
| 532 |
+
youtube_input = gr.Textbox(
|
| 533 |
+
label="لینک یوتیوب",
|
| 534 |
+
placeholder="https://youtube.com/watch?v=... یا https://youtu.be/...",
|
| 535 |
+
lines=1
|
| 536 |
+
)
|
| 537 |
+
youtube_status = gr.HTML("🎬 زیرنویس فارسی و انگلیسی پشتیبانی میشود")
|
| 538 |
+
load_youtube_btn = gr.Button("📺 استخراج زیرنویس از یوتیوب", variant="secondary")
|
| 539 |
|
| 540 |
# Sample text selector
|
| 541 |
sample_selector = gr.Dropdown(
|
|
|
|
| 585 |
gr.Markdown("""
|
| 586 |
### نحوه استفاده:
|
| 587 |
1. **متن خود را وارد کنید**: متن فارسی خود را در قسمت "متن ورودی" بنویسید یا کپی کنید
|
| 588 |
+
2. **یا فایل آپلود کنید**: فایل متنی (.txt, .docx, .pdf) خود را آپلود کنید
|
| 589 |
+
3. **یا از وبسایت استخراج کنید**: آدرس وبسایت مورد نظر را وارد کنید
|
| 590 |
+
4. **یا ویدیو یوتیوب**: لینک یوتیوب برای استخراج زیرنویس وارد کنید
|
| 591 |
+
5. **نمونه متن انتخاب کنید**: میتوانید از نمونه متنهای آماده استفاده کنید
|
| 592 |
+
6. **طول خلاصه را تعیین کنید**: کوتاه، متوسط یا بلند
|
| 593 |
+
7. **دکمه خلاصهسازی را بزنید**: منتظر بمانید تا خلاصه تولید شود
|
| 594 |
+
|
| 595 |
+
### ویژگیها:
|
| 596 |
+
- ✅ پشتیبانی کامل از زبان فارسی با mT5
|
| 597 |
+
- ✅ خروجی کاملاً فارسی (بدون ترجمه به انگلیسی)
|
| 598 |
+
- ✅ آپلود فایلهای متنی (TXT, DOCX, PDF)
|
| 599 |
+
- ✅ استخراج محتوا از وبسایتها
|
| 600 |
+
- ✅ خلاصه ویدیوهای یوتیوب (از طریق زیرنویس)
|
| 601 |
+
- ✅ سه سطح طول خلاصه
|
| 602 |
+
- ✅ نمونه متنهای آماده
|
| 603 |
+
- ✅ نمایش آمار متن
|
| 604 |
+
- ✅ رابط کاربری فارسی
|
| 605 |
+
- ✅ سیستم fallback پیشرفته
|
| 606 |
+
|
| 607 |
+
### منابع ورودی پشتیبانی شده:
|
| 608 |
+
- **متن مستقیم**: کپی و پیست متن
|
| 609 |
+
- **فایلها**: TXT, DOCX, PDF
|
| 610 |
+
- **وبسایتها**: خبرگاریها، وبلاگها، مقالات آنلاین
|
| 611 |
+
- **یوتیوب**: ویدیوهای دارای زیرنویس فارسی یا انگلیسی
|
| 612 |
+
|
| 613 |
+
### فرمتهای فایل:
|
| 614 |
+
- **TXT**: فایلهای متن ساده (UTF-8, CP1256)
|
| 615 |
+
- **DOCX**: اسناد Microsoft Word
|
| 616 |
+
- **PDF**: اسناد PDF (نیاز به PyPDF2)
|
| 617 |
+
|
| 618 |
+
### وبسایتهای پشتیبانی شده:
|
| 619 |
+
- **خبرگاریها**: BBC Persian, Radio Farda, ایسنا و...
|
| 620 |
+
- **وبلاگها**: وردپرس، بلاگر و...
|
| 621 |
+
- **مقالات**: مجلات آنلاین، سایتهای آموزشی
|
| 622 |
+
|
| 623 |
+
### یوتیوب:
|
| 624 |
+
- **زیرنویس فارسی**: ویدیوهای دارای زیرنویس فارسی
|
| 625 |
+
- **زیرنویس انگلیسی**: ترجمه خودکار برای محتوای انگلیسی
|
| 626 |
+
- **زیرنویس خودکار**: زیرنویسهای تولید شده توسط یوتیوب
|
| 627 |
+
- **فرمتهای لینک**: youtube.com/watch?v=... یا youtu.be/...
|
| 628 |
|
| 629 |
### نکات:
|
| 630 |
- متن ورودی باید حداقل 100 کاراکتر باشد
|
| 631 |
+
- کیفیت خلاصه با متنهای طولانیتر بهتر است
|
| 632 |
+
- از mT5 برای حفظ زبان فارسی استفاده میکند
|
| 633 |
+
- خلاصه تولیدی همیشه به زبان فارسی است
|
| 634 |
+
- برای وبسایتها، محتوای اصلی مقاله استخراج میشود
|
| 635 |
+
- برای یوتیوب، ابتدا زیرنویس فارسی، سپس انگلیسی جستجو میشود
|
| 636 |
+
- فایلها، وبسایتها و ویدیوهای بزرگ زمان بیشتری نیاز دارند
|
| 637 |
""")
|
| 638 |
|
| 639 |
# Event handlers
|
|
|
|
| 659 |
|
| 660 |
return summary, orig_len, summ_len, ratio
|
| 661 |
|
| 662 |
+
def handle_file_upload(file):
|
| 663 |
+
"""Handle file upload and update text input"""
|
| 664 |
+
content, message = process_uploaded_file(file)
|
| 665 |
+
return content, message
|
| 666 |
+
|
| 667 |
+
def handle_url_input(url):
|
| 668 |
+
"""Handle URL input and update text input"""
|
| 669 |
+
content, message = process_url_input(url)
|
| 670 |
+
return content, message
|
| 671 |
+
|
| 672 |
+
def handle_youtube_input(url):
|
| 673 |
+
"""Handle YouTube URL input and update text input"""
|
| 674 |
+
content, message = process_youtube_input(url)
|
| 675 |
+
return content, message
|
| 676 |
+
|
| 677 |
# Connect events
|
| 678 |
sample_selector.change(
|
| 679 |
fn=load_sample_text,
|
|
|
|
| 681 |
outputs=[text_input]
|
| 682 |
)
|
| 683 |
|
| 684 |
+
load_file_btn.click(
|
| 685 |
+
fn=handle_file_upload,
|
| 686 |
+
inputs=[file_upload],
|
| 687 |
+
outputs=[text_input, file_status]
|
| 688 |
+
)
|
| 689 |
+
|
| 690 |
+
load_url_btn.click(
|
| 691 |
+
fn=handle_url_input,
|
| 692 |
+
inputs=[url_input],
|
| 693 |
+
outputs=[text_input, url_status]
|
| 694 |
+
)
|
| 695 |
+
|
| 696 |
+
load_youtube_btn.click(
|
| 697 |
+
fn=handle_youtube_input,
|
| 698 |
+
inputs=[youtube_input],
|
| 699 |
+
outputs=[text_input, youtube_status]
|
| 700 |
+
)
|
| 701 |
+
|
| 702 |
summarize_btn.click(
|
| 703 |
fn=process_summarization,
|
| 704 |
inputs=[text_input, summary_length],
|
|
|
|
| 706 |
)
|
| 707 |
|
| 708 |
clear_btn.click(
|
| 709 |
+
fn=lambda: ("", "", "", "", "", 0, 0, 0),
|
| 710 |
+
outputs=[text_input, summary_output, file_status, url_status, youtube_status, original_length, summary_length_num, compression_ratio]
|
| 711 |
)
|
| 712 |
|
| 713 |
# Launch the app
|