Skip to content

Commit

Permalink
Updated downloaded filename to a more organized pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
MacielG1 committed Jun 26, 2024
1 parent b23f8e6 commit ac6621b
Showing 1 changed file with 161 additions and 46 deletions.
207 changes: 161 additions & 46 deletions vod_recovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def print_download_type_menu():

def print_handle_m3u8_availability_menu():
handle_m3u8_availability_options = [
"1) Check if M3U8 file is muted",
"1) Check if M3U8 file has muted segments",
"2) Unmute & Remove invalid segments",
"3) Return"
]
Expand Down Expand Up @@ -259,6 +259,7 @@ def print_get_twitch_url_menu():
def get_twitch_or_tracker_url():
while True:
url = input("Enter Twitchtracker/Streamscharts/Sullygnome/Twitch URL: ").strip()

if re.match(r'^(https?:\/\/)?(www\.)?(twitchtracker\.com|streamscharts\.com|sullygnome\.com|twitch\.tv)\/.*', url):
return url

Expand Down Expand Up @@ -591,7 +592,7 @@ def get_m3u8_file_dialog():
def parse_vod_filename(m3u8_video_filename):
base = os.path.basename(m3u8_video_filename)
streamer_name, video_id = base.split('.m3u8', 1)[0].rsplit('_', 1)
return f"{streamer_name}_{video_id}"
return streamer_name, video_id


def parse_vod_filename_with_Brackets(m3u8_video_filename):
Expand Down Expand Up @@ -789,7 +790,7 @@ def handle_vod_recover(url, url_parser, datetime_parser, website_name):

if source_duration and int(source_duration) >= m3u8_duration + 10:
print(f"The duration from {website_name} exceeds the M3U8 duration by at least 10 seconds. This may indicate a split stream.\n")
return m3u8_source
return m3u8_source, stream_datetime


def website_vod_recover():
Expand Down Expand Up @@ -1441,7 +1442,7 @@ def clip_recover(streamer, video_id, duration):

