mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-29 14:13:22 -08:00
feat(player): implement continue from timestamp
This commit is contained in:
@@ -70,8 +70,18 @@ class Config(object):
|
||||
self.user = user
|
||||
user_data_helper.update_user_info(user)
|
||||
|
||||
def update_watch_history(self, anime_id: int, episode: str | None):
|
||||
self.watch_history.update({str(anime_id): episode})
|
||||
def update_watch_history(
|
||||
self, anime_id: int, episode: str | None, start_time="0", total_time="0"
|
||||
):
|
||||
self.watch_history.update(
|
||||
{
|
||||
str(anime_id): {
|
||||
"episode": episode,
|
||||
"start_time": start_time,
|
||||
"total_time": total_time,
|
||||
}
|
||||
}
|
||||
)
|
||||
user_data_helper.update_watch_history(self.watch_history)
|
||||
|
||||
def update_anime_list(self, anime_id: int, remove=False):
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
import random
|
||||
from datetime import datetime
|
||||
|
||||
from rich import print
|
||||
from rich.progress import Progress
|
||||
@@ -20,6 +21,19 @@ from ..utils.tools import QueryDict, exit_app
|
||||
from ..utils.utils import clear, fuzzy_inquirer
|
||||
|
||||
|
||||
def calculate_time_delta(start_time, end_time):
|
||||
time_format = "%H:%M:%S"
|
||||
|
||||
# Convert string times to datetime objects
|
||||
start = datetime.strptime(start_time, time_format)
|
||||
end = datetime.strptime(end_time, time_format)
|
||||
|
||||
# Calculate the difference
|
||||
delta = end - start
|
||||
|
||||
return delta
|
||||
|
||||
|
||||
def player_controls(config: Config, anilist_config: QueryDict):
|
||||
# user config
|
||||
config.translation_type.lower()
|
||||
@@ -46,8 +60,25 @@ def player_controls(config: Config, anilist_config: QueryDict):
|
||||
current_episode,
|
||||
)
|
||||
|
||||
mpv(current_link, selected_server["episode_title"])
|
||||
start_time = config.watch_history[str(anime_id)]["start_time"]
|
||||
print("[green]Continuing from:[/] ", start_time)
|
||||
stop_time, total_time = mpv(
|
||||
current_link, selected_server["episode_title"], start_time=start_time
|
||||
)
|
||||
if stop_time == "0":
|
||||
episode = str(int(current_episode) + 1)
|
||||
else:
|
||||
error = 5 * 60
|
||||
delta = calculate_time_delta(stop_time, total_time)
|
||||
if delta.total_seconds() > error:
|
||||
episode = current_episode
|
||||
else:
|
||||
episode = str(int(current_episode) + 1)
|
||||
stop_time = "0"
|
||||
total_time = "0"
|
||||
|
||||
clear()
|
||||
config.update_watch_history(anime_id, episode, stop_time, total_time)
|
||||
player_controls(config, anilist_config)
|
||||
|
||||
def _next_episode():
|
||||
@@ -220,14 +251,34 @@ def fetch_streams(config: Config, anilist_config: QueryDict):
|
||||
{
|
||||
"mediaId": anime_id,
|
||||
"status": "CURRENT",
|
||||
"progress": episode_number if episode_number else 1,
|
||||
"progress": episode_number if episode_number else 0,
|
||||
}
|
||||
)
|
||||
|
||||
mpv(stream_link, selected_server["episode_title"])
|
||||
start_time = config.watch_history.get(str(anime_id), {}).get("start_time", "0")
|
||||
if start_time != "0":
|
||||
print("[green]Continuing from:[/] ", start_time)
|
||||
stop_time, total_time = mpv(
|
||||
stream_link, selected_server["episode_title"], start_time=start_time
|
||||
)
|
||||
print("Finished at: ", stop_time)
|
||||
|
||||
# update_watch_history
|
||||
config.update_watch_history(anime_id, str(int(episode_number) + 1))
|
||||
if stop_time == "0":
|
||||
episode = str(int(episode_number) + 1)
|
||||
else:
|
||||
error = 5 * 60
|
||||
delta = calculate_time_delta(stop_time, total_time)
|
||||
if delta.total_seconds() > error:
|
||||
episode = episode_number
|
||||
else:
|
||||
episode = str(int(episode_number) + 1)
|
||||
stop_time = "0"
|
||||
total_time = "0"
|
||||
|
||||
config.update_watch_history(
|
||||
anime_id, episode, start_time=stop_time, total_time=total_time
|
||||
)
|
||||
|
||||
# switch to controls
|
||||
clear()
|
||||
@@ -249,8 +300,11 @@ def fetch_episode(config: Config, anilist_config: QueryDict):
|
||||
|
||||
# prompt for episode number
|
||||
episodes = anime["availableEpisodesDetail"][translation_type]
|
||||
if continue_from_history and user_watch_history.get(str(anime_id)) in episodes:
|
||||
episode_number = user_watch_history[str(anime_id)]
|
||||
if (
|
||||
continue_from_history
|
||||
and user_watch_history.get(str(anime_id), {}).get("episode") in episodes
|
||||
):
|
||||
episode_number = user_watch_history[str(anime_id)]["episode"]
|
||||
print(f"[bold cyan]Continuing from Episode:[/] [bold]{episode_number}[/]")
|
||||
else:
|
||||
choices = [*episodes, "Back"]
|
||||
@@ -266,7 +320,8 @@ def fetch_episode(config: Config, anilist_config: QueryDict):
|
||||
if episode_number == "Back":
|
||||
anilist_options(config, anilist_config)
|
||||
return
|
||||
config.update_watch_history(anime_id, episode_number)
|
||||
start_time = user_watch_history.get(str(anime_id), {}).get("start_time", "0")
|
||||
config.update_watch_history(anime_id, episode_number, start_time=start_time)
|
||||
|
||||
# update internal config
|
||||
anilist_config.episodes = episodes
|
||||
|
||||
@@ -3,7 +3,6 @@ import shutil
|
||||
import subprocess
|
||||
from typing import Optional
|
||||
|
||||
|
||||
# legacy
|
||||
# def mpv(link, title: None | str = "anime", *custom_args):
|
||||
# MPV = shutil.which("mpv")
|
||||
@@ -25,8 +24,50 @@ from typing import Optional
|
||||
# else:
|
||||
# subprocess.run([MPV, *custom_args, f"--title={title}", link])
|
||||
#
|
||||
#
|
||||
def mpv(link: str, title: Optional[str] = "anime", *custom_args):
|
||||
|
||||
|
||||
def stream_video(url, mpv_args):
|
||||
process = subprocess.Popen(
|
||||
["mpv", url, *mpv_args],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
)
|
||||
|
||||
last_time = None
|
||||
av_time_pattern = re.compile(r"AV: ([0-9:]*) / ([0-9:]*) \(([0-9]*)%\)")
|
||||
last_time = "0"
|
||||
total_time = "0"
|
||||
|
||||
try:
|
||||
while True:
|
||||
output = process.stderr.readline()
|
||||
|
||||
if output:
|
||||
# Match the timestamp in the output
|
||||
match = av_time_pattern.search(output.strip())
|
||||
if match:
|
||||
current_time = match.group(1)
|
||||
total_time = match.group(2)
|
||||
match.group(3)
|
||||
last_time = current_time
|
||||
# print(f"Current stream time: {current_time}, Total time: {total_time}, Progress: {percentage}%")
|
||||
|
||||
# Check if the process has terminated
|
||||
retcode = process.poll()
|
||||
if retcode is not None:
|
||||
print("Finshed at: ", last_time)
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
finally:
|
||||
process.terminate()
|
||||
|
||||
return last_time, total_time
|
||||
|
||||
|
||||
def mpv(link: str, title: Optional[str] = "anime", start_time: str = "0", *custom_args):
|
||||
# Determine if mpv is available
|
||||
MPV = shutil.which("mpv")
|
||||
|
||||
@@ -54,6 +95,7 @@ def mpv(link: str, title: Optional[str] = "anime", *custom_args):
|
||||
"-n",
|
||||
"com.google.android.youtube/.UrlActivity",
|
||||
]
|
||||
return "0"
|
||||
else:
|
||||
# Android specific commands to launch mpv with a regular URL
|
||||
args = [
|
||||
@@ -71,10 +113,12 @@ def mpv(link: str, title: Optional[str] = "anime", *custom_args):
|
||||
]
|
||||
|
||||
subprocess.run(args)
|
||||
return "0"
|
||||
else:
|
||||
# General mpv command with custom arguments
|
||||
mpv_args = [MPV, *custom_args, f"--title={title}", link]
|
||||
subprocess.run(mpv_args)
|
||||
mpv_args = [*custom_args, f"--title={title}", f"--start={start_time}"]
|
||||
stop_time, total_time = stream_video(link, mpv_args)
|
||||
return stop_time, total_time
|
||||
|
||||
|
||||
# Example usage
|
||||
|
||||
Reference in New Issue
Block a user