Skip to content

Commit

Permalink
Merge pull request #333 from eichblatt/dev
Browse files Browse the repository at this point in the history
Allow explicit Collectin Names
  • Loading branch information
eichblatt authored Jan 29, 2023
2 parents ed62f1a + f53b912 commit 82f6b2f
Show file tree
Hide file tree
Showing 10 changed files with 436 additions and 433 deletions.
154 changes: 56 additions & 98 deletions timemachine/Archivary.py

Large diffs are not rendered by default.

160 changes: 88 additions & 72 deletions timemachine/GD.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,22 @@
import time


#from timemachine.mpv import MPV
# from timemachine.mpv import MPV
from mpv import MPV
from tenacity import retry
from tenacity.stop import stop_after_delay, stop_after_attempt
from tenacity.wait import wait_random
from tenacity.retry import retry_if_result
from typing import Callable, Optional

logging.basicConfig(format='%(asctime)s.%(msecs)03d %(levelname)s: %(name)s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S')
logging.basicConfig(
format="%(asctime)s.%(msecs)03d %(levelname)s: %(name)s %(message)s",
level=logging.INFO,
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
BIN_DIR = os.path.join(os.path.dirname(ROOT_DIR), 'bin')
BIN_DIR = os.path.join(os.path.dirname(ROOT_DIR), "bin")


@retry(stop=stop_after_delay(30))
Expand All @@ -45,10 +49,12 @@ def return_last_value(retry_state):
return retry_state.outcome.result()


@retry(stop=stop_after_attempt(7),
wait=wait_random(min=1, max=2),
retry=retry_if_result(lambda x: not x),
retry_error_callback=return_last_value)
@retry(
stop=stop_after_attempt(7),
wait=wait_random(min=1, max=2),
retry=retry_if_result(lambda x: not x),
retry_error_callback=return_last_value,
)
def retry_until_true(callable: Callable, *args, **kwargs):
return callable(*args, **kwargs)

Expand All @@ -60,21 +66,23 @@ def helper(x):
if x not in memo:
memo[x] = f(x)
return memo[x]

return helper


# @memoize
def to_date(datestring): return datetime.datetime.fromisoformat(datestring)
def to_date(datestring):
return datetime.datetime.fromisoformat(datestring)


def to_year(datestring):
if type(datestring) == list: # handle one bad case on 2009.01.10
if type(datestring) == list: # handle one bad case on 2009.01.10
datestring = datestring[0]
return to_date(datestring[:10]).year


def to_decade(datestring):
if type(datestring) == list: # handle one bad case on 2009.01.10
if type(datestring) == list: # handle one bad case on 2009.01.10
datestring = datestring[0]
return 10 * divmod(to_date(datestring[:10]).year, 10)[0]

Expand All @@ -87,8 +95,8 @@ def __init__(self, tape=None):
# self._set_property('prefetch-playlist','yes')
# self._set_property('cache-dir','/home/steve/cache')
# self._set_property('cache-on-disk','yes')
self._set_property('audio-buffer', 10.0) # This allows to play directly from the html without a gap!
self._set_property('cache', 'yes')
self._set_property("audio-buffer", 10.0) # This allows to play directly from the html without a gap!
self._set_property("cache", "yes")
self.tape = None
self.download_when_possible = False

Expand All @@ -106,13 +114,13 @@ def __repr__(self):

def set_audio_device(self, audio_device=None):
# check to see if pulse audio daemon is running system-wide before setting audio device
if audio_device == 'pulse' and os.path.exists('/etc/systemd/system/pulseaudio.service'):
self.default_audio_device = 'pulse'
if audio_device == "pulse" and os.path.exists("/etc/systemd/system/pulseaudio.service"):
self.default_audio_device = "pulse"
else:
self.default_audio_device = 'auto'
self._set_property('audio-device', self.default_audio_device)
self.default_audio_device = "auto"
self._set_property("audio-device", self.default_audio_device)

if self.default_audio_device == 'pulse':
if self.default_audio_device == "pulse":
self.restart_pulse_audio()
else:
self.stop_pulse_audio()
Expand All @@ -135,10 +143,10 @@ def extract_urls(self, tape): # NOTE this should also give a list of backup URL
best_track = None
candidates = []
for f in track_files:
if f['format'] == preferred_format:
best_track = f['url']
elif f['format'] in playable_formats:
candidates.append(f['url'])
if f["format"] == preferred_format:
best_track = f["url"]
elif f["format"] in playable_formats:
candidates.append(f["url"])
if best_track is None and len(candidates) > 0:
best_track = candidates[0]
urls.append(best_track)
Expand All @@ -149,12 +157,12 @@ def create_playlist(self):
urls = self.extract_urls(self.tape)
if len(urls) == 0:
self.tape._remove_from_archive = True
self.command('loadfile', urls[0])
self.command("loadfile", urls[0])
if len(urls) > 1:
_ = [self.command('loadfile', x, 'append') for x in urls[1:]]
_ = [self.command("loadfile", x, "append") for x in urls[1:]]
self.playlist_pos = 0
self.pause()
logger.info(F"Playlist {self.playlist}")
logger.info(f"Playlist {self.playlist}")
return

def stop_pulse_audio(self):
Expand All @@ -170,16 +178,16 @@ def restart_pulse_audio(self):

def reset_audio_device(self, kwarg=None):
logger.info("in reset_audio_device")
if self.get_prop('audio-device') == 'null':
logger.info(F"changing audio-device to {self.default_audio_device}")
if self.get_prop("audio-device") == "null":
logger.info(f"changing audio-device to {self.default_audio_device}")
audio_device = self.default_audio_device
if audio_device == 'pulse':
if audio_device == "pulse":
self.restart_pulse_audio()
else:
self.stop_pulse_audio()
self._set_property('audio-device', audio_device)
self.wait_for_property('audio-device', lambda v: v == audio_device)
if self.get_prop('current-ao') is None:
self._set_property("audio-device", audio_device)
self.wait_for_property("audio-device", lambda v: v == audio_device)
if self.get_prop("current-ao") is None:
logger.warning("Current-ao is None")
# self.stop()
return False
Expand All @@ -193,13 +201,13 @@ def play(self, wait=True):
if not retry_until_true(self.reset_audio_device, None):
logger.warning("Failed to reset audio device when playing")
logger.debug("playing")
self._set_property('pause', False)
self._set_property("pause", False)
if wait:
self.wait_until_playing() # blocking occasionally here.
self.wait_until_playing() # blocking occasionally here.

def pause(self, wait=True):
logger.debug("pausing")
self._set_property('pause', True)
self._set_property("pause", True)
if wait:
self.wait_until_paused()

Expand All @@ -208,30 +216,30 @@ def stop(self):
self.pause()

def next(self, blocking=False):
pos = self.get_prop('playlist-pos')
pos = self.get_prop("playlist-pos")
if pos is None or pos + 1 == len(self.playlist):
return
self.command('playlist-next')
self.command("playlist-next")
if blocking:
self.wait_for_event('file-loaded')
self.wait_for_event("file-loaded")

def prev(self):
pos = self.get_prop('playlist-pos')
pos = self.get_prop("playlist-pos")
if pos is None or pos == 0:
return
self.command('playlist-prev')
self.command("playlist-prev")

def time_remaining(self):
icounter = 0
self.wait_for_property('time-remaining', lambda v: v is not None)
time_remaining = self.get_prop('time-remaining')
self.wait_for_property("time-remaining", lambda v: v is not None)
time_remaining = self.get_prop("time-remaining")
while time_remaining is None and icounter < 20:
logger.info(F'time-remaining is {time_remaining},icounter:{icounter},playlist:{self.playlist}')
logger.info(f"time-remaining is {time_remaining},icounter:{icounter},playlist:{self.playlist}")
time.sleep(1)
icounter = icounter + 1
time_remaining = self.get_prop('time-remaining')
time_remaining = self.get_prop("time-remaining")
self.status()
logger.debug(F'time-remaining is {time_remaining}')
logger.debug(f"time-remaining is {time_remaining}")
return time_remaining

def seek_in_tape_to(self, destination, ticking=True, threshold=1):
Expand All @@ -240,18 +248,20 @@ def seek_in_tape_to(self, destination, ticking=True, threshold=1):
required to seek (the slippage).
destination -- seconds from current tape location (from beginning?)
"""
logger.debug(F'seek_in_tape_to {destination}')
logger.debug(f"seek_in_tape_to {destination}")

start_tick = datetime.datetime.now()
slippage = 0
skipped = 0
dest_orig = destination
time_remaining = self.time_remaining()
playlist_pos = self.get_prop('playlist-pos')
logger.debug(F'seek_in_tape_to dest:{destination},time-remainig:{time_remaining},playlist-pos:{playlist_pos}')
while (destination > time_remaining) and self.get_prop('playlist-pos') + 1 < len(self.playlist):
duration = self.get_prop('duration')
logger.debug(F'seek_in_tape_to dest:{destination},time-remainig:{time_remaining},playlist-pos:{playlist_pos}, duration: {duration}, slippage {slippage}')
playlist_pos = self.get_prop("playlist-pos")
logger.debug(f"seek_in_tape_to dest:{destination},time-remainig:{time_remaining},playlist-pos:{playlist_pos}")
while (destination > time_remaining) and self.get_prop("playlist-pos") + 1 < len(self.playlist):
duration = self.get_prop("duration")
logger.debug(
f"seek_in_tape_to dest:{destination},time-remainig:{time_remaining},playlist-pos:{playlist_pos}, duration: {duration}, slippage {slippage}"
)
self.next(blocking=True)
skipped = skipped + time_remaining
destination = dest_orig - skipped
Expand All @@ -260,56 +270,58 @@ def seek_in_tape_to(self, destination, ticking=True, threshold=1):
now_tick = datetime.datetime.now()
slippage = (now_tick - start_tick).seconds
destination = destination + slippage
playlist_pos = self.get_prop('playlist-pos')
playlist_pos = self.get_prop("playlist-pos")
self.seek(destination)
self.status()
self.play()
return

def seek_to(self, track_no, destination=0.0, threshold=1):
logger.debug(F'seek_to {track_no},{destination}')
logger.debug(f"seek_to {track_no},{destination}")
try:
if track_no < 0 or track_no > len(self.playlist):
raise Exception(F'seek_to track {track_no} out of bounds')
paused = self.get_prop('pause')
current_track = self.get_prop('playlist-pos')
raise Exception(f"seek_to track {track_no} out of bounds")
paused = self.get_prop("pause")
current_track = self.get_prop("playlist-pos")
self.status()
if current_track != track_no:
self._set_property('playlist-pos', track_no)
self._set_property("playlist-pos", track_no)
# self.wait_for_event('file-loaded') # NOTE: this could wait forever!
time.sleep(5)
duration = self.get_prop('duration')
duration = self.get_prop("duration")
if destination < 0:
destination = duration + destination
if (destination > duration) or (destination < 0):
raise Exception(F'seek_to destination {destination} out of bounds (0,{duration})')
raise Exception(f"seek_to destination {destination} out of bounds (0,{duration})")

self.seek(destination, reference='absolute')
self.seek(destination, reference="absolute")
if not paused:
self.play()
time_pos = self.get_prop('time-pos')
time_pos = self.get_prop("time-pos")
if abs(time_pos - destination) > threshold:
raise Exception(F'Not close enough: time_pos {time_pos} - destination ({time_pos - destination})>{threshold}')
raise Exception(
f"Not close enough: time_pos {time_pos} - destination ({time_pos - destination})>{threshold}"
)
except Exception as e:
logger.warning(e)
finally:
pass

def fseek(self, jumpsize=30, sleeptime=2):
try:
logger.debug(F'seeking {jumpsize}')
logger.debug(f"seeking {jumpsize}")

current_track = self.get_prop('playlist-pos')
time_pos = self.get_prop('time-pos')
current_track = self.get_prop("playlist-pos")
time_pos = self.get_prop("time-pos")
if time_pos is None:
time_pos = 0
time_pos = max(0, time_pos)
duration = self.get_prop('duration')
duration = self.get_prop("duration")
# self.wait_for_property('duration', lambda v: v is not None)

destination = time_pos + jumpsize

logger.debug(F'destination {destination} time_pos {time_pos} duration {duration}')
logger.debug(f"destination {destination} time_pos {time_pos} duration {duration}")

if destination < 0:
if abs(destination) < abs(sleeptime * 5):
Expand All @@ -320,7 +332,7 @@ def fseek(self, jumpsize=30, sleeptime=2):
else:
self.seek_to(current_track, destination)
except Exception as e:
logger.warning(F'exception in seeking {e}')
logger.warning(f"exception in seeking {e}")
finally:
time.sleep(sleeptime)

Expand All @@ -331,14 +343,18 @@ def status(self):
if self.playlist_pos is None:
logger.info("Playlist not started")
return None
playlist_pos = self.get_prop('playlist-pos')
paused = self.get_prop('pause')
logger.info(F"Playlist at track {playlist_pos}, Paused {paused}")
playlist_pos = self.get_prop("playlist-pos")
paused = self.get_prop("pause")
logger.info(f"Playlist at track {playlist_pos}, Paused {paused}")
if self.raw.time_pos is None:
logger.info("Track not started")
return None
duration = self.get_prop('duration')
logger.info(F"duration: {duration}. time: {datetime.timedelta(seconds=int(self.raw.time_pos))}, time remaining: {datetime.timedelta(seconds=int(self.raw.time_remaining))}")
duration = self.get_prop("duration")
logger.info(
f"duration: {duration}. time: {datetime.timedelta(seconds=int(self.raw.time_pos))}, time remaining: {datetime.timedelta(seconds=int(self.raw.time_remaining))}"
)
return int(self.raw.time_remaining)

def close(self): self.terminate()
def close(self):
self.terminate()

2 changes: 1 addition & 1 deletion timemachine/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
logger = logging.getLogger(__name__)
try:
from timemachine import controls
except NotImplementedError:
except Exception as e:
logger.warning(f"Failied to import controls")

OPTIONS_PATH = os.path.join(os.getenv("HOME"), ".timemachine_options.txt")
Expand Down
Loading

0 comments on commit 82f6b2f

Please sign in to comment.