for response in grequests.imap(rs, size=100):
iteration_counter += 1
print(f'\rSearching for clips..... {iteration_counter} of {len(full_url_list)}', end=" ", flush=True)
print(f'\rSearching for clips... {iteration_counter} of {len(full_url_list)}', end=" ", flush=True)
if response.status_code == 200:
valid_counter += 1
valid_url_list.append(response.url)
Expand Down Expand Up @@ -1846,7 +1847,6 @@ def download_m3u8_video_file_slice(m3u8_file_path, output_filename, video_start_
'-ss', video_start_time,
'-to', video_end_time,
'-i', m3u8_file_path,

'-c', 'copy',
# '-c:a', 'aac',
'-y',
Expand Down Expand Up @@ -1892,32 +1892,72 @@ def get_VLC_Location():
return None


def handle_vod_url_normal(m3u8_source, title=None):

def handle_vod_url_normal(m3u8_source, title=None, stream_date=None):
start = time()
is_file = os.path.isfile(m3u8_source)


if is_file:
if title:
vod_filename = sanitize_filename(title) + parse_vod_filename_with_Brackets(m3u8_source) + get_default_video_format()
else:
vod_filename = parse_vod_filename(m3u8_source) + get_default_video_format()
vod_filename = get_filename_for_file_source(m3u8_source, title=title, stream_date=stream_date)
print(f"\nDownloading Vod: {vod_filename}")

download_m3u8_video_file(m3u8_source, vod_filename)
os.remove(m3u8_source)
else:
if title:
vod_filename = sanitize_filename(title) + parse_streamer_and_video_id_from_m3u8_link(m3u8_source) + get_default_video_format()
else:
vod_filename = f"{parse_streamer_from_m3u8_link(m3u8_source)}_{parse_video_id_from_m3u8_link(m3u8_source)}{get_default_video_format()}"
vod_filename = get_filename_for_url_source(m3u8_source, title=title, stream_date=stream_date)
print(f"\nDownloading Vod: {vod_filename}")

download_m3u8_video_url(m3u8_source, vod_filename)

formatted_elapsed = str(timedelta(seconds=int(time() - start))).zfill(8)
print(f"\n\033[92m\u2713 Vod downloaded to {os.path.join(get_default_directory(), vod_filename)} in {formatted_elapsed}\033[0m\n")


def handle_vod_url_trim(m3u8_source, title=None):
def format_date(date_string):
try:
return datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S").strftime("%Y-%m-%d")
except ValueError:
return None


def get_filename_for_file_source(m3u8_source, title, stream_date):
streamer_name, video_id = parse_vod_filename(m3u8_source)
formatted_date = format_date(stream_date) if stream_date else None

filename_parts = [streamer_name]

if formatted_date:
filename_parts.append(formatted_date)

if title:
filename_parts.append(sanitize_filename(title))

filename_parts.append(f"[{video_id}]")
filename = " - ".join(filename_parts) + get_default_video_format()

return filename


def get_filename_for_url_source(m3u8_source, title, stream_date):
streamer = parse_streamer_from_m3u8_link(m3u8_source)
vod_id = parse_video_id_from_m3u8_link(m3u8_source)
formatted_date = format_date(stream_date) if stream_date else None

filename_parts = [streamer]

if formatted_date:
filename_parts.append(formatted_date)

if title:
filename_parts.append(sanitize_filename(title))

filename_parts.append(f"[{vod_id}]")
filename = " - ".join(filename_parts) + get_default_video_format()

return filename


def handle_vod_url_trim(m3u8_source, title=None, stream_date=None):
vod_start_time = get_time_input_HH_MM_SS("Enter start time (HH:MM:SS): ")
vod_end_time = get_time_input_HH_MM_SS("Enter end time (HH:MM:SS): ")

Expand All @@ -1926,66 +1966,100 @@ def handle_vod_url_trim(m3u8_source, title=None):

is_file = os.path.isfile(m3u8_source)
if is_file:
if title:
vod_filename = sanitize_filename(title) + parse_vod_filename_with_Brackets(m3u8_source) + get_default_video_format()
else:
vod_filename = parse_vod_filename(m3u8_source) + get_default_video_format()
vod_filename = get_filename_for_file_trim(m3u8_source, title, stream_date, raw_start_time, raw_end_time)
download_m3u8_video_file_slice(m3u8_source, vod_filename, vod_start_time, vod_end_time)

if os.path.isfile(m3u8_source):
os.remove(m3u8_source)
else:
if title:
vod_filename = f"{sanitize_filename(title)}{parse_streamer_and_video_id_from_m3u8_link(m3u8_source)} - {raw_start_time} - {raw_end_time}{get_default_video_format()}"
else:
vod_filename = f"{parse_streamer_from_m3u8_link(m3u8_source)}_{parse_video_id_from_m3u8_link(m3u8_source)} - {raw_start_time} - {raw_end_time}{get_default_video_format()}"
vod_filename = get_filename_for_url_trim(m3u8_source, title, stream_date, raw_start_time, raw_end_time)
download_m3u8_video_url_slice(m3u8_source, vod_filename, vod_start_time, vod_end_time)

print(f"\n\033[92m\u2713 Vod downloaded to {os.path.join(get_default_directory(), vod_filename)}\033[0m\n")


def get_filename_for_file_trim(m3u8_source, title, stream_date, raw_start_time, raw_end_time):
streamer_name, video_id = parse_vod_filename(m3u8_source)
formatted_date = format_date(stream_date) if stream_date else None

filename_parts = [streamer_name]

if formatted_date:
filename_parts.append(formatted_date)

if title:
filename_parts.append(sanitize_filename(title))

filename_parts.append(f"[{video_id}]")
filename_parts.extend([raw_start_time, raw_end_time])

filename = " - ".join(filename_parts) + get_default_video_format()

return filename


def get_filename_for_url_trim(m3u8_source, title, stream_date, raw_start_time, raw_end_time):
streamer = parse_streamer_from_m3u8_link(m3u8_source)
vod_id = parse_video_id_from_m3u8_link(m3u8_source)
formatted_date = format_date(stream_date) if stream_date else None

filename_parts = [streamer]

if formatted_date:
filename_parts.append(formatted_date)

if title:
filename_parts.append(sanitize_filename(title))

filename_parts.append(f"[{vod_id}]")
filename_parts.extend([raw_start_time, raw_end_time])

filename = " - ".join(filename_parts) + get_default_video_format()

return filename


def get_time_input_HH_MM_SS(prompt):
while True:
time_input = input(prompt).strip().replace("'", "").replace('"', '')
if re.match(r'^(\d+):([0-5]\d):([0-5]\d)$', time_input):
return time_input
else:
print("\nInvalid input format! Please enter the time in HH:MM:SS format.\n")

print("\nInvalid input format! Please enter the time in HH:MM:SS format.\n")


def get_time_input_HH_MM(prompt):
while True:
time_input = input(prompt).strip().replace("'", "").replace('"', '')

# Check if the input follows the HH:MM format
if re.match(r'^(\d+):([0-5]\d)$', time_input):
return time_input
else:
print("\nInvalid input format! Please enter the time in HH:MM format.\n")

print("\nInvalid input format! Please enter the time in HH:MM format.\n")


def get_time_input_YYYY_MM_DD_HH_MM_SS(prompt):
while True:
time_input = input(prompt).strip().replace("'", "").replace('"', '')

# Check if the input follows the YYYY-MM-DD HH:MM:SS format
if re.match(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$', time_input):
return time_input
else:
print("\nInvalid input format! Please enter the time in YYYY-MM-DD HH:MM:SS format.\n")

print("\nInvalid input format! Please enter the time in YYYY-MM-DD HH:MM:SS format.\n")

def handle_download_menu(link, title=None):

def handle_download_menu(link, title=None, stream_datetime=None):
vlc_location = get_VLC_Location()
exit_option = 3 if not vlc_location else 4

while True:
start_download = print_confirm_download_menu()
if start_download == 1:
handle_vod_url_normal(link, title)
handle_vod_url_normal(link, title, stream_datetime)
input("Press Enter to continue...")
return run_vod_recover()
elif start_download == 2:
handle_vod_url_trim(link, title)
handle_vod_url_trim(link, title, stream_datetime)
input("Press Enter to continue...")
return run_vod_recover()
elif start_download == 3 and vlc_location:
Expand All @@ -1999,7 +2073,30 @@ def handle_download_menu(link, title=None):
print("\n✖ Invalid option! Please Try Again.")


def get_datetime_from_m3u8(m3u8_file):
try:
date = None
total_seconds = 0
date_pattern = re.compile(r'#ID3-EQUIV-TDTG:(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})')

with open(m3u8_file, 'r', encoding='utf-8') as f:
for line in f:
date_match = date_pattern.match(line)
if date_match:
date = date_match.group(1)
if line.startswith('#EXT-X-TWITCH-TOTAL-SECS:'):
total_seconds = int(float(line.split(':')[-1].strip()))
if date is not None:
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S")
adjusted_date = date - timedelta(seconds=total_seconds)
adjusted_date_str = adjusted_date.strftime("%Y-%m-%d")
return adjusted_date_str
except Exception:
pass


def handle_file_download_menu(m3u8_file_path):
stream_date = get_datetime_from_m3u8(m3u8_file_path)

vlc_location = get_VLC_Location()
exit_option = 3 if not vlc_location else 4
Expand All @@ -2008,7 +2105,14 @@ def handle_file_download_menu(m3u8_file_path):
start_download = print_confirm_download_menu()
if start_download == 1:
start = time()
output_filename = parse_vod_filename(m3u8_file_path) + get_default_video_format()

streamer_name, video_id = parse_vod_filename(m3u8_file_path)

if stream_date:
output_filename = f"{streamer_name} - {stream_date} - [{video_id}]{get_default_video_format()}"
else:
output_filename = f"{streamer_name} - [{video_id}]{get_default_video_format()}"

download_m3u8_video_file(m3u8_file_path, output_filename)

formatted_elapsed = str(timedelta(seconds=int(time() - start))).zfill(8)
Expand All @@ -2021,7 +2125,11 @@ def handle_file_download_menu(m3u8_file_path):
raw_start_time = vod_start_time.replace(":", ".")
raw_end_time = vod_end_time.replace(":", ".")

vod_filename = f"{parse_vod_filename(m3u8_file_path)} - {raw_start_time} - {raw_end_time}{get_default_video_format()}"
streamer_name, video_id = parse_vod_filename(m3u8_file_path)
if stream_date:
vod_filename = f"{streamer_name} - {stream_date} - [{video_id}] - {raw_start_time} - {raw_end_time}{get_default_video_format()}"
else:
vod_filename = f"{streamer_name} - [{video_id}] - {raw_start_time} - {raw_end_time}{get_default_video_format()}"

download_m3u8_video_file_slice(m3u8_file_path, vod_filename, vod_start_time, vod_end_time)

Expand All @@ -2037,7 +2145,7 @@ def handle_file_download_menu(m3u8_file_path):

def print_confirm_download_menu():
vlc_location = get_VLC_Location()
menu_options = ["1) Start Downloading", "2) Specify Start and End time"]
menu_options = ["1) Start Downloading", "2) Specify Start & End times"]
if vlc_location:
menu_options.append("3) Play with VLC")
menu_options.append(f"{3 if not vlc_location else 4}) Return")
Expand Down Expand Up @@ -2093,8 +2201,8 @@ def get_vod_or_highlight_url(vod_id):
vod_data = data['data']['video']

if data is None or vod_data is None:
return None, None

return None, None, None
current_url = urlparse(vod_data['seekPreviewsURL'])

domain = current_url.netloc
Expand All @@ -2120,24 +2228,30 @@ def get_vod_or_highlight_url(vod_id):
if url is not None:
response = requests.get(url, timeout=20)
if response.status_code == 200:
return url, vod_data["title"]
return response.url, None
return url, vod_data["title"], vod_data["createdAt"]
return response.url, None, None


def twitch_recover(link=None):
url = link if link else print_get_twitch_url_menu()
vod_id = extract_id_from_url(url)
url, title = get_vod_or_highlight_url(vod_id)
url, title, stream_datetime = get_vod_or_highlight_url(vod_id)

if url is None:
print("\n✖ Unable to find it! Try using one of the other websites.\n")
input("Press Enter to continue...")
return run_vod_recover()

try:
format_datetime = datetime.strptime(stream_datetime, "%Y-%m-%dT%H:%M:%SZ").strftime("%Y-%m-%d %H:%M:%S")
except Exception:
format_datetime = None

m3u8_url = return_supported_qualities(url)
print(f"\n\033[92m\u2713 Found URL: {m3u8_url}\033[0m")

m3u8_source = process_m3u8_configuration(m3u8_url, skip_check=True)
return handle_download_menu(m3u8_source, title)
return handle_download_menu(m3u8_source, title=title, stream_datetime=format_datetime)


def get_twitch_clip(clip_slug):
Expand Down Expand Up @@ -2180,6 +2294,7 @@ def twitch_clip_downloader(clip_url, slug, streamer):
raise Exception('Unable to download clip!')
download_location = os.path.join(get_default_directory(), f"{streamer}-{slug}{get_default_video_format()}")
start = time()

with open(os.path.join(get_default_directory(), download_location), 'wb') as file:
copyfileobj(response.raw, file)

Expand Down Expand Up @@ -2208,8 +2323,8 @@ def run_vod_recover():
if menu == 1:
vod_mode = print_video_mode_menu()
if vod_mode == 1:
link = website_vod_recover()
handle_download_menu(link)
link, stream_datetime = website_vod_recover()
handle_download_menu(link, stream_datetime=stream_datetime)
elif vod_mode == 2:
manual_vod_recover()
elif vod_mode == 3:
Expand Down

0 comments on commit ac6621b

Please sign in to comment.