-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
165 lines (135 loc) · 5.94 KB
/
Copy pathutils.py
File metadata and controls
165 lines (135 loc) · 5.94 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
import re
import hashlib
from datetime import datetime
from typing import Union, Optional
# Helper function for sanitizing filenames
def sanitize_filename(name: str) -> str:
"""
Removes or replaces characters unsafe for filenames.
:param name: Filename to sanitize
:return: Sanitized filename or 'untitled'
"""
# Remove characters that are problematic
name = re.sub(r'[\\/*?:"<>|]', "", name)
# Replace sequences of dots or spaces with a single underscore
name = re.sub(r'\.+', '_', name)
name = re.sub(r'\s+', '_', name)
# Ensure it doesn't start/end with problematic chars like space or dot
name = name.strip(' .')
# Limit length of the name
max_len = 200
if len(name) > max_len:
name = name[:max_len]
return name if name else "untitled" # Ensure not empty
def normalize_title_for_comparison(title: str) -> str:
"""
Normalizes a title for comparison by:
1. Converting to lowercase
2. Removing common separators and special characters
3. Removing common words that might be added/removed in filenames
:param title: Title string to normalize
:return: Normalized title string
"""
# Convert to lowercase
normalized = title.lower()
# Replace underscores with spaces (since sanitize_filename replaces spaces with underscores)
normalized = normalized.replace('_', ' ')
# Remove special characters and extra spaces
normalized = re.sub(r'[^\w\s]', '', normalized)
normalized = re.sub(r'\s+', ' ', normalized).strip()
# Remove common words that might be added by YouTube or downloaders
common_words = [
'official', 'video', 'hd', '4k',
]
for word in common_words:
normalized = normalized.replace(f' {word} ', ' ')
# Remove any leading/trailing common words
for word in common_words:
if normalized.startswith(f'{word} '):
normalized = normalized[len(word)+1:]
if normalized.endswith(f' {word}'):
normalized = normalized[:-len(word)-1]
return normalized.strip()
def normalize_vtt_for_hash(vtt: str) -> str:
"""
Normalizes VTT (WebVTT) content for consistent hashing.
This function ensures that the same transcript content always produces
the same hash, regardless of platform-specific line endings or trailing
whitespace variations.
Normalization steps:
1. Converts all line endings (\r\n, \r) to \n
2. Removes trailing whitespace from each line (preserves leading whitespace)
3. Joins lines with consistent \n separator
Leading whitespace is preserved as it may be significant for VTT formatting,
but trailing whitespace is removed as it's never meaningful and varies by editor.
:param vtt: Raw VTT content string
:return: Normalized VTT string suitable for hashing
Example:
>>> vtt = "WEBVTT\r\n\r\n00:00:00.000 --> 00:00:05.000 \nHello world \n"
>>> normalized = normalize_vtt_for_hash(vtt)
>>> # Produces consistent output regardless of input line endings
"""
if not vtt:
return ""
# Normalize line endings and remove trailing whitespace from each line
return '\n'.join(
line.rstrip()
for line in vtt.replace('\r\n', '\n').replace('\r', '\n').split('\n')
)
def compute_vtt_hash(vtt: str) -> str:
"""
Computes a SHA-256 hash of normalized VTT content.
This function provides a consistent way to detect changes in transcript content
across the application. The same transcript will always produce the same hash,
even if it comes from different sources or has minor formatting differences.
:param vtt: Raw VTT content string
:return: Hexadecimal SHA-256 hash string (64 characters)
Example:
>>> vtt = "WEBVTT\n\n00:00:00.000 --> 00:00:05.000\nHello\n"
>>> hash_value = compute_vtt_hash(vtt)
>>> len(hash_value)
64
"""
normalized = normalize_vtt_for_hash(vtt)
return hashlib.sha256(normalized.encode('utf-8')).hexdigest()
def format_datetime(upload_date: Optional[str] = None,
timestamp: Optional[int] = None,
dt: Optional[datetime] = None) -> Optional[str]:
"""
Standardized datetime formatter for the application.
Returns datetime in SQLite CURRENT_TIMESTAMP format: 'YYYY-MM-DD HH:MM:SS'
:param upload_date: Date in YYYYMMDD format (e.g., '20210310')
or ISO format (e.g., '2021-03-10')
:param timestamp: Unix timestamp in seconds (e.g., 1615358397)
:param dt: Python datetime object
:return: Formatted datetime string or None if conversion fails
"""
try:
if dt and isinstance(dt, datetime):
# Use provided datetime object directly
return dt.strftime('%Y-%m-%d %H:%M:%S')
if timestamp:
# Use timestamp for full date and time
dt = datetime.fromtimestamp(timestamp)
return dt.strftime('%Y-%m-%d %H:%M:%S')
elif upload_date:
# Handle date-only values
try:
# Try standard format YYYYMMDD
dt = datetime.strptime(upload_date, '%Y%m%d')
# Return with time set to 00:00:00 for consistency
return dt.strftime('%Y-%m-%d %H:%M:%S')
except ValueError:
# Try alternative formats
for fmt in ['%Y-%m-%d', '%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%d %H:%M:%S']:
try:
dt = datetime.strptime(upload_date, fmt)
return dt.strftime('%Y-%m-%d %H:%M:%S')
except ValueError:
continue
# If we get here, none of the formats matched
raise ValueError(f"Unrecognized date format: {upload_date}")
return None
except Exception as e:
print(f"Error formatting date: {str(e)}")
return None