udemy-userAPI 0.2.8__py3-none-any.whl → 0.2.9__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,,