plexflow 0.0.122__py3-none-any.whl → 0.0.123__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.
@@ -0,0 +1,107 @@
1
+ from plexflow.core.context.partial_context import PartialContext
2
+ from datetime import datetime as dt
3
+ from plexflow.core.metadata.auto.auto_providers.auto.show import AutoShow
4
+ from plexflow.core.metadata.auto.auto_providers.tmdb.show import AutoTmdbShow
5
+ from plexflow.core.metadata.auto.auto_providers.tvdb.show import AutoTvdbShow
6
+ from plexflow.core.metadata.auto.auto_providers.imdb.show import AutoImdbShow
7
+ from plexflow.core.metadata.auto.auto_providers.plex.show import AutoPlexShow
8
+
9
+ class Show(PartialContext):
10
+ def __init__(self, **kwargs) -> None:
11
+ super().__init__(**kwargs)
12
+
13
+ @property
14
+ def sources(self) -> list:
15
+ keys = self.get_keys("show/*")
16
+ # extract the source from the key
17
+ return [key.split("/")[-1] for key in keys]
18
+
19
+ def from_source(self, source: str) -> AutoShow:
20
+ return self.get(f"show/{source}")
21
+
22
+ @property
23
+ def title(self) -> str:
24
+ for source in self.sources:
25
+ details = self.from_source(source)
26
+ if details and details.title:
27
+ return details.title
28
+
29
+ @property
30
+ def year(self) -> int:
31
+ for source in self.sources:
32
+ details = self.from_source(source)
33
+ if details and details.year:
34
+ return details.year
35
+
36
+ @property
37
+ def release_date(self) -> dt:
38
+ for source in self.sources:
39
+ details = self.from_source(source)
40
+ if details and details.release_date:
41
+ return details.release_date
42
+
43
+ @property
44
+ def rank(self) -> int:
45
+ return self.plex.rank
46
+
47
+ @property
48
+ def released(self) -> bool:
49
+ dates = []
50
+ for source in self.sources:
51
+ details = self.from_source(source)
52
+ if details and details.release_date:
53
+ dates.append(details.release_date)
54
+
55
+ sorted_dates = sorted(dates)
56
+ now = dt.now()
57
+ return all([date < now for date in sorted_dates])
58
+
59
+ @property
60
+ def runtime(self) -> int:
61
+ for source in self.sources:
62
+ details = self.from_source(source)
63
+ if details and details.runtime:
64
+ return details.runtime
65
+
66
+ @property
67
+ def titles(self) -> set:
68
+ titles = set()
69
+ for source in self.sources:
70
+ details = self.from_source(source)
71
+ if details and details.title:
72
+ titles.add(details.title)
73
+ titles.update(details.titles)
74
+ return titles
75
+
76
+ @property
77
+ def summary(self) -> str:
78
+ for source in self.sources:
79
+ details = self.from_source(source)
80
+ if details and details.summary:
81
+ return details.summary
82
+
83
+ @property
84
+ def language(self) -> str:
85
+ for source in self.sources:
86
+ details = self.from_source(source)
87
+ if details and details.language:
88
+ return details.language
89
+
90
+ @property
91
+ def plex(self) -> AutoPlexShow:
92
+ return self.from_source("plex")
93
+
94
+ @property
95
+ def tmdb(self) -> AutoTmdbShow:
96
+ return self.from_source("tmdb")
97
+
98
+ @property
99
+ def imdb(self) -> AutoImdbShow:
100
+ return self.from_source("imdb")
101
+
102
+ @property
103
+ def tvdb(self) -> AutoTvdbShow:
104
+ return self.from_source("tvdb")
105
+
106
+ def update(self, movie: AutoShow):
107
+ self.set(f"show/{movie.source}", movie)
@@ -7,6 +7,7 @@ from plexflow.core.metadata.auto.auto_providers.imdb.movie import AutoImdbMovie
7
7
  from plexflow.core.metadata.auto.auto_providers.tmdb.show import AutoTmdbShow
8
8
  from plexflow.core.metadata.auto.auto_providers.tvdb.show import AutoTvdbShow
9
9
  from plexflow.core.metadata.auto.auto_providers.imdb.show import AutoImdbShow
10
+ from plexflow.core.metadata.auto.auto_providers.plex.show import AutoPlexShow
10
11
  from plexflow.core.metadata.auto.auto_providers.plex.movie import AutoPlexMovie
