-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogic.py
More file actions
371 lines (318 loc) · 12.2 KB
/
Copy pathlogic.py
File metadata and controls
371 lines (318 loc) · 12.2 KB
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
import re
import requests
import datetime
import time
from urllib.parse import urlparse
from bs4 import BeautifulSoup
HEADERS = {
'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',
'Accept-Language': 'en-US,en;q=0.9',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
}
# Maximum number of retries for failed requests
MAX_RETRIES = 3
# Delay between retries in seconds
RETRY_DELAY = 2
def fetch_url_with_retry(url, timeout=10, max_retries=MAX_RETRIES, retry_delay=RETRY_DELAY):
"""
Fetch a URL with retry mechanism for failed requests.
Args:
url (str): The URL to fetch
timeout (int): Timeout in seconds
max_retries (int): Maximum number of retries
retry_delay (int): Delay between retries in seconds
Returns:
requests.Response: The response object
Raises:
requests.RequestException: If all retries fail
"""
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=timeout, headers=HEADERS)
response.raise_for_status()
return response
except (requests.Timeout, requests.ConnectionError) as e:
if attempt < max_retries - 1:
print(f" Attempt {attempt + 1} failed: {str(e)}. Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
else:
print(f" All {max_retries} attempts failed. Giving up.")
raise
def parse_youtube_url(url):
"""
Parse a YouTube URL to extract video title, channel name, and provider.
Args:
url (str): The YouTube URL
Returns:
dict: A dictionary containing video title, channel name, and provider
"""
try:
# Get the HTML content of the page with retry mechanism
print(f" Fetching YouTube content from {url}...")
response = fetch_url_with_retry(url)
# Parse the HTML content
soup = BeautifulSoup(response.text, 'html.parser')
# Extract video title
video_title = soup.find('meta', property='og:title')['content'] if soup.find('meta', property='og:title') else "Unknown Title"
# Extract channel name
channel_name = soup.find('link', itemprop='name')['content'] if soup.find('link', itemprop='name') else "Unknown Channel"
return {
"title": video_title,
"channel": channel_name,
"provider": "YouTube"
}
except requests.Timeout:
print(f" Timeout error: YouTube request took too long to complete for {url}")
return {
"title": "Timeout Error",
"channel": "Unknown Channel",
"provider": "YouTube"
}
except requests.ConnectionError:
print(f" Connection error: Could not connect to YouTube for {url}")
return {
"title": "Connection Error",
"channel": "Unknown Channel",
"provider": "YouTube"
}
except Exception as e:
print(f" Error parsing YouTube URL: {e}")
return {
"title": "Unknown Title",
"channel": "Unknown Channel",
"provider": "YouTube"
}
def parse_web_url(url):
"""
Parse a web URL to extract page title and site address.
Args:
url (str): The web URL
Returns:
dict: A dictionary containing page title and site address
"""
try:
# Get the HTML content of the page with retry mechanism
print(f" Fetching web content from {url}...")
response = fetch_url_with_retry(url)
# Parse the HTML content
soup = BeautifulSoup(response.text, 'html.parser')
# Extract page title
page_title = soup.title.string if soup.title else "Unknown Title"
# Extract site address
parsed_url = urlparse(url)
site_address = parsed_url.netloc
return {
"title": page_title,
"site": site_address
}
except requests.Timeout:
print(f" Timeout error: Web request took too long to complete for {url}")
return {
"title": "Timeout Error",
"site": urlparse(url).netloc if urlparse(url).netloc else "Unknown Site"
}
except requests.ConnectionError:
print(f" Connection error: Could not connect to website for {url}")
return {
"title": "Connection Error",
"site": urlparse(url).netloc if urlparse(url).netloc else "Unknown Site"
}
except Exception as e:
print(f" Error parsing web URL: {e}")
return {
"title": "Unknown Title",
"site": urlparse(url).netloc if urlparse(url).netloc else "Unknown Site"
}
def is_youtube_url(url):
"""
Check if a URL is a YouTube URL.
Args:
url (str): The URL to check
Returns:
bool: True if the URL is a YouTube URL, False otherwise
"""
youtube_patterns = [
r'(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&\s]+)',
r'(?:https?:\/\/)?(?:www\.)?youtu\.be\/([^\s]+)'
]
for pattern in youtube_patterns:
if re.match(pattern, url):
return True
return False
def format_citation_dstu_8302_2015(url, details, resource_type):
"""
Format a citation according to DSTU 8302:2015 standards.
Args:
url (str): The URL of the resource
details (dict): Details about the resource
resource_type (str): Type of resource ('video' or 'web')
Returns:
str: Formatted citation
"""
# Get current date for access date
today = datetime.datetime.now().strftime("%d.%m.%Y")
if resource_type == 'video':
# Format for video resources
channel = details.get('channel', 'Unknown Channel')
title = details.get('title', 'Unknown Title')
provider = details.get('provider', 'YouTube')
return f"{channel}. {title} [Відео]. {provider}. URL: {url} (дата звернення: {today})."
else:
# Format for web resources
title = details.get('title', 'Unknown Title')
site = details.get('site', 'Unknown Site')
return f"{title} [Електронний ресурс]. {site}. URL: {url} (дата звернення: {today})."
def process_links(links):
"""
Process a list of links.
Args:
links (list): A list of URLs, one per line
Returns:
dict: A dictionary mapping each index (starting at 1) to:
{
"link": <the URL>,
"type": "video" or "web",
"details": {
For videos: title, channel, provider
For web pages: title, site
}
}
"""
result = {}
if not links:
print("No links provided.")
return result
total_links = len(links)
print(f"Processing {total_links} links...")
success_count = 0
error_count = 0
for idx, url in enumerate(links, start=1):
url = url.strip()
if not url: # Skip empty lines
continue
print(f"\nProcessing link {idx}/{total_links}: {url}")
try:
if is_youtube_url(url):
details = parse_youtube_url(url)
result[idx] = {
"link": url,
"type": "video",
"details": details
}
if "Error" not in details["title"]:
success_count += 1
else:
error_count += 1
else:
details = parse_web_url(url)
result[idx] = {
"link": url,
"type": "web",
"details": details
}
if "Error" not in details["title"]:
success_count += 1
else:
error_count += 1
# Print a progress indicator
print(f" Progress: {idx}/{total_links} links processed ({success_count} successful, {error_count} with errors)")
except Exception as e:
error_count += 1
print(f" Error processing link {idx}: {url} - {str(e)}")
# Add a placeholder entry for failed links
result[idx] = {
"link": url,
"type": "unknown",
"details": {"title": "Error processing link", "site": urlparse(url).netloc if urlparse(url).netloc else "Unknown Site"}
}
print(f" Progress: {idx}/{total_links} links processed ({success_count} successful, {error_count} with errors)")
print(f"\nProcessed {total_links} links: {success_count} successful, {error_count} with errors.")
return result
def get_indexed_links_with_type_multiline():
"""
Prompts the user to enter one link per line.
Stops when an empty line is entered (or EOF is sent).
Returns a dict mapping each index (starting at 1) to:
{
"link": <the URL>,
"type": "video" or "web",
"details": {
For videos: title, channel, provider
For web pages: title, site
}
}
"""
print("Enter each link on its own line. Submit an empty line (or EOF) to finish:")
links = []
try:
while True:
try:
line = input().strip()
if not line:
break
links.append(line)
except EOFError:
break
except KeyboardInterrupt:
# Handle Ctrl+C gracefully
print("\nInput interrupted.")
# Process links even if the list is empty (will return an empty dict)
return process_links(links)
if __name__ == "__main__":
import sys
import os
try:
print("Copyright Resources Parser")
print("-------------------------")
# Check if links are provided as command-line arguments
if len(sys.argv) > 1:
print("Processing links from command-line arguments...")
links = sys.argv[1:]
indexed = process_links(links)
else:
# Check if stdin has data (piped input)
try:
# Check if stdin is a terminal or has data
if not os.isatty(sys.stdin.fileno()):
print("Processing links from stdin (piped input)...")
links = []
# Set a timeout for reading from stdin
import select
# Check if there's data available to read from stdin
if select.select([sys.stdin], [], [], 0.0)[0]:
# Read all available lines from stdin with a timeout
for line in sys.stdin:
line = line.strip()
if line: # Skip empty lines
links.append(line)
print(f"Read link: {line}")
else:
print("No data available from stdin.")
if links:
indexed = process_links(links)
else:
print("No links were read from stdin.")
indexed = {}
else:
# Interactive mode
print("Starting interactive mode...")
indexed = get_indexed_links_with_type_multiline()
except (AttributeError, ValueError, OSError) as e:
print(f"Error detecting input mode: {str(e)}")
print("Falling back to interactive mode...")
indexed = get_indexed_links_with_type_multiline()
print("\nFormatted Citations:")
print("-------------------")
if not indexed:
print("No links were processed successfully.")
else:
# Print formatted citations
for idx, info in indexed.items():
citation = format_citation_dstu_8302_2015(info['link'], info['details'], info['type'])
print(f"{idx}: {citation}")
print("\nProcessing complete.")
except Exception as e:
print(f"An unexpected error occurred: {str(e)}")
import traceback
traceback.print_exc()
sys.exit(1)