plexflow 0.0.140__py3-none-any.whl → 0.0.141__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plexflow/core/plex/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- plexflow/core/plex/utils/__pycache__/paginated.cpython-312.pyc +0 -0
- plexflow/core/plex/watchlist/__pycache__/watchlist.cpython-312.pyc +0 -0
- plexflow/core/plex/watchlist/watchlist.py +1 -1
- plexflow/utils/transcribe/__pycache__/__init__.cpython-312.pyc +0 -0
- plexflow/utils/transcribe/__pycache__/speech2text.cpython-312.pyc +0 -0
- plexflow/utils/video/sync.py +103 -0
- {plexflow-0.0.140.dist-info → plexflow-0.0.141.dist-info}/METADATA +4 -1
- {plexflow-0.0.140.dist-info → plexflow-0.0.141.dist-info}/RECORD +11 -10
- {plexflow-0.0.140.dist-info → plexflow-0.0.141.dist-info}/WHEEL +0 -0
- {plexflow-0.0.140.dist-info → plexflow-0.0.141.dist-info}/entry_points.txt +0 -0
Binary file
|
Binary file
|
Binary file
|
@@ -18,6 +18,6 @@ def get_watchlist(**kwargs) -> MediaContainer:
|
|
18
18
|
None
|
19
19
|
|
20
20
|
"""
|
21
|
-
context = PlexAuthorizedRequestContext(base_url="https://
|
21
|
+
context = PlexAuthorizedRequestContext(base_url="https://discover.provider.plex.tv")
|
22
22
|
response = context.get(endpoint="/library/sections/watchlist/all", **kwargs)
|
23
23
|
return from_json(response.content.decode("utf-8"))
|
Binary file
|
Binary file
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
import argparse
|
5
|
+
import logging
|
6
|
+
import os
|
7
|
+
import shutil
|
8
|
+
import subprocess
|
9
|
+
import sys
|
10
|
+
|
11
|
+
|
12
|
+
# --- Custom Exceptions for Clearer Error Handling ---
|
13
|
+
class SubSyncError(Exception):
|
14
|
+
"""Base exception for this tool."""
|
15
|
+
pass
|
16
|
+
|
17
|
+
class FFSubsyncExecutableError(SubSyncError):
|
18
|
+
"""Raised when the ffsubsync executable is not found."""
|
19
|
+
pass
|
20
|
+
|
21
|
+
class FFSubsyncProcessError(SubSyncError):
|
22
|
+
"""Raised when the ffsubsync process fails."""
|
23
|
+
def __init__(self, message, returncode, stdout, stderr):
|
24
|
+
super().__init__(message)
|
25
|
+
self.returncode = returncode
|
26
|
+
self.stdout = stdout
|
27
|
+
self.stderr = stderr
|
28
|
+
|
29
|
+
def synchronize_subtitle(
|
30
|
+
video_path: str,
|
31
|
+
subtitle_path: str,
|
32
|
+
output_path: str = None,
|
33
|
+
ffsubsync_path: str = 'ffsubsync'
|
34
|
+
) -> str:
|
35
|
+
"""
|
36
|
+
Synchronizes a subtitle file with a video using ffsubsync.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
video_path: Path to the reference video file.
|
40
|
+
subtitle_path: Path to the subtitle file to synchronize.
|
41
|
+
output_path: Path for the synchronized output file. If None, a default
|
42
|
+
is generated next to the input subtitle.
|
43
|
+
ffsubsync_path: Path to the ffsubsync executable.
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
The path to the successfully created synchronized subtitle file.
|
47
|
+
|
48
|
+
Raises:
|
49
|
+
FFSubsyncExecutableError: If ffsubsync or its dependencies are not found.
|
50
|
+
FileNotFoundError: If the input video or subtitle files do not exist.
|
51
|
+
FFSubsyncProcessError: If the ffsubsync process returns a non-zero exit code.
|
52
|
+
"""
|
53
|
+
# 1. --- Pre-flight Checks ---
|
54
|
+
logging.info("Performing pre-flight checks...")
|
55
|
+
if not shutil.which(ffsubsync_path):
|
56
|
+
raise FFSubsyncExecutableError(
|
57
|
+
f"'{ffsubsync_path}' command not found. Please ensure ffsubsync is "
|
58
|
+
"installed (pip install ffsubsync) and in your system's PATH."
|
59
|
+
)
|
60
|
+
|
61
|
+
for fpath in [video_path, subtitle_path]:
|
62
|
+
if not os.path.exists(fpath):
|
63
|
+
raise FileNotFoundError(f"Input file not found at '{fpath}'")
|
64
|
+
|
65
|
+
# 2. --- Determine Output Path ---
|
66
|
+
if output_path is None:
|
67
|
+
base, ext = os.path.splitext(subtitle_path)
|
68
|
+
output_path = f"{base}_synced{ext}"
|
69
|
+
|
70
|
+
logging.info(f"Reference video: {video_path}")
|
71
|
+
logging.info(f"Input subtitle: {subtitle_path}")
|
72
|
+
logging.info(f"Output path: {output_path}")
|
73
|
+
|
74
|
+
# 3. --- Construct and Run the Command ---
|
75
|
+
command = [
|
76
|
+
ffsubsync_path,
|
77
|
+
video_path,
|
78
|
+
"-i", subtitle_path,
|
79
|
+
"-o", output_path,
|
80
|
+
]
|
81
|
+
logging.debug(f"Executing command: {' '.join(command)}")
|
82
|
+
|
83
|
+
try:
|
84
|
+
result = subprocess.run(
|
85
|
+
command,
|
86
|
+
check=True, # Raises CalledProcessError on non-zero exit codes
|
87
|
+
capture_output=True,
|
88
|
+
text=True,
|
89
|
+
encoding='utf-8',
|
90
|
+
)
|
91
|
+
# ffsubsync logs to stderr, so we log it for debug purposes
|
92
|
+
logging.debug("ffsubsync process output (stderr):\n%s", result.stderr)
|
93
|
+
|
94
|
+
except subprocess.CalledProcessError as e:
|
95
|
+
raise FFSubsyncProcessError(
|
96
|
+
"ffsubsync failed during execution.",
|
97
|
+
returncode=e.returncode,
|
98
|
+
stdout=e.stdout,
|
99
|
+
stderr=e.stderr
|
100
|
+
) from e
|
101
|
+
|
102
|
+
logging.info(f"Successfully synchronized subtitle to '{output_path}'")
|
103
|
+
return output_path
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: plexflow
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.141
|
4
4
|
Summary: A short description of the package.
|
5
5
|
License: MIT
|
6
6
|
Keywords: keyword1,keyword2,keyword3
|
@@ -27,8 +27,10 @@ Requires-Dist: cloudscraper (>=1.2.71,<2.0.0)
|
|
27
27
|
Requires-Dist: dataclasses-json (>=0.6.4,<0.7.0)
|
28
28
|
Requires-Dist: dateparser (>=1.2.0,<2.0.0)
|
29
29
|
Requires-Dist: fake-useragent (>=1.4.0,<2.0.0)
|
30
|
+
Requires-Dist: ffsubsync (>=0.4.29,<0.5.0)
|
30
31
|
Requires-Dist: google-cloud-pubsub (>=2.28.0,<3.0.0)
|
31
32
|
Requires-Dist: google-genai (>=1.26.0,<2.0.0)
|
33
|
+
Requires-Dist: groq (>=0.31.0,<0.32.0)
|
32
34
|
Requires-Dist: html5lib (>=1.1,<2.0)
|
33
35
|
Requires-Dist: humanfriendly (>=10.0,<11.0)
|
34
36
|
Requires-Dist: iso639-lang (>=2.2.3,<3.0.0)
|
@@ -56,6 +58,7 @@ Requires-Dist: requests (>=2.25.1,<3.0.0)
|
|
56
58
|
Requires-Dist: retrying (>=1.3.4,<2.0.0)
|
57
59
|
Requires-Dist: scrapy (>=2.11.2,<3.0.0)
|
58
60
|
Requires-Dist: seleniumbase (>=4.28.4,<5.0.0)
|
61
|
+
Requires-Dist: stable-ts (>=2.19.0,<3.0.0)
|
59
62
|
Requires-Dist: tmdbsimple (>=2.9.1,<3.0.0)
|
60
63
|
Requires-Dist: tpblite (>=0.8.0,<0.9.0)
|
61
64
|
Requires-Dist: tqdm (>=4.67.1,<5.0.0)
|
@@ -323,9 +323,9 @@ plexflow/core/plex/token/__pycache__/auto_token.cpython-312.pyc,sha256=1uRpJbyiW
|
|
323
323
|
plexflow/core/plex/token/auto_token.py,sha256=CcAd6KcnI7ul5TxpdYVw_7T0ab2K3YTi8DcqHtjKt4Q,3615
|
324
324
|
plexflow/core/plex/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
325
325
|
plexflow/core/plex/utils/__pycache__/__init__.cpython-311.pyc,sha256=2b2qu_365BZcXqAU5s34hzrvKC0s2_W8OReMDlBr3Sc,174
|
326
|
-
plexflow/core/plex/utils/__pycache__/__init__.cpython-312.pyc,sha256=
|
326
|
+
plexflow/core/plex/utils/__pycache__/__init__.cpython-312.pyc,sha256=kDTyAiv1LvPBl4_1L0O5-mL4S3Vxj12MsP0Cv8L2NC0,160
|
327
327
|
plexflow/core/plex/utils/__pycache__/paginated.cpython-311.pyc,sha256=7WkEf0cLkNXyI5BHrAZndqSoR6NM-V0wW1Oi5uSIf0M,1523
|
328
|
-
plexflow/core/plex/utils/__pycache__/paginated.cpython-312.pyc,sha256=
|
328
|
+
plexflow/core/plex/utils/__pycache__/paginated.cpython-312.pyc,sha256=5dnkFug_9eFqj3zXm6CWPMw_N9tYwWy68F0mntRyWhM,1383
|
329
329
|
plexflow/core/plex/utils/paginated.py,sha256=4w-Q2kpkCmsCYnxJazoltvvpLq-c2hzyS4DWsGHkeoM,1171
|
330
330
|
plexflow/core/plex/watchlist/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
331
331
|
plexflow/core/plex/watchlist/__pycache__/__init__.cpython-311.pyc,sha256=5GlwuWEZYpXuoiIJkYyX4h6wxwzMD9DUosgJ5Gw_6ZU,178
|
@@ -333,9 +333,9 @@ plexflow/core/plex/watchlist/__pycache__/__init__.cpython-312.pyc,sha256=FlojeaU
|
|
333
333
|
plexflow/core/plex/watchlist/__pycache__/datatypes.cpython-311.pyc,sha256=raf3JEsEqRmBYH2FOfY4Wujcdmobp94rviGft5Z7jCQ,8849
|
334
334
|
plexflow/core/plex/watchlist/__pycache__/datatypes.cpython-312.pyc,sha256=iOPW1Mhf5SuUFHMzATc2pqN_uF-_NAmcCrzeUReQEt0,7606
|
335
335
|
plexflow/core/plex/watchlist/__pycache__/watchlist.cpython-311.pyc,sha256=CocErWnueQIN8ReWjjpteDvF7mDtsdlxvFe4TaMiKvY,1274
|
336
|
-
plexflow/core/plex/watchlist/__pycache__/watchlist.cpython-312.pyc,sha256=
|
336
|
+
plexflow/core/plex/watchlist/__pycache__/watchlist.cpython-312.pyc,sha256=hZpx1d7RgxUamD-kxd0qdcYs6EXwaS4Od53SUgG2FbU,1415
|
337
337
|
plexflow/core/plex/watchlist/datatypes.py,sha256=Ukri8nM16-IKxPxia4n45syjtc401nMWSd3hgjdrJSE,5193
|
338
|
-
plexflow/core/plex/watchlist/watchlist.py,sha256=
|
338
|
+
plexflow/core/plex/watchlist/watchlist.py,sha256=8WB_2zvf2xCc9IdOnUCjAIiFtCyCGK2hU4ORu72Lzjw,851
|
339
339
|
plexflow/core/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
340
340
|
plexflow/core/storage/__pycache__/__init__.cpython-311.pyc,sha256=kKE5c5UA7CgoFly4hrI7a7s-ztUxg3zLnkjgpShAj2k,171
|
341
341
|
plexflow/core/storage/__pycache__/__init__.cpython-312.pyc,sha256=bkMvtPZ53O_tyElEvOeGWcjGR-DEufOi4f0zHQeJFXo,157
|
@@ -725,9 +725,9 @@ plexflow/utils/torrent/extract/torrentquest.py,sha256=gQn9GanFM5SXvpwjcyRu0XgTZ2
|
|
725
725
|
plexflow/utils/torrent/files.py,sha256=XJxvFedZQb8H-wSzNIZI_2nUOT-pqr6MwgYcoty0yA0,2285
|
726
726
|
plexflow/utils/torrent/hash.py,sha256=yaVAXj6t4qouNVUdZ-ulgrhTSLsp7DOggyB6sanCPX8,3162
|
727
727
|
plexflow/utils/transcribe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
728
|
-
plexflow/utils/transcribe/__pycache__/__init__.cpython-312.pyc,sha256=
|
728
|
+
plexflow/utils/transcribe/__pycache__/__init__.cpython-312.pyc,sha256=AEEtGtOBmfH0I2QiG4MzzInGJracQnS7YyNLJ5Gwg2s,161
|
729
729
|
plexflow/utils/transcribe/__pycache__/speech2text.cpython-311.pyc,sha256=WcYPF8JcosrCCwmIFl0eamZpasICUBJ_MeUS_Ok7zUs,1632
|
730
|
-
plexflow/utils/transcribe/__pycache__/speech2text.cpython-312.pyc,sha256=
|
730
|
+
plexflow/utils/transcribe/__pycache__/speech2text.cpython-312.pyc,sha256=DqZEkcJR1rubjolqlFp6Y-Ed2lh-MTSoHMUS4N_6sRA,2822
|
731
731
|
plexflow/utils/transcribe/speech2text.py,sha256=ldcZqx18Yr8aa-y5sw7WaEKCci9mvBI0z06YQqJemnU,3547
|
732
732
|
plexflow/utils/video/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
733
733
|
plexflow/utils/video/__pycache__/__init__.cpython-312.pyc,sha256=SA7I28R8t-m5Q4uA7laJCUz3JHqVgUtkvewvrAk1qVk,156
|
@@ -735,7 +735,8 @@ plexflow/utils/video/__pycache__/audio.cpython-312.pyc,sha256=kmzGDCHSC1hWyHwRut
|
|
735
735
|
plexflow/utils/video/__pycache__/subtitle.cpython-312.pyc,sha256=PCjpCLydGXaRsQy6cikhgsEs8WlComfOoYPiLFqfVMA,2515
|
736
736
|
plexflow/utils/video/audio.py,sha256=Pd8OuQHX2QN-lc5iYkB0Vo1OEHmTcvDYH-uKud1f1q4,5262
|
737
737
|
plexflow/utils/video/subtitle.py,sha256=qPvvBjlPj0fynJJvGJgGeKt9ey26R-cF6EoLaYt9iXU,1333
|
738
|
-
plexflow
|
739
|
-
plexflow-0.0.
|
740
|
-
plexflow-0.0.
|
741
|
-
plexflow-0.0.
|
738
|
+
plexflow/utils/video/sync.py,sha256=LqBA0M-9MIozYNtZA4BzQfNjl8uWoV7doxhzKC6mlxU,3347
|
739
|
+
plexflow-0.0.141.dist-info/METADATA,sha256=9C-KLjhTvIXwGg6HwuXoaerpnjQktuhe16v-JJxNl8Q,3096
|
740
|
+
plexflow-0.0.141.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
741
|
+
plexflow-0.0.141.dist-info/entry_points.txt,sha256=9RJC3ikOQORHNOn573EdwJOBUnFU_4EGHbtNUM5pjjY,1557
|
742
|
+
plexflow-0.0.141.dist-info/RECORD,,
|
File without changes
|
File without changes
|