11
12
 
12
13
  class AutoMeta:
@@ -29,12 +30,14 @@ class AutoMeta:
29
30
  raise ValueError(f"Invalid source: {source}")
30
31
 
31
32
  @staticmethod
32
- def show(imdb_id: str, source: str = 'tmdb') -> AutoShow:
33
+ def show(imdb_id: str, source: str = 'tmdb', rating_key: str = None) -> AutoShow:
33
34
  if source == 'tmdb':
34
35
  return AutoTmdbShow(imdb_id)
35
36
  elif source == 'tvdb':
36
37
  return AutoTvdbShow(imdb_id)
37
38
  elif source == 'imdb':
38
39
  return AutoImdbShow(imdb_id)
40
+ elif source == 'plex':
41
+ return AutoPlexShow(rating_key=rating_key)
39
42
  else:
40
43
  raise ValueError(f"Invalid source: {source}")
@@ -0,0 +1,29 @@
1
+ from datetime import datetime
2
+ from plexflow.core.metadata.auto.auto_providers.auto.episode import AutoEpisode
3
+ from plexflow.core.metadata.auto.auto_providers.auto.season import AutoSeason
4
+ from plexflow.core.metadata.providers.plex.datatypes import PlexEpisodeMetadata
5
+
6
+ class AutoPlexEpisode(AutoEpisode):
7
+ def __init__(self, parent: AutoSeason, data: PlexEpisodeMetadata) -> None:
8
+ super().__init__(parent=parent)
9
+ self._episode = data
10
+
11
+ @property
12
+ def release_date(self) -> datetime:
13
+ return datetime.strptime(self._episode.originallyAvailableAt, '%Y-%m-%d') if self._episode.originallyAvailableAt else None
14
+
15
+ @property
16
+ def episode_number(self) -> int:
17
+ return self._episode.index
18
+
19
+ @property
20
+ def title(self) -> str:
21
+ return self._episode.title
22
+
23
+ @property
24
+ def runtime(self) -> int:
25
+ return self._episode.duration
26
+
27
+ @property
28
+ def summary(self) -> str:
29
+ return self._episode.summary
@@ -0,0 +1,25 @@
1
+ from datetime import datetime
2
+ from plexflow.core.metadata.auto.auto_providers.auto.season import AutoSeason
3
+ from plexflow.core.metadata.auto.auto_providers.auto.show import AutoShow
4
+ from plexflow.core.metadata.auto.auto_providers.auto.episode import AutoEpisode
5
+ from plexflow.core.metadata.providers.plex.datatypes import PlexSeasonMetadata
6
+ from plexflow.core.metadata.providers.plex.plex import search_episodes_by_season_rating_key
7
+ from plexflow.core.metadata.auto.auto_providers.plex.episode import AutoPlexEpisode
8
+
9
+ class AutoPlexSeason(AutoSeason):
10
+ def __init__(self, parent: AutoShow, data: PlexSeasonMetadata) -> None:
11
+ super().__init__(parent)
12
+ self._season = data
13
+
14
+ @property
15
+ def episodes(self) -> list[AutoEpisode]:
16
+ episodes = search_episodes_by_season_rating_key(key=self._season.ratingKey)
17
+ return [AutoPlexEpisode(self, episode) for episode in episodes]
18
+
19
+ @property
20
+ def release_date(self) -> datetime:
21
+ return datetime.strptime(self._season.originallyAvailableAt, '%Y-%m-%d')
22
+
23
+ @property
24
+ def season_number(self) -> int:
25
+ return self._season.index
@@ -0,0 +1,46 @@
1
+ from plexflow.core.metadata.auto.auto_providers.auto.show import AutoShow
2
+ from plexflow.core.metadata.auto.auto_providers.auto.season import AutoSeason
3
+ from plexflow.core.metadata.providers.plex.plex import search_show_by_rating_key, search_seasons_by_show_rating_key
4
+ from datetime import datetime
5
+ from plexflow.utils.imdb.imdb_codes import extract_imdb_code
6
+ from plexflow.core.metadata.auto.auto_providers.plex.season import AutoPlexSeason
7
+
8
+ class AutoPlexShow(AutoShow):
9
+ def __init__(self, rating_key: str) -> None:
10
+ self._show = search_show_by_rating_key(rating_key)
11
+ imdb_id = next((next(extract_imdb_code(g.id), None) for g in self._show.Guid), None)
12
+
13
+ super().__init__(imdb_id, 'plex')
14
+
15
+ @property
16
+ def id(self) -> str:
17
+ return self._show.ratingKey
18
+
19
+ @property
20
+ def title(self) -> str:
21
+ return self._show.title
22
+
23
+ @property
24
+ def release_date(self) -> datetime:
25
+ return datetime.strptime(self._show.originallyAvailableAt, '%Y-%m-%d')
26
+
27
+ @property
28
+ def runtime(self) -> int:
29
+ return self._show.duration // 60000 if isinstance(self._show.duration, int) else None
30
+
31
+ @property
32
+ def titles(self) -> list:
33
+ return []
34
+
35
+ @property
36
+ def summary(self) -> str:
37
+ return self._show.summary
38
+
39
+ @property
40
+ def language(self) -> str:
41
+ return None
42
+
43
+ @property
44
+ def seasons(self) -> list[AutoSeason]:
45
+ seasons = search_seasons_by_show_rating_key(key=self.id)
46
+ return [AutoPlexSeason(self, season) for season in seasons]
@@ -368,17 +368,57 @@ class PlexShowMetadata:
368
368
  _catchall (dict): A catch-all dictionary for any additional attributes.
369
369
  """
370
370
 
371
+ art: Optional[str] = None,
372
+ guid: Optional[str] = None,
373
+ key: Optional[str] = None,
374
+ primaryExtraKey: Optional[str] = None,
375
+ rating: Optional[float] = None,
376
+ ratingKey: Optional[str] = None,
377
+ studio: Optional[str] = None,
378
+ subtype: Optional[str] = None,
379
+ summary: Optional[str] = None,
380
+ tagline: Optional[str] = None,
381
+ type: Optional[str] = None,
382
+ thumb: Optional[str] = None,
383
+ addedAt: Optional[int] = None,
384
+ duration: Optional[int] = None,
385
+ publicPagesURL: Optional[str] = None,
386
+ slug: Optional[str] = None,
387
+ userState: Optional[bool] = None,
388
+ title: Optional[str] = None,
389
+ leafCount: Optional[int] = None,
390
+ childCount: Optional[int] = None,
391
+ skipChildren: Optional[bool] = None,
392
+ isContinuingSeries: Optional[bool] = None,
393
+ contentRating: Optional[str] = None,
394
+ originallyAvailableAt: Optional[str] = None,
395
+ year: Optional[int] = None,
396
+ ratingImage: Optional[str] = None,
397
+ imdbRatingCount: Optional[int] = None,
398
+ source: Optional[str] = None
399
+ Image: Optional[List[PlexImage]] = None
400
+ Guid: Optional[List[PlexGuid]] = None
401
+ Role: Optional[List[PlexRole]] = None
402
+ Country: Optional[List[PlexCountry]] = None
403
+ Director: Optional[List[PlexDirector]] = None
404
+ Writer: Optional[List[PlexWriter]] = None
405
+ Rating: Optional[List[PlexRating]] = None
406
+ Studio: Optional[List[PlexStudio]] = None
407
+ Producer: Optional[List[PlexProducer]] = None
408
+ Genre: Optional[List[PlexGenre]] = None
409
+ _catchall: dict = field(default_factory=dict)
410
+
371
411
  def __post_init__(self):
372
- self.Image = [PlexImage(**image) if isinstance(image, dict) else image for image in self.Image]
373
- self.Genre = [PlexGenre(**genre) if isinstance(genre, dict) else genre for genre in self.Genre]
374
- self.Guid = [PlexGuid(**guid) if isinstance(guid, dict) else guid for guid in self.Guid]
375
- self.Country = [PlexCountry(**country) if isinstance(country, dict) else country for country in self.Country]
376
- self.Role = [PlexRole(**role) if isinstance(role, dict) else role for role in self.Role]
377
- self.Director = [PlexDirector(**director) if isinstance(director, dict) else director for director in self.Director]
378
- self.Producer = [PlexProducer(**producer) if isinstance(producer, dict) else producer for producer in self.Producer]
379
- self.Writer = [PlexWriter(**writer) if isinstance(writer, dict) else writer for writer in self.Writer]
380
- self.Rating = [PlexRating(**rating) if isinstance(rating, dict) else rating for rating in self.Rating]
381
- self.Studio = [PlexStudio(**studio) if isinstance(studio, dict) else studio for studio in self.Studio]
412
+ self.Image = [PlexImage(**image) if isinstance(image, dict) else image for image in self.Image] if self.Image else []
413
+ self.Genre = [PlexGenre(**genre) if isinstance(genre, dict) else genre for genre in self.Genre] if self.Genre else []
414
+ self.Guid = [PlexGuid(**guid) if isinstance(guid, dict) else guid for guid in self.Guid] if self.Guid else []
415
+ self.Country = [PlexCountry(**country) if isinstance(country, dict) else country for country in self.Country] if self.Country else []
416
+ self.Role = [PlexRole(**role) if isinstance(role, dict) else role for role in self.Role] if self.Role else []
417
+ self.Director = [PlexDirector(**director) if isinstance(director, dict) else director for director in self.Director] if self.Director else []
418
+ self.Producer = [PlexProducer(**producer) if isinstance(producer, dict) else producer for producer in self.Producer] if self.Producer else []
419
+ self.Writer = [PlexWriter(**writer) if isinstance(writer, dict) else writer for writer in self.Writer] if self.Writer else []
420
+ self.Rating = [PlexRating(**rating) if isinstance(rating, dict) else rating for rating in self.Rating] if self.Rating else []
421
+ self.Studio = [PlexStudio(**studio) if isinstance(studio, dict) else studio for studio in self.Studio] if self.Studio else []
382
422
 
383
423
 
384
424
  @dataclass_json(undefined=Undefined.EXCLUDE)
@@ -614,7 +654,7 @@ class PlexEpisodeMetadata:
614
654
  _catchall: dict = field(default_factory=dict)
615
655
 
616
656
  def __post_init__(self):
617
- self.Image = [PlexImage(**image) if isinstance(image, dict) else image for image in self.Image]
657
+ self.Image = [PlexImage(**image) if isinstance(image, dict) else image for image in self.Image] if self.Image else []
618
658
  self.Guid = [PlexGuid(**guid) if isinstance(guid, dict) else guid for guid in self.Guid]
619
659
  self.Role = [PlexRole(**role) if isinstance(role, dict) else role for role in self.Role] if self.Role else []
620
660
  self.Director = [PlexDirector(**director) if isinstance(director, dict) else director for director in self.Director] if self.Director else []
@@ -2,6 +2,9 @@ import subprocess
2
2
  from pathlib import Path
3
3
  from typing import List, Optional
4
4
  import re
5
+ import json
6
+ from typing import Tuple
7
+
5
8
 
6
9
  class FFmpegError(Exception):
7
10
  """Custom exception for FFmpeg errors."""
@@ -74,6 +77,9 @@ def get_audio_stream_indices(video_path: Path) -> List[int]:
74
77
  - FileNotFoundError: If the video file does not exist.
75
78
  - FFmpegError: If ffmpeg encounters an error during processing.
76
79
  """
80
+ if isinstance(video_path, str):
81
+ video_path = Path(video_path)
82
+
77
83
  if not video_path.exists():
78
84
  raise FileNotFoundError(f"The video file {video_path} does not exist.")
79
85
 
@@ -95,4 +101,48 @@ def get_audio_stream_indices(video_path: Path) -> List[int]:
95
101
  stream_index = int(match.group(1))
96
102
  audio_indices.append(stream_index)
97
103
 
98
- return audio_indices
104
+ return audio_indices
105
+
106
+ def get_audio_stream_info(video_path: Path) -> List[Tuple[int, Optional[str]]]:
107
+ """
108
+ Retrieves the indices and language tags of audio streams in a video file using ffprobe.
109
+
110
+ Parameters:
111
+ - video_path (Path): Path to the input video file.
112
+
113
+ Returns:
114
+ - List[Tuple[int, Optional[str]]]: A list of tuples, where each tuple contains
115
+ (stream_index, language_code). language_code will be None if not found.
116
+
117
+ Raises:
118
+ - FileNotFoundError: If the video file does not exist.
119
+ - FFmpegError: If ffprobe encounters an error during processing or JSON parsing.
120
+ """
121
+ if isinstance(video_path, str):
122
+ video_path = Path(video_path)
123
+
124
+ if not video_path.exists():
125
+ raise FileNotFoundError(f"The video file {video_path} does not exist.")
126
+
127
+ command = [
128
+ 'ffprobe',
129
+ '-v', 'error', # Suppress verbose output
130
+ '-select_streams', 'a', # Select only audio streams
131
+ '-show_entries', 'stream=index:stream_tags=language', # Show index and language tag
132
+ '-of', 'json', # Output in JSON format
133
+ str(video_path)
134
+ ]
135
+
136
+ try:
137
+ result = subprocess.run(command, check=True, capture_output=True, text=True)
138
+ data = json.loads(result.stdout)
139
+
140
+ audio_stream_info = []
141
+ for s in data.get('streams', []):
142
+ index = s.get('index')
143
+ language = s.get('tags', {}).get('language')
144
+ if index is not None:
145
+ audio_stream_info.append((index, language))
146
+ return audio_stream_info
147
+ except (subprocess.CalledProcessError, json.JSONDecodeError) as e:
148
+ raise FFmpegError(f"FFprobe error: {e.stderr if isinstance(e, subprocess.CalledProcessError) else e}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: plexflow
3
- Version: 0.0.122
3
+ Version: 0.0.123
4
4
  Summary: A short description of the package.
5
5
  License: MIT
6
6
  Keywords: keyword1,keyword2,keyword3
@@ -32,6 +32,7 @@ plexflow/core/context/partials/ids.py,sha256=QoQ6FbX1OIWrE-iuz-G6kSzBlTt1_I1jyfl
32
32
  plexflow/core/context/partials/movie.py,sha256=VXQ2SspFgGSRgDefg4VlHrH2fns3KRuKlU72ps6527o,3861
33
33
  plexflow/core/context/partials/movie_assets.py,sha256=qjZTs-lpPfZkQQSKm6CB4aeECX5_YzOom51PxZzmnts,1913
34
34
  plexflow/core/context/partials/reports.py,sha256=0W58RwK3VSsVHbF0rhvMNNlZZr01eutwermyvdeEZIs,810
35
+ plexflow/core/context/partials/show.py,sha256=BcGXISwnWYDbz_VhDkTmaZ98VDXoaF4wmM2b9wjxz3U,3389
35
36
  plexflow/core/context/partials/subtitles.py,sha256=0NhKGkP-sArQswuSyA7puRSjjoobF-3Ah7Pd39QkgTU,535
36
37
  plexflow/core/context/partials/tgx_batch.py,sha256=TduB09oBOQ8CtmPYsHIeNe7AI-ypKw21zQAX-7qktEs,859
37
38
  plexflow/core/context/partials/tgx_context.py,sha256=_FuhOvKsFqi_uynHxgC9_QIR2CfYmz-uJCRFtGFJmXI,1641
@@ -103,8 +104,8 @@ plexflow/core/metadata/auto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
103
104
  plexflow/core/metadata/auto/__pycache__/__init__.cpython-311.pyc,sha256=lH9qVMiMhT0beU3xsnhOgd0m2HXp8tnbVl85bwxYcLI,177
104
105
  plexflow/core/metadata/auto/__pycache__/__init__.cpython-312.pyc,sha256=TBF2IS00BSmTn_t5LvTujeJbtXD5D1Wd-_K4vjqb2kE,163
105
106
  plexflow/core/metadata/auto/__pycache__/auto_meta.cpython-311.pyc,sha256=fOZ525wZuT7oPzhUeLVrjCbVNil17f4HVVlpMYjTxec,2935
106
- plexflow/core/metadata/auto/__pycache__/auto_meta.cpython-312.pyc,sha256=2h6LnbOiA0gtmOI9Vl4tl3PVfYSR5DtOpV3aTZKAawk,2466
107
- plexflow/core/metadata/auto/auto_meta.py,sha256=yLLaE7YYNKjlqlR8JDhEcPVJU0uH2lujvURNU-XwoP0,1887
107
+ plexflow/core/metadata/auto/__pycache__/auto_meta.cpython-312.pyc,sha256=29bpuA-JSy19Ukt-dwIVN2A775ZO2cx3KLZ9V96s46E,2632
108
+ plexflow/core/metadata/auto/auto_meta.py,sha256=vuWnifrW000ls4LO3abhfzVNKAjcb4BXNzUXLrkmK1E,2075
108
109
  plexflow/core/metadata/auto/auto_providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
110
  plexflow/core/metadata/auto/auto_providers/__pycache__/__init__.cpython-311.pyc,sha256=IffYBef71LYioll3WjuKtsNDb5S7dUFd21v_vpqWKjo,192
110
111
  plexflow/core/metadata/auto/auto_providers/__pycache__/__init__.cpython-312.pyc,sha256=Azc9iLSZ4fgwbEL8lRcdBqiiyGBQ_HU57ihUnCa3MVI,178
@@ -121,7 +122,7 @@ plexflow/core/metadata/auto/auto_providers/auto/__pycache__/movie.cpython-312.py
121
122
  plexflow/core/metadata/auto/auto_providers/auto/__pycache__/season.cpython-311.pyc,sha256=4uxtPg7KE_Cut75mhjRjtnGVTgeinxbciJP3nlV1elY,2580
122
123
  plexflow/core/metadata/auto/auto_providers/auto/__pycache__/season.cpython-312.pyc,sha256=L6uj3tL3H1ZkHVJkYYKL4bbsTpC2SSODL7-4hdUTYi8,2307
123
124
  plexflow/core/metadata/auto/auto_providers/auto/__pycache__/show.cpython-311.pyc,sha256=3C_No5XMWNMuvHc7Vmlg_RKAFrTxcfFEhUskvW3CccE,2041
124
- plexflow/core/metadata/auto/auto_providers/auto/__pycache__/show.cpython-312.pyc,sha256=CrjCgW2cDlNcU836jMvtWGb9EN_iFdznGlN9Opi9w38,1759
125
+ plexflow/core/metadata/auto/auto_providers/auto/__pycache__/show.cpython-312.pyc,sha256=nC5CoqomV_9sPzXIYegqQ4-sragZhFLr70Y2ADigAyA,1759
125
126
  plexflow/core/metadata/auto/auto_providers/auto/episode.py,sha256=79QxeLpzaXp08wCS3VB3OboS0LoIsOUDfQ8uspfpBm4,1004
126
127
  plexflow/core/metadata/auto/auto_providers/auto/item.py,sha256=zZV0ArqzdLyde28eI93WjSS4PQoz-9YZ0ep80BYEPA8,1047
127
128
  plexflow/core/metadata/auto/auto_providers/auto/movie.py,sha256=ddiaLxlSsAuBHs14RUxW2xBDsfkc_LeEtu1GlP0uPLE,488
@@ -151,11 +152,17 @@ plexflow/core/metadata/auto/auto_providers/plex/__init__.py,sha256=47DEQpj8HBSa-
151
152
  plexflow/core/metadata/auto/auto_providers/plex/__pycache__/__init__.cpython-311.pyc,sha256=zhUOQ0VKGPvJIAViRNqPX7uawbiwevltDiXpJm-NizY,197
152
153
  plexflow/core/metadata/auto/auto_providers/plex/__pycache__/__init__.cpython-312.pyc,sha256=Mst-5xGQD5Xc_nqvW1-j7EJdEFMV4h16xsJptKOubFE,183
153
154
  plexflow/core/metadata/auto/auto_providers/plex/__pycache__/episode.cpython-311.pyc,sha256=giWCnLbrfYX5xIm9kRMhDOwJLhRtBv-NYE8bywjN-Ns,2602
155
+ plexflow/core/metadata/auto/auto_providers/plex/__pycache__/episode.cpython-312.pyc,sha256=3CUlqkhIDO6qWiMYSG9NyNpu9WF7wl-OD6O9lV1MWis,2351
154
156
  plexflow/core/metadata/auto/auto_providers/plex/__pycache__/movie.cpython-311.pyc,sha256=Ou8VJS0aNVu9sJQrb4TXLdzu8Ysc0BYgi_tUXUll834,3337
155
157
  plexflow/core/metadata/auto/auto_providers/plex/__pycache__/movie.cpython-312.pyc,sha256=w5kTMqGYoI5zs1HJy_Tat4hienwP0cdfYTlp0EXS79E,3094
156
158
  plexflow/core/metadata/auto/auto_providers/plex/__pycache__/season.cpython-311.pyc,sha256=V8CHtjkpHNLZUaRCrzUVquPEXHnOGW5INQR_2mjQ1vI,2597
159
+ plexflow/core/metadata/auto/auto_providers/plex/__pycache__/season.cpython-312.pyc,sha256=TaRq1jZQQ_djZRoewUioSj39j0XM4Mti8BJ9pgn3kE0,2288
157
160
  plexflow/core/metadata/auto/auto_providers/plex/__pycache__/show.cpython-311.pyc,sha256=BtLZsFDxLpnVOvhwHVHhsteQqrm7c4p7t0YUTqJEk4Y,3993
161
+ plexflow/core/metadata/auto/auto_providers/plex/__pycache__/show.cpython-312.pyc,sha256=HDQ18cj4qXVM-ESiEAqtw-GZs6-JBWYpOPwLyKBYIFU,3667
162
+ plexflow/core/metadata/auto/auto_providers/plex/episode.py,sha256=CoE2lJoJ-RcKTRYdcEUS-Lr6AXmKKsBbcG8beCMmkvs,985
158
163
  plexflow/core/metadata/auto/auto_providers/plex/movie.py,sha256=SnIUD94wsSBpsEk5ER0xLtzn5WGgdlcKyDo9qjFUUEs,1199
164
+ plexflow/core/metadata/auto/auto_providers/plex/season.py,sha256=57dUBrM4Oo0fmX-vgg4j7Ssgznuiw6Cl-PjW-KaQxmY,1129
165
+ plexflow/core/metadata/auto/auto_providers/plex/show.py,sha256=ilyGlXoggzMwsM5fMK4Gi38NcIsTdn1mlekqT_RJaJs,1570
159
166
  plexflow/core/metadata/auto/auto_providers/tmdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
160
167
  plexflow/core/metadata/auto/auto_providers/tmdb/__pycache__/__init__.cpython-311.pyc,sha256=u4k9dx-nYwwP84Wnd-MMEU9ySXazNIuZC-WCjvnDr8g,197
161
168
  plexflow/core/metadata/auto/auto_providers/tmdb/__pycache__/__init__.cpython-312.pyc,sha256=m2uIdevu5Xl7qq5jYJT8UwIfrMzggkyn-YRFflzR5zY,183
@@ -215,13 +222,13 @@ plexflow/core/metadata/providers/plex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCe
215
222
  plexflow/core/metadata/providers/plex/__pycache__/__init__.cpython-311.pyc,sha256=Dn0hfFPLViFaic_zPb69TzXE3CVgDayCBJbfj5GM6N8,187
216
223
  plexflow/core/metadata/providers/plex/__pycache__/__init__.cpython-312.pyc,sha256=fwdj_MNjIInQSPXMhY9YiRSe9CJAwPElwbYa1fNbRvE,173
217
224
  plexflow/core/metadata/providers/plex/__pycache__/datatypes.cpython-311.pyc,sha256=S6n0CREPam6rZ5qDl-iasX-yYLFAoQCwr3zAVU1OKMI,49762
218
- plexflow/core/metadata/providers/plex/__pycache__/datatypes.cpython-312.pyc,sha256=izUC-dJsaGmX_gTHwvM5l8FumMJrESNDQL8rxgzot78,42907
225
+ plexflow/core/metadata/providers/plex/__pycache__/datatypes.cpython-312.pyc,sha256=leELWfqr32G9VbGGawOESJYUtB2F_Ofd2__2wsIValA,45957
219
226
  plexflow/core/metadata/providers/plex/__pycache__/imdb.cpython-311.pyc,sha256=N_uu32ljM0xa1Nn8NK43QtU9tYRw_LyBblq7FgbfCg0,4342
220
227
  plexflow/core/metadata/providers/plex/__pycache__/moviemeter.cpython-311.pyc,sha256=P78PwQIcVFzjwQqjHRGHhoWx6XtHRqJKIWeeX_jId4s,2551
221
228
  plexflow/core/metadata/providers/plex/__pycache__/plex.cpython-311.pyc,sha256=FgbspEc3m5Vkm_m9Ry7gVAavs_2-4Er4FIREdJgaDIQ,8182
222
229
  plexflow/core/metadata/providers/plex/__pycache__/plex.cpython-312.pyc,sha256=GKjYqAdmiDSuAQ3HlsQJMRAHfi65sbw7maeLcuq6rbs,7334
223
230
  plexflow/core/metadata/providers/plex/__pycache__/tmdb.cpython-311.pyc,sha256=vWlpw_-ldSWwFvz67kTwxK9XxZPy7iExyLgD6rTOf10,4200
224
- plexflow/core/metadata/providers/plex/datatypes.py,sha256=-PSBCSyR5sfA2TOFCAr5mlnfSMngz8OaEjcc51CCw_U,32441
231
+ plexflow/core/metadata/providers/plex/datatypes.py,sha256=-EWmoJkitlNLSNpyvWDZX1qPw9vjXenMnQZVrwwazOk,34235
225
232
  plexflow/core/metadata/providers/plex/plex.py,sha256=KrOjSLzeZVIGDnLDC7dw87Yizq5h3Fbf9MtPv_tcET8,5918
226
233
  plexflow/core/metadata/providers/tmdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
227
234
  plexflow/core/metadata/providers/tmdb/__pycache__/__init__.cpython-311.pyc,sha256=djXYmvhVt2MFuzdiZ7ojQCXMa9UffVv_j_hBl0bdIvM,187
@@ -699,12 +706,12 @@ plexflow/utils/transcribe/__pycache__/speech2text.cpython-311.pyc,sha256=WcYPF8J
699
706
  plexflow/utils/transcribe/__pycache__/speech2text.cpython-312.pyc,sha256=dY538EbFsJXzZr3139_TrtuLytcmculX_yBhi9DeKaM,2842
700
707
  plexflow/utils/transcribe/speech2text.py,sha256=ldcZqx18Yr8aa-y5sw7WaEKCci9mvBI0z06YQqJemnU,3547
701
708
  plexflow/utils/video/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
702
- plexflow/utils/video/__pycache__/__init__.cpython-312.pyc,sha256=Kn1y-pxBX-xxS-zJHHevU3gCL7pkzwWlX5GA68ICJ1M,158
703
- plexflow/utils/video/__pycache__/audio.cpython-312.pyc,sha256=vXBnJwWgTDFdixMBs-QJeeejwXQJ4wnXuG6oYqbqaSk,4497
709
+ plexflow/utils/video/__pycache__/__init__.cpython-312.pyc,sha256=SA7I28R8t-m5Q4uA7laJCUz3JHqVgUtkvewvrAk1qVk,156
710
+ plexflow/utils/video/__pycache__/audio.cpython-312.pyc,sha256=kmzGDCHSC1hWyHwRutWunOWNKatY613d1gmhz5VILpw,6686
704
711
  plexflow/utils/video/__pycache__/subtitle.cpython-312.pyc,sha256=PCjpCLydGXaRsQy6cikhgsEs8WlComfOoYPiLFqfVMA,2515
705
- plexflow/utils/video/audio.py,sha256=tJ_lNwcjVuBQYD5cYOlXpr__eh8-hnReIgNRgIYOpqo,3380
712
+ plexflow/utils/video/audio.py,sha256=Pd8OuQHX2QN-lc5iYkB0Vo1OEHmTcvDYH-uKud1f1q4,5262
706
713
  plexflow/utils/video/subtitle.py,sha256=qPvvBjlPj0fynJJvGJgGeKt9ey26R-cF6EoLaYt9iXU,1333
707
- plexflow-0.0.122.dist-info/METADATA,sha256=9kmjVeA5QrcKOrh-1ZSuhWipXjnNT2YW4zcQhoMNeKI,2971
708
- plexflow-0.0.122.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
709
- plexflow-0.0.122.dist-info/entry_points.txt,sha256=9RJC3ikOQORHNOn573EdwJOBUnFU_4EGHbtNUM5pjjY,1557
710
- plexflow-0.0.122.dist-info/RECORD,,
714
+ plexflow-0.0.123.dist-info/METADATA,sha256=Z7UDvQ_ZclEi0KBFhCaWZkqsgU6mBZPzYyF0_fJoAPw,2971
715
+ plexflow-0.0.123.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
716
+ plexflow-0.0.123.dist-info/entry_points.txt,sha256=9RJC3ikOQORHNOn573EdwJOBUnFU_4EGHbtNUM5pjjY,1557
717
+ plexflow-0.0.123.dist-info/RECORD,,