udemy-userAPI 0.2.8__py3-none-any.whl → 0.2.9__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.
@@ -1,4 +1,4 @@
1
- __version__ = '0.2.8'
1
+ __version__ = '0.2.9'
2
2
  __lib_name__ = 'udemy_userAPI' # local name
3
3
  __repo_name__ = 'udemy-userAPI'
4
4
  __autor__ = 'PauloCesar-dev404'
udemy_userAPI/api.py CHANGED
@@ -168,7 +168,7 @@ def get_mpd_file(mpd_url):
168
168
  data = []
169
169
  # Exibe o código de status
170
170
  if response.status_code == 200:
171
- return response.content
171
+ return response.text
172
172
  else:
173
173
  UnhandledExceptions(f"erro ao obter dados de aulas!! {response.status_code}")
174
174
  except requests.ConnectionError as e:
@@ -186,7 +186,6 @@ def get_mpd_file(mpd_url):
186
186
  def parser_chapers(results):
187
187
  """
188
188
  :param results:
189
- :param tip: chaper,videos
190
189
  :return:
191
190
  """
192
191
  if not results:
udemy_userAPI/bultins.py CHANGED
@@ -9,32 +9,35 @@ from .mpd_analyzer import MPDParser
9
9
 
10
10
  class DRM:
11
11
  def __init__(self, license_token: str, get_media_sources: list):
12
- self.__mpd_content = None
12
+ self.__mpd_file_path = None
13
13
  self.__token = license_token
14
14
  self.__dash_url = organize_streams(streams=get_media_sources).get('dash', {})
15
- if not license_token or not get_media_sources or not self.__dash_url:
15
+ if not license_token or get_media_sources:
16
16
  return
17
17
 
18
18
  def get_key_for_lesson(self):
19
19
  """get keys for lesson"""
20
- if self.__dash_url:
21
- self.__mpd_content = get_mpd_file(mpd_url=self.__dash_url[0].get('src'))
22
- parser = MPDParser(mpd_file_path=self.__mpd_content, is_file=True, headers=HEADERS_USER)
23
- resolutions = get_highest_resolution(parser.get_all_video_resolutions())
24
- parser.set_selected_resolution(resolution=resolutions)
25
- init_url = parser.get_selected_video_init_url()
26
- if init_url:
27
- pssh = get_pssh(init_url=init_url)
28
- if pssh:
29
- keys = extract(pssh=pssh, license_token=self.__token)
30
- if keys:
31
- return keys
32
- else:
33
- return None
20
+ try:
21
+ if self.__dash_url:
22
+ self.__mpd_file_path = get_mpd_file(mpd_url=self.__dash_url[0].get('src'))
23
+ parser = MPDParser(mpd_content=self.__mpd_file_path)
24
+ resolutions = get_highest_resolution(parser.get_all_video_resolutions())
25
+ parser.set_selected_resolution(resolution=resolutions)
26
+ init_url = parser.get_selected_video_init_url()
27
+ if init_url:
28
+ pssh = get_pssh(init_url=init_url)
29
+ if pssh:
30
+ keys = extract(pssh=pssh, license_token=self.__token)
31
+ if keys:
32
+ return keys
33
+ else:
34
+ return None
35
+ else:
36
+ return None
34
37
  else:
35
38
  return None
36
- else:
37
- return None
39
+ except Exception as e:
40
+ raise Exception(f"Não foi possível obter as chaves!\n{e}")
38
41
 
39
42
 
40
43
  class Files:
@@ -44,8 +47,7 @@ class Files:
44
47
 
45
48
  @property
46
49
  def get_download_url(self) -> dict[str, Any | None] | list[dict[str, Any | None]]:
47
- """obter url de download de um arquivo quando disponivel(geralemnete para arquivos esta opção é valida"""
48
- da = {}
50
+ """Obter url de download de um arquivo quando disponivel(geralemnete para arquivos esta opção é valida"""
49
51
  download_urls = []
50
52
  for files in self.__data:
51
53
  lecture_id = files.get('lecture_id', None)
@@ -68,6 +70,7 @@ class Files:
68
70
  headers=HEADERS_USER)
69
71
  if resp.status_code == 200:
70
72
  da = json.loads(resp.text)
