Skip to content

Commit

Permalink
refactor(Track): Move from OnXyz callables to Event observer
Browse files Browse the repository at this point in the history
Fixes #85
  • Loading branch information
rlaphoenix committed Apr 2, 2024
1 parent 226b609 commit 5a12cb3
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 39 deletions.
19 changes: 16 additions & 3 deletions devine/commands/dl.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from devine.core.constants import DOWNLOAD_LICENCE_ONLY, AnyTrack, context_settings
from devine.core.credential import Credential
from devine.core.drm import DRM_T, Widevine
from devine.core.events import events
from devine.core.proxies import Basic, Hola, NordVPN
from devine.core.service import Service
from devine.core.services import Services
Expand Down Expand Up @@ -324,6 +325,14 @@ def result(
with console.status(f"Delaying by {delay} seconds..."):
time.sleep(delay)

with console.status("Subscribing to events...", spinner="dots"):
events.reset()
events.subscribe(events.Types.SEGMENT_DOWNLOADED, service.on_segment_downloaded)
events.subscribe(events.Types.TRACK_DOWNLOADED, service.on_track_downloaded)
events.subscribe(events.Types.TRACK_DECRYPTED, service.on_track_decrypted)
events.subscribe(events.Types.TRACK_REPACKED, service.on_track_repacked)
events.subscribe(events.Types.TRACK_MULTIPLEX, service.on_track_multiplex)

with console.status("Getting tracks...", spinner="dots"):
title.tracks.add(service.get_tracks(title), warn_only=True)
title.tracks.chapters = service.get_chapters(title)
Expand All @@ -339,8 +348,13 @@ def result(
non_sdh_sub = deepcopy(subtitle)
non_sdh_sub.id += "_stripped"
non_sdh_sub.sdh = False
non_sdh_sub.OnMultiplex = lambda: non_sdh_sub.strip_hearing_impaired()
title.tracks.add(non_sdh_sub)
events.subscribe(
events.Types.TRACK_MULTIPLEX,
lambda track: (
track.strip_hearing_impaired()
) if track.id == non_sdh_sub.id else None
)

with console.status("Sorting tracks by language and bitrate...", spinner="dots"):
title.tracks.sort_videos(by_language=v_lang or lang)
Expand Down Expand Up @@ -636,8 +650,7 @@ def result(
if track.needs_repack:
track.repackage()
has_repacked = True
if callable(track.OnRepacked):
track.OnRepacked()
events.emit(events.Types.TRACK_REPACKED, track=track)
if has_repacked:
# we don't want to fill up the log with "Repacked x track"
self.log.info("Repacked one or more tracks with FFMPEG")
Expand Down
16 changes: 10 additions & 6 deletions devine/core/manifests/dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from devine.core.constants import DOWNLOAD_CANCELLED, DOWNLOAD_LICENCE_ONLY, AnyTrack
from devine.core.downloaders import requests as requests_downloader
from devine.core.drm import Widevine
from devine.core.events import events
from devine.core.tracks import Audio, Subtitle, Tracks, Video
from devine.core.utilities import is_close_match, try_ensure_utf8
from devine.core.utils.xml import load_xml
Expand Down Expand Up @@ -474,8 +475,8 @@ def download_track(
max_workers=16
):
file_downloaded = status_update.get("file_downloaded")
if file_downloaded and callable(track.OnSegmentDownloaded):
track.OnSegmentDownloaded(file_downloaded)
if file_downloaded:
events.emit(events.Types.SEGMENT_DOWNLOADED, track=track, segment=file_downloaded)
else:
downloaded = status_update.get("downloaded")
if downloaded and downloaded.endswith("/s"):
Expand Down Expand Up @@ -514,15 +515,18 @@ def download_track(
progress(advance=1)

track.path = save_path
if callable(track.OnDownloaded):
track.OnDownloaded()
events.emit(events.Types.TRACK_DOWNLOADED, track=track)

if drm:
progress(downloaded="Decrypting", completed=0, total=100)
drm.decrypt(save_path)
track.drm = None
if callable(track.OnDecrypted):
track.OnDecrypted(drm)
events.emit(
events.Types.TRACK_DECRYPTED,
track=track,
drm=drm,
segment=None
)
progress(downloaded="Decrypting", advance=100)

save_dir.rmdir()
Expand Down
16 changes: 10 additions & 6 deletions devine/core/manifests/hls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from devine.core.constants import DOWNLOAD_CANCELLED, DOWNLOAD_LICENCE_ONLY, AnyTrack
from devine.core.downloaders import requests as requests_downloader
from devine.core.drm import DRM_T, ClearKey, Widevine
from devine.core.events import events
from devine.core.tracks import Audio, Subtitle, Tracks, Video
from devine.core.utilities import get_binary_path, get_extension, is_close_match, try_ensure_utf8

Expand Down Expand Up @@ -282,8 +283,8 @@ def download_track(
max_workers=16
):
file_downloaded = status_update.get("file_downloaded")
if file_downloaded and callable(track.OnSegmentDownloaded):
track.OnSegmentDownloaded(file_downloaded)
if file_downloaded:
events.emit(events.Types.SEGMENT_DOWNLOADED, track=track, segment=file_downloaded)
else:
downloaded = status_update.get("downloaded")
if downloaded and downloaded.endswith("/s"):
Expand Down Expand Up @@ -381,8 +382,12 @@ def decrypt(include_this_segment: bool) -> Path:
drm.decrypt(merged_path)
merged_path.rename(decrypted_path)

if callable(track.OnDecrypted):
track.OnDecrypted(drm, decrypted_path)
events.emit(
events.Types.TRACK_DECRYPTED,
track=track,
drm=drm,
segment=decrypted_path
)

return decrypted_path

Expand Down Expand Up @@ -537,8 +542,7 @@ def merge_discontinuity(include_this_segment: bool, include_map_data: bool = Tru
progress(downloaded="Downloaded")

track.path = save_path
if callable(track.OnDownloaded):
track.OnDownloaded()
events.emit(events.Types.TRACK_DOWNLOADED, track=track)

@staticmethod
def merge_segments(segments: list[Path], save_path: Path) -> int:
Expand Down
51 changes: 51 additions & 0 deletions devine/core/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
from abc import ABCMeta, abstractmethod
from collections.abc import Generator
from http.cookiejar import CookieJar
from pathlib import Path
from typing import Optional, Union
from urllib.parse import urlparse

import click
import m3u8
import requests
from requests.adapters import HTTPAdapter, Retry
from rich.padding import Padding
Expand All @@ -17,6 +19,7 @@
from devine.core.console import console
from devine.core.constants import AnyTrack
from devine.core.credential import Credential
from devine.core.drm import DRM_T
from devine.core.search_result import SearchResult
from devine.core.titles import Title_T, Titles_T
from devine.core.tracks import Chapters, Tracks
Expand Down Expand Up @@ -235,5 +238,53 @@ def get_chapters(self, title: Title_T) -> Chapters:
option `chapter_fallback_name`. For example, `"Chapter {i:02}"` for "Chapter 01".
"""

# Optional Event methods

def on_segment_downloaded(self, track: AnyTrack, segment: Path) -> None:
"""
Called when one of a Track's Segments has finished downloading.
Parameters:
track: The Track object that had a Segment downloaded.
segment: The Path to the Segment that was downloaded.
"""

def on_track_downloaded(self, track: AnyTrack) -> None:
"""
Called when a Track has finished downloading.
Parameters:
track: The Track object that was downloaded.
"""

def on_track_decrypted(self, track: AnyTrack, drm: DRM_T, segment: Optional[m3u8.Segment] = None) -> None:
"""
Called when a Track has finished decrypting.
Parameters:
track: The Track object that was decrypted.
drm: The DRM object it decrypted with.
segment: The HLS segment information that was decrypted.
"""

def on_track_repacked(self, track: AnyTrack) -> None:
"""
Called when a Track has finished repacking.
Parameters:
track: The Track object that was repacked.
"""

def on_track_multiplex(self, track: AnyTrack) -> None:
"""
Called when a Track is about to be Multiplexed into a Container.
Note: Right now only MKV containers are multiplexed but in the future
this may also be called when multiplexing to other containers like
MP4 via ffmpeg/mp4box.
Parameters:
track: The Track object that was repacked.
"""

__all__ = ("Service",)
27 changes: 9 additions & 18 deletions devine/core/tracks/track.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
from uuid import UUID
from zlib import crc32

import m3u8
from langcodes import Language
from requests import Session

from devine.core.config import config
from devine.core.constants import DOWNLOAD_CANCELLED, DOWNLOAD_LICENCE_ONLY
from devine.core.downloaders import aria2c, curl_impersonate, requests
from devine.core.drm import DRM_T, Widevine
from devine.core.events import events
from devine.core.utilities import get_binary_path, get_boxes, try_ensure_utf8
from devine.core.utils.subprocess import ffprobe

Expand Down Expand Up @@ -122,17 +122,6 @@ def __init__(
# TODO: Currently using OnFoo event naming, change to just segment_filter
self.OnSegmentFilter: Optional[Callable] = None

# Called after one of the Track's segments have downloaded
self.OnSegmentDownloaded: Optional[Callable[[Path], None]] = None
# Called after the Track has downloaded
self.OnDownloaded: Optional[Callable] = None
# Called after the Track or one of its segments have been decrypted
self.OnDecrypted: Optional[Callable[[DRM_T, Optional[m3u8.Segment]], None]] = None
# Called after the Track has been repackaged
self.OnRepacked: Optional[Callable] = None
# Called before the Track is multiplexed
self.OnMultiplex: Optional[Callable] = None

def __repr__(self) -> str:
return "{name}({items})".format(
name=self.__class__.__name__,
Expand Down Expand Up @@ -257,15 +246,18 @@ def cleanup():
save_path.with_suffix(f"{save_path.suffix}.aria2__temp").unlink(missing_ok=True)

self.path = save_path
if callable(self.OnDownloaded):
self.OnDownloaded()
events.emit(events.Types.TRACK_DOWNLOADED, track=self)

if drm:
progress(downloaded="Decrypting", completed=0, total=100)
drm.decrypt(save_path)
self.drm = None
if callable(self.OnDecrypted):
self.OnDecrypted(drm)
events.emit(
events.Types.TRACK_DECRYPTED,
track=self,
drm=drm,
segment=None
)
progress(downloaded="Decrypted", completed=100)

if track_type == "Subtitle" and self.codec.name not in ("fVTT", "fTTML"):
Expand Down Expand Up @@ -299,8 +291,7 @@ def cleanup():
if self.path.stat().st_size <= 3: # Empty UTF-8 BOM == 3 bytes
raise IOError("Download failed, the downloaded file is empty.")

if callable(self.OnDownloaded):
self.OnDownloaded(self)
events.emit(events.Types.TRACK_DOWNLOADED, track=self)

def delete(self) -> None:
if self.path:
Expand Down
10 changes: 4 additions & 6 deletions devine/core/tracks/tracks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from devine.core.config import config
from devine.core.console import console
from devine.core.constants import LANGUAGE_MAX_DISTANCE, AnyTrack, TrackT
from devine.core.events import events
from devine.core.tracks.attachment import Attachment
from devine.core.tracks.audio import Audio
from devine.core.tracks.chapters import Chapter, Chapters
Expand Down Expand Up @@ -337,8 +338,7 @@ def mux(self, title: str, delete: bool = True, progress: Optional[partial] = Non
for i, vt in enumerate(self.videos):
if not vt.path or not vt.path.exists():
raise ValueError("Video Track must be downloaded before muxing...")
if callable(vt.OnMultiplex):
vt.OnMultiplex()
events.emit(events.Types.TRACK_MULTIPLEX, track=vt)
cl.extend([
"--language", f"0:{vt.language}",
"--default-track", f"0:{i == 0}",
Expand All @@ -350,8 +350,7 @@ def mux(self, title: str, delete: bool = True, progress: Optional[partial] = Non
for i, at in enumerate(self.audio):
if not at.path or not at.path.exists():
raise ValueError("Audio Track must be downloaded before muxing...")
if callable(at.OnMultiplex):
at.OnMultiplex()
events.emit(events.Types.TRACK_MULTIPLEX, track=at)
cl.extend([
"--track-name", f"0:{at.get_track_name() or ''}",
"--language", f"0:{at.language}",
Expand All @@ -365,8 +364,7 @@ def mux(self, title: str, delete: bool = True, progress: Optional[partial] = Non
for st in self.subtitles:
if not st.path or not st.path.exists():
raise ValueError("Text Track must be downloaded before muxing...")
if callable(st.OnMultiplex):
st.OnMultiplex()
events.emit(events.Types.TRACK_MULTIPLEX, track=st)
default = bool(self.audio and is_close_match(st.language, [self.audio[0].language]) and st.forced)
cl.extend([
"--track-name", f"0:{st.get_track_name() or ''}",
Expand Down

0 comments on commit 5a12cb3

Please sign in to comment.