73
+ # para cdaa dict de um fle colocar seu titulo:
71
74
  dt_file = {'title-file': title,
72
75
  'lecture_title': lecture_title,
73
76
  'lecture_id': lecture_id,
@@ -84,12 +87,12 @@ class Lecture:
84
87
  self.__course_id = course_id
85
88
  self.__data = data
86
89
  self.__additional_files = additional_files
87
- self.__asset = self.__data.get("asset")
90
+ self.__asset = self.__data.get("asset", {})
88
91
 
89
92
  @property
90
93
  def get_lecture_id(self) -> int:
91
94
  """Obtém o ID da lecture"""
92
- return self.__data.get('id')
95
+ return self.__data.get('id', 0)
93
96
 
94
97
  @property
95
98
  def get_description(self) -> str:
@@ -138,10 +141,12 @@ class Lecture:
138
141
  def course_is_drmed(self) -> DRM:
139
142
  """verifica se a aula possui DRM se sim retorna as keys da aula...
140
143
  retorna 'kid:key' or None"""
141
- if self.__asset.get('course_is_drmed', {}):
144
+ try:
142
145
  d = DRM(license_token=self.get_media_license_token,
143
146
  get_media_sources=self.get_media_sources)
144
147
  return d
148
+ except Exception as e:
149
+ DeprecationWarning(e)
145
150
 
146
151
  @property
147
152
  def get_download_urls(self) -> list:
@@ -271,16 +276,15 @@ class Course:
271
276
  def get_additional_files(self) -> list[Any]:
272
277
  """Retorna a lista de arquivos adcionais de um curso."""
273
278
  supplementary_assets = []
274
- files_downloader = []
275
279
  for item in self.__additional_files_data.get('results', []):
276
280
  # Check if the item is a lecture with supplementary assets
277
281
  if item.get('_class') == 'lecture':
278
- id = item.get('id', {})
282
+ id_l = item.get('id', {})
279
283
  title = item.get('title', {})
280
284
  assets = item.get('supplementary_assets', [])
281
285
  for asset in assets:
282
286
  supplementary_assets.append({
283
- 'lecture_id': id,
287
+ 'lecture_id': id_l,
284
288
  'lecture_title': title,
285
289
  'asset': asset
286
290
  })
@@ -292,16 +296,15 @@ class Course:
292
296
  def __load_assets(self):
293
297
  """Retorna a lista de arquivos adcionais de um curso."""
294
298
  supplementary_assets = []
295
- files_downloader = []
296
299
  for item in self.__additional_files_data.get('results', []):
297
300
  # Check if the item is a lecture with supplementary assets
298
301
  if item.get('_class') == 'lecture':
299
- id = item.get('id')
302
+ id_l = item.get('id')
300
303
  title = item.get('title')
301
304
  assets = item.get('supplementary_assets', [])
302
305
  for asset in assets:
303
306
  supplementary_assets.append({
304
- 'lecture_id': id,
307
+ 'lecture_id': id_l,
305
308
  'lecture_title': title,
306
309
  'asset': asset
307
310
  })
@@ -1,304 +1,191 @@
1
1
  import re
2
- import xml.etree.ElementTree as ET
2
+ import xml.etree.ElementTree as Et
3
+
4
+
5
+ def calculate_segment_url2(media_template, segment_number, segment_time, rep_id):
6
+ """
7
+ Calcula a URL de um segmento específico, substituindo variáveis no template.
8
+ """
9
+ url = media_template.replace('$Number$', str(segment_number))
10
+ url = url.replace('$RepresentationID$', rep_id)
11
+ if '$Time$' in url:
12
+ url = url.replace('$Time$', str(segment_time))
13
+ return url
14
+
15
+
16
+ def build_url2(template, rep_id):
17
+ """
18
+ Constrói a URL substituindo variáveis no template com base nos atributos.
19
+ """
20
+ if '$RepresentationID$' in template:
21
+ template = template.replace('$RepresentationID$', rep_id)
22
+ return template
23
+
24
+
25
+ def parse_duration(duration_str):
26
+ """
27
+ Converte uma duração em formato ISO 8601 (ex: "PT163.633S") para segundos (float).
28
+ """
29
+ match = re.match(r'PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?', duration_str)
30
+ if match:
31
+ hours = int(match.group(1)) if match.group(1) else 0
32
+ minutes = int(match.group(2)) if match.group(2) else 0
33
+ seconds = float(match.group(3)) if match.group(3) else 0.0
34
+ return hours * 3600 + minutes * 60 + seconds
35
+ return 0.0
3
36
 
4
37
 
5
38
  class MPDParser:
6
39
  """
7
40
  Classe para analisar e extrair informações de manifestos MPD (Media Presentation Description),
8
- com foco em arquivos VOD (Video on Demand). Atualmente, não oferece suporte para transmissões ao vivo
41
+ com foco em arquivos VOD (Video on Demand). Atualmente, não oferece suporte para transmissões ao vivo.
9
42
  """
10
43
 
11
- def __init__(self, mpd_file_path: str, headers=None,is_file=None):
44
+ def __init__(self, mpd_content: str):
12
45
  """
13
- Inicializa o parser para um arquivo MPD a partir de um caminho de arquivo.
46
+ Inicializa o parser para um arquivo MPD.
14
47
 
15
48
  Args:
16
- mpd_file_path (str): Caminho do arquivo MPD.
17
- headers (dict, opcional): Headers HTTP adicionais para requisição, caso necessário.
49
+ mpd_content (str): Caminho do arquivo MPD ou conteúdo bruto.
18
50
  """
19
- self.is_file = is_file
20
- self.availability_start_time = None
21
- self.mpd_file_path = mpd_file_path
22
- self.headers = headers if headers else {}
23
- self.video_representations = {} # Armazena representações de vídeo, organizadas por resolução
24
- self.audio_representations = {} # Armazena representações de áudio, organizadas por taxa de bits
25
- self.content_protection = {} # Armazena informações de proteção de conteúdo
26
- self.selected_resolution = None # Resolução selecionada para recuperação de segmentos de vídeo
27
- self.initi = self.__parse_mpd2()
28
- if not self.initi:
29
- initi = self.__parse_mpd()
30
-
31
- def __parse_mpd(self):
51
+ self._mpd_content = mpd_content
52
+ self._video_representations = {}
53
+ self._audio_representations = {}
54
+ self._content_protection = {}
55
+ self._selected_resolution = None
56
+
57
+ # Tenta fazer o parsing com diferentes métodos
58
+ if not self.__parse_mpd_v2():
59
+ self.__parse_mpd_v1()
60
+
61
+ def __parse_mpd_v1(self):
32
62
  """
33
- Faz o parsing do arquivo MPD localizado no caminho especificado e
34
- extrai informações sobre segmentos de vídeo e áudio, além de proteção de conteúdo.
63
+ Parsing básico do MPD (versão 1).
35
64
  """
36
- mpd_content = ''
37
- if not self.is_file:
38
- try:
39
- with open(self.mpd_file_path, 'r', encoding='utf-8') as file:
40
- mpd_content = file.read()
41
- except FileNotFoundError:
42
- print(f"Erro: Arquivo '{self.mpd_file_path}' não encontrado.")
43
- return
44
- except IOError:
45
- print(f"Erro ao ler o arquivo '{self.mpd_file_path}'.")
46
- return
47
- else:
48
- mpd_content = self.mpd_file_path
49
- # Analisa o conteúdo MPD usando namespaces XML para acessar nós DASH
50
- root = ET.fromstring(mpd_content)
65
+ content = self._mpd_content
66
+ root = Et.fromstring(content)
51
67
  ns = {'dash': 'urn:mpeg:dash:schema:mpd:2011'}
52
68
 
53
- # Processa cada AdaptationSet para extração de representações de áudio e vídeo
54
69
  for adaptation_set in root.findall('.//dash:AdaptationSet', ns):
55
70
  mime_type = adaptation_set.attrib.get('mimeType', '')
56
- # Extrai informações de proteção de conteúdo, se presentes
57
- for content_protection in adaptation_set.findall('dash:ContentProtection', ns):
58
- scheme_id_uri = content_protection.attrib.get('schemeIdUri', '')
59
- value = content_protection.attrib.get('value', '')
60
- self.content_protection[scheme_id_uri] = value
61
-
62
- # Extrai informações de cada representação de mídia
63
- for representation in adaptation_set.findall('dash:Representation', ns):
64
- rep_id = representation.attrib.get('id')
65
- width = int(representation.attrib.get('width', 0))
66
- height = int(representation.attrib.get('height', 0))
67
- resolution = (width, height) if width and height else None
68
- bandwidth = int(representation.attrib.get('bandwidth', 0))
69
- # Obtém a quantidade de canais de áudio, se disponível
70
- audio_channels = representation.find('dash:AudioChannelConfiguration', ns)
71
- audio_channels_count = int(audio_channels.attrib.get('value', 0)) if audio_channels is not None else 0
72
-
73
- # Processa SegmentTemplate para URLs de inicialização e segmentos
74
- segment_template = representation.find('dash:SegmentTemplate', ns)
75
- if segment_template is not None:
76
- init_template = segment_template.get('initialization')
77
- init_url = self.__build_url(init_template, rep_id, bandwidth) if init_template else None
78
-
79
- media_url_template = segment_template.get('media')
80
- timescale = int(segment_template.get('timescale', 1))
81
-
82
- # Processa SegmentTimeline para obtenção de segmentos individuais
83
- segment_timeline = segment_template.find('dash:SegmentTimeline', ns)
84
- segments = []
85
- if segment_timeline is not None:
86
- segment_number = int(segment_template.get('startNumber', 1))
87
- start_time = 0
88
- for segment in segment_timeline.findall('dash:S', ns):
89
- t = int(segment.get('t', start_time))
90
- d = int(segment.get('d'))
91
- r = int(segment.get('r', 0))
92
-
93
- # Adiciona segmentos repetidos se necessário
94
- for _ in range(r + 1):
95
- segments.append(
96
- self.__calculate_segment_url(media_url_template, segment_number, t, rep_id,
97
- bandwidth)
98
- )
99
- t += d
100
- segment_number += 1
101
-
102
- # Armazena informações de representação com resolução ou taxa de bits como chave
103
- representation_info = {
104
- 'id': rep_id,
105
- 'resolution': resolution,
106
- 'bandwidth': bandwidth,
107
- 'audio_channels': audio_channels_count,
108
- 'init_url': init_url,
109
- 'segments': segments,
110
- }
111
- if 'video' in mime_type:
112
- self.video_representations[resolution] = representation_info
113
- elif 'audio' in mime_type:
114
- self.audio_representations[bandwidth] = representation_info
115
-
116
- def __parse_mpd2(self):
71
+ self.__parse_adaptation_set(adaptation_set, mime_type, ns)
72
+
73
+ def __parse_mpd_v2(self):
117
74
  """
118
- Faz o parsing do arquivo MPD localizado no caminho especificado e
119
- extrai informações sobre segmentos de vídeo e áudio, além de proteção de conteúdo.
75
+ Parsing avançado do MPD (versão 2).
120
76
  """
121
- mpd_content = ''
122
- if not self.is_file:
123
- try:
124
- with open(self.mpd_file_path, 'r', encoding='utf-8') as file:
125
- mpd_content = file.read()
126
- except FileNotFoundError:
127
- print(f"Erro: Arquivo '{self.mpd_file_path}' não encontrado.")
128
- return
129
- except IOError:
130
- print(f"Erro ao ler o arquivo '{self.mpd_file_path}'.")
131
- return
132
- else:
133
- mpd_content = self.mpd_file_path
134
-
135
- # Analisar o conteúdo MPD
136
- root = ET.fromstring(mpd_content)
77
+ content = self._mpd_content
78
+ root = Et.fromstring(content)
137
79
  ns = {'dash': 'urn:mpeg:dash:schema:mpd:2011'}
138
80
 
139
- # Extrai a duração total da apresentação em segundos
140
- self.media_presentation_duration = self.parse_duration(root.attrib.get('mediaPresentationDuration', 'PT0S'))
141
-
142
81
  for adaptation_set in root.findall('.//dash:AdaptationSet', ns):
143
82
  mime_type = adaptation_set.attrib.get('mimeType', '')
83
+ self.__parse_adaptation_set(adaptation_set, mime_type, ns)
84
+ return True
144
85
 
145
- # Extrai proteção de conteúdo
146
- for content_protection in adaptation_set.findall('dash:ContentProtection', ns):
147
- scheme_id_uri = content_protection.attrib.get('schemeIdUri', '')
148
- value = content_protection.attrib.get('value', '')
149
- self.content_protection[scheme_id_uri] = value
150
-
151
- # Extrai representações de vídeo e áudio
152
- for representation in adaptation_set.findall('dash:Representation', ns):
153
- rep_id = representation.attrib.get('id')
154
- width = int(representation.attrib.get('width', 0))
155
- height = int(representation.attrib.get('height', 0))
156
- resolution = (width, height) if width and height else None
157
- bandwidth = int(representation.attrib.get('bandwidth', 0))
158
-
159
- # SegmentTemplate e SegmentTimeline
160
- segment_template = adaptation_set.find('dash:SegmentTemplate', ns)
161
- if segment_template is not None:
162
- init_template = segment_template.get('initialization')
163
- media_template = segment_template.get('media')
164
- timescale = int(segment_template.get('timescale', 1))
165
- start_number = int(segment_template.get('startNumber', 1))
166
-
167
- # Processa SegmentTimeline
168
- segment_timeline = segment_template.find('dash:SegmentTimeline', ns)
169
- segments = []
170
- if segment_timeline is not None:
171
- segment_number = start_number
172
- for segment in segment_timeline.findall('dash:S', ns):
173
- t = int(segment.get('t', 0))
174
- d = int(segment.get('d'))
175
- r = int(segment.get('r', 0)) # Quantidade de repetições
176
-
177
- for i in range(r + 1): # Inclui o segmento e suas repetições
178
- segment_time = t + i * d
179
- segment_url = self.__calculate_segment_url2(media_template, segment_number, segment_time,
180
- rep_id)
181
- segments.append(segment_url)
182
- segment_number += 1
183
- else:
184
- # No SegmentTimeline, gera segmentos contínuos
185
- duration = int(segment_template.get('duration', 1))
186
- total_segments = int((self.media_presentation_duration * timescale) // duration)
187
- for segment_number in range(start_number, start_number + total_segments):
188
- segment_time = (segment_number - 1) * duration
189
- segment_url = self.__calculate_segment_url2(media_template, segment_number, segment_time,
190
- rep_id)
191
- segments.append(segment_url)
192
-
193
- # Armazena representações de vídeo e áudio com URLs de segmentos
194
- representation_info = {
195
- 'id': rep_id,
196
- 'resolution': resolution,
197
- 'bandwidth': bandwidth,
198
- 'init_url': self.__build_url2(init_template, rep_id),
199
- 'segments': segments,
200
- }
201
- if 'video' in mime_type:
202
- self.video_representations[resolution] = representation_info
203
- elif 'audio' in mime_type:
204
- self.audio_representations[bandwidth] = representation_info
205
- def parse_duration(self, duration_str):
86
+ def __parse_adaptation_set(self, adaptation_set, mime_type, ns):
206
87
  """
207
- Converte uma duração em formato ISO 8601 (ex: "PT163.633S") para segundos (float).
88
+ Analisa um AdaptationSet para representações de vídeo ou áudio.
89
+
90
+ Args:
91
+ adaptation_set (ET.Element): Elemento do AdaptationSet.
92
+ mime_type (str): Tipo MIME (vídeo ou áudio).
93
+ ns (dict): Namespace para parsing do XML.
208
94
  """
209
- match = re.match(r'PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?', duration_str)
210
- if match:
211
- hours = int(match.group(1)) if match.group(1) else 0
212
- minutes = int(match.group(2)) if match.group(2) else 0
213
- seconds = float(match.group(3)) if match.group(3) else 0.0
214
- return hours * 3600 + minutes * 60 + seconds
215
- return 0.0
95
+ # Extrai informações de proteção de conteúdo
96
+ for content_protection in adaptation_set.findall('dash:ContentProtection', ns):
97
+ scheme_id_uri = content_protection.attrib.get('schemeIdUri', '')
98
+ value = content_protection.attrib.get('value', '')
99
+ self._content_protection[scheme_id_uri] = value
216
100
 
217
- @staticmethod
218
- def __build_url(template, rep_id, bandwidth):
101
+ # Processa representações dentro do AdaptationSet
102
+ for representation in adaptation_set.findall('dash:Representation', ns):
103
+ self.__process_representation(representation, mime_type, ns)
104
+
105
+ def __process_representation(self, representation, mime_type, ns):
219
106
  """
220
- Constrói uma URL substituindo placeholders em um template de URL.
107
+ Processa uma representação de mídia (vídeo ou áudio).
221
108
 
222
109
  Args:
223
- template (str): Template de URL com placeholders.
110
+ representation (ET.Element): Elemento da Representação.
111
+ mime_type (str): Tipo MIME da mídia.
112
+ ns (dict): Namespace para parsing do XML.
113
+ """
114
+ rep_id = representation.attrib.get('id')
115
+ width = int(representation.attrib.get('width', 0))
116
+ height = int(representation.attrib.get('height', 0))
117
+ resolution = (width, height) if width and height else None
118
+ bandwidth = int(representation.attrib.get('bandwidth', 0))
119
+
120
+ # Extrai informações do SegmentTemplate
121
+ segment_template = representation.find('dash:SegmentTemplate', ns)
122
+ if segment_template:
123
+ init_url = self.__build_url(segment_template.get('initialization'), rep_id, bandwidth)
124
+ segments = self.__generate_segments(segment_template, ns, rep_id, bandwidth)
125
+
126
+ representation_info = {
127
+ 'id': rep_id,
128
+ 'resolution': resolution,
129
+ 'bandwidth': bandwidth,
130
+ 'init_url': init_url,
131
+ 'segments': segments,
132
+ }
133
+ if 'video' in mime_type:
134
+ self._video_representations[resolution] = representation_info
135
+ elif 'audio' in mime_type:
136
+ self._audio_representations[bandwidth] = representation_info
137
+
138
+ def __generate_segments(self, segment_template, ns, rep_id, bandwidth):
139
+ """
140
+ Gera a lista de URLs de segmentos com base no SegmentTemplate.
141
+
142
+ Args:
143
+ segment_template (ET.Element): Elemento do SegmentTemplate.
144
+ ns (dict): Namespace para parsing do XML.
224
145
  rep_id (str): ID da representação.
225
146
  bandwidth (int): Largura de banda da representação.
226
147
 
227
148
  Returns:
228
- str: URL formatada com placeholders substituídos.
229
- """
230
- if '$RepresentationID$' in template:
231
- template = template.replace('$RepresentationID$', rep_id)
232
- if '$Bandwidth$' in template:
233
- template = template.replace('$Bandwidth$', str(bandwidth))
234
- return template
235
-
236
- def __build_url2(self, template, rep_id):
237
- """
238
- Constrói a URL substituindo variáveis no template com base nos atributos.
239
- """
240
- if '$RepresentationID$' in template:
241
- template = template.replace('$RepresentationID$', rep_id)
242
- return template
149
+ list: URLs dos segmentos.
150
+ """
151
+ segments = []
152
+ media_template = segment_template.get('media')
153
+ segment_timeline = segment_template.find('dash:SegmentTimeline', ns)
154
+
155
+ if segment_timeline:
156
+ segment_number = int(segment_template.get('startNumber', 1))
157
+ for segment in segment_timeline.findall('dash:S', ns):
158
+ t = int(segment.get('t', 0))
159
+ d = int(segment.get('d'))
160
+ r = int(segment.get('r', 0))
161
+ for i in range(r + 1):
162
+ segment_time = t + i * d
163
+ segments.append(self.__build_url(media_template, rep_id, bandwidth, segment_time, segment_number))
164
+ segment_number += 1
165
+ return segments
243
166
 
244
167
  @staticmethod
245
- def __calculate_segment_url(media_template, segment_number, segment_time, rep_id, bandwidth):
168
+ def __build_url(template, rep_id, bandwidth, segment_time=None, segment_number=None):
246
169
  """
247
- Constrói a URL de um segmento substituindo placeholders por valores reais.
170
+ Constrói uma URL substituindo placeholders.
248
171
 
249
172
  Args:
250
- media_template (str): Template de URL do segmento.
251
- segment_number (int): Número do segmento.
252
- segment_time (int): Timestamp do segmento.
173
+ template (str): Template de URL.
253
174
  rep_id (str): ID da representação.
254
- bandwidth (int): Largura de banda da representação.
175
+ bandwidth (int): Largura de banda.
176
+ segment_time (int, opcional): Timestamp do segmento.
177
+ segment_number (int, opcional): Número do segmento.
255
178
 
256
179
  Returns:
257
- str: URL do segmento com placeholders substituídos.
180
+ str: URL formatada.
258
181
  """
259
- url = media_template.replace('$Number$', str(segment_number))
260
- url = url.replace('$RepresentationID$', rep_id).replace('$Bandwidth$', str(bandwidth))
261
- if '$Time$' in url:
182
+ url = template.replace('$RepresentationID$', rep_id).replace('$Bandwidth$', str(bandwidth))
183
+ if segment_time is not None:
262
184
  url = url.replace('$Time$', str(segment_time))
185
+ if segment_number is not None:
186
+ url = url.replace('$Number$', str(segment_number))
263
187
  return url
264
188
 
265
- def __calculate_segment_url2(self, media_template, segment_number, segment_time, rep_id):
266
- """
267
- Calcula a URL de um segmento específico, substituindo variáveis no template.
268
- """
269
- url = media_template.replace('$Number$', str(segment_number))
270
- url = url.replace('$RepresentationID$', rep_id)
271
- if '$Time$' in url:
272
- url = url.replace('$Time$', str(segment_time))
273
- return url
274
-
275
- def get_video_representations(self):
276
- """
277
- Retorna as representações de vídeo extraídas do arquivo MPD.
278
-
279
- Returns:
280
- dict: Representações de vídeo com resoluções como chaves.
281
- """
282
- return self.video_representations
283
-
284
- def get_audio_representations(self):
285
- """
286
- Retorna as representações de áudio extraídas do arquivo MPD.
287
-
288
- Returns:
289
- dict: Representações de áudio com taxas de bits como chaves.
290
- """
291
- return self.audio_representations
292
-
293
- def get_content_protection_info(self):
294
- """
295
- Retorna as informações de proteção de conteúdo extraídas do MPD.
296
-
297
- Returns:
298
- dict: Dados de proteção de conteúdo com URI do esquema como chaves.
299
- """
300
- return self.content_protection
301
-
302
189
  def set_selected_resolution(self, resolution: tuple):
303
190
  """
304
191
  Define a resolução selecionada para a recuperação de segmentos de vídeo.
@@ -309,23 +196,12 @@ class MPDParser:
309
196
  Raises:
310
197
  Exception: Se a resolução não estiver disponível no manifesto.
311
198
  """
312
- if resolution in self.video_representations:
313
- self.selected_resolution = resolution
199
+ if resolution in self._video_representations:
200
+ self._selected_resolution = resolution
314
201
  else:
315
202
  raise Exception(
316
- f'A resolução {resolution} não está disponível!\n\n\t=> Resoluções disponíveis no arquivo: {self.get_all_video_resolutions()}')
317
-
318
- def get_selected_video_segments(self):
319
- """
320
- Retorna os URLs dos segmentos de vídeo para a resolução selecionada.
321
-
322
- Returns:
323
- list: URLs dos segmentos de vídeo para a resolução selecionada.
324
- """
325
- if self.selected_resolution:
326
- return self.video_representations[self.selected_resolution].get('segments', [])
327
- else:
328
- raise Exception(f'Você deve selecioanar uma resolução no método self.set_selected_resolution()')
203
+ f'A resolução {resolution} não está disponível!\n\n'
204
+ f'\t=> Resoluções disponíveis no arquivo: {self.get_all_video_resolutions()}')
329
205
 
330
206
  def get_selected_video_init_url(self):
331
207
  """
@@ -334,8 +210,8 @@ class MPDParser:
334
210
  Returns:
335
211
  str: URL de inicialização do vídeo, ou None se não houver resolução selecionada.
336
212
  """
337
- if self.selected_resolution:
338
- return self.video_representations[self.selected_resolution].get('init_url')
213
+ if self._selected_resolution:
214
+ return self._video_representations[self._selected_resolution].get('init_url')
339
215
  return None
340
216
 
341
217
  def get_all_video_resolutions(self):
@@ -343,15 +219,6 @@ class MPDParser:
343
219
  Retorna uma lista de todas as resoluções de vídeo disponíveis.
344
220
 
345
221
  Returns:
346
- list: Lista de tuplas com resoluções de vídeo (largura, altura).
347
- """
348
- return list(self.video_representations.keys())
349
-
350
- def get_audio_channels_count(self):
351
- """
352
- Retorna um dicionário com a quantidade de canais de áudio para cada taxa de bits de áudio.
353
-
354
- Returns:
355
- dict: Quantidade de canais de áudio para cada taxa de bits.
222
+ list: lista de tuplas com resoluções de vídeo (largura, altura).
356
223
  """
357
- return {bandwidth: info['audio_channels'] for bandwidth, info in self.audio_representations.items()}
224
+ return list(self._video_representations.keys())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: udemy_userAPI
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: Obtenha detalhes de cursos que o usuário esteja inscrito da plataforma Udemy,usando o EndPoint de usuário o mesmo que o navegador utiliza para acessar e redenrizar os cursos.
5
5
  Author: PauloCesar-dev404
6
6
  Author-email: paulocesar0073dev404@gmail.com
@@ -19,7 +19,7 @@ Requires-Dist: pywidevine
19
19
  # udemy-userAPI
20
20
 
21
21
 
22
- ![Versão](https://img.shields.io/badge/version-0.2.8-orange)
22
+ ![Versão](https://img.shields.io/badge/version-0.2.9-orange)
23
23
  ![Licença](https://img.shields.io/badge/license-MIT-orange)
24
24
  [![Sponsor](https://img.shields.io/badge/💲Donate-yellow)](https://apoia.se/paulocesar-dev404)
25
25
  [![Sponsor](https://img.shields.io/badge/Documentation-green)](https://github.com/PauloCesar-dev404/udemy-userAPI/blob/main/docs/iniciando.md)
@@ -11,19 +11,19 @@ m3u8_analyzer/__init__.py,sha256=v7CiVqsCq2YH347C-QR1kHPJtXFFdru8qole3E9adCY,217
11
11
  m3u8_analyzer/__version__.py,sha256=YP3yT87ZKrU3eARUUdQ_pg4xAXLGfBXjH4ZgEoZSq1I,25
12
12
  m3u8_analyzer/exeptions.py,sha256=fK6bU3YxNSbfsPmCp4yudUvmwy_g6dj2KwIkH0dW4LI,3672
13
13
  udemy_userAPI/__init__.py,sha256=BPle89xE_CMTKKe_Lw6jioYLgpH-q_Lpho2S-n1PIUA,206
14
- udemy_userAPI/__version__.py,sha256=vx5RTMBCZ8TPFvdbHibhAEl83lKiEzlCu5oYTGRiPQg,405
15
- udemy_userAPI/api.py,sha256=dpwFtXewQmKwgG1IvzDFYZoEHNTwZbLIuv4WKgbqjOg,18817
14
+ udemy_userAPI/__version__.py,sha256=ddg6Jc4hdL3Ucjndnb_uXJAIG6W_GhKfswf9LQ5rImo,405
15
+ udemy_userAPI/api.py,sha256=M061zpgrwigktMeb9JINmblqsojkSdTwdzeRwimlGlw,18783
16
16
  udemy_userAPI/authenticate.py,sha256=84frcOMfOzfCBfXDtoTa3POqkwWwuqgJ6h4ROF0TVAM,13850
17
- udemy_userAPI/bultins.py,sha256=_-CM8Y-EuOEyg3kbNI2LKUONdCn2d1El1AmoNqFo0EU,12426
17
+ udemy_userAPI/bultins.py,sha256=Z3Jw-e4HUE-5LGfOtAkoyHFGfdyOSXpAinsaNPa41Bc,12597
18
18
  udemy_userAPI/exeptions.py,sha256=nuZoAt4i-ctrW8zx9LZtejrngpFXDHOVE5cEXM4RtrY,508
19
19
  udemy_userAPI/sections.py,sha256=zPyDhvTIQCL0nbf7OJZG28Kax_iooILQ_hywUwvHoL8,4043
20
20
  udemy_userAPI/udemy.py,sha256=KMWMmid0zC9pUCULjLSAOK0P7yvCOtdShXpT6Q-fhro,2127
21
21
  udemy_userAPI/.cache/.udemy_userAPI,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  udemy_userAPI/mpd_analyzer/__init__.py,sha256=i3JVWyvcFLaj5kPmx8c1PgjsLht7OUIQQClD4yqYbo8,102
23
23
  udemy_userAPI/mpd_analyzer/bin.wvd,sha256=1rAJdCc120hQlX9qe5KUS628eY2ZHYxQSmyhGNefSzo,2956
24
- udemy_userAPI/mpd_analyzer/mpd_parser.py,sha256=_vw1feJXDjw5fQLOmA5-H3UklX_30Pbl__HtDUqvp3c,17283
25
- udemy_userAPI-0.2.8.dist-info/LICENSE,sha256=l4jdKYt8gSdDFOGr09vCKnMn_Im55XIcQKqTDEtFfNs,1095
26
- udemy_userAPI-0.2.8.dist-info/METADATA,sha256=rOApAgyamEEbPBHpoE7JDYl1RjPryfsCr3e3aPl_LCY,1417
27
- udemy_userAPI-0.2.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
28
- udemy_userAPI-0.2.8.dist-info/top_level.txt,sha256=ijTINaSDRKhdahY_X7dmSRFTxBIwQErWv9ATCG55mog,14
29
- udemy_userAPI-0.2.8.dist-info/RECORD,,
24
+ udemy_userAPI/mpd_analyzer/mpd_parser.py,sha256=PgUkHc5x8FTuXFCuYkWPZr9TaO_nsKalb02EFYl_zeA,8926
25
+ udemy_userAPI-0.2.9.dist-info/LICENSE,sha256=l4jdKYt8gSdDFOGr09vCKnMn_Im55XIcQKqTDEtFfNs,1095
26
+ udemy_userAPI-0.2.9.dist-info/METADATA,sha256=nFDTysEImMWRp_2-d9VreIuk7TAkWMkAa6p35KHjJy4,1417
27
+ udemy_userAPI-0.2.9.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
28
+ udemy_userAPI-0.2.9.dist-info/top_level.txt,sha256=ijTINaSDRKhdahY_X7dmSRFTxBIwQErWv9ATCG55mog,14
29
+ udemy_userAPI-0.2.9.dist-info/RECORD,,