gamdl 3.2__tar.gz → 3.4__tar.gz

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.
Files changed (50) hide show
  1. {gamdl-3.2 → gamdl-3.4}/PKG-INFO +1 -1
  2. gamdl-3.4/gamdl/__init__.py +1 -0
  3. {gamdl-3.2 → gamdl-3.4}/gamdl/cli/cli.py +6 -14
  4. {gamdl-3.2 → gamdl-3.4}/gamdl/cli/database.py +12 -2
  5. {gamdl-3.2 → gamdl-3.4}/gamdl/cli/utils.py +25 -2
  6. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/downloader.py +3 -2
  7. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/types.py +2 -2
  8. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/base.py +8 -4
  9. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/exceptions.py +3 -1
  10. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/interface.py +0 -2
  11. {gamdl-3.2 → gamdl-3.4}/gamdl/utils.py +14 -5
  12. {gamdl-3.2 → gamdl-3.4}/gamdl.egg-info/PKG-INFO +1 -1
  13. {gamdl-3.2 → gamdl-3.4}/pyproject.toml +1 -1
  14. gamdl-3.2/gamdl/__init__.py +0 -1
  15. {gamdl-3.2 → gamdl-3.4}/LICENSE +0 -0
  16. {gamdl-3.2 → gamdl-3.4}/README.md +0 -0
  17. {gamdl-3.2 → gamdl-3.4}/gamdl/__main__.py +0 -0
  18. {gamdl-3.2 → gamdl-3.4}/gamdl/api/__init__.py +0 -0
  19. {gamdl-3.2 → gamdl-3.4}/gamdl/api/apple_music.py +0 -0
  20. {gamdl-3.2 → gamdl-3.4}/gamdl/api/constants.py +0 -0
  21. {gamdl-3.2 → gamdl-3.4}/gamdl/api/exceptions.py +0 -0
  22. {gamdl-3.2 → gamdl-3.4}/gamdl/api/itunes.py +0 -0
  23. {gamdl-3.2 → gamdl-3.4}/gamdl/cli/__init__.py +0 -0
  24. {gamdl-3.2 → gamdl-3.4}/gamdl/cli/cli_config.py +0 -0
  25. {gamdl-3.2 → gamdl-3.4}/gamdl/cli/config_file.py +0 -0
  26. {gamdl-3.2 → gamdl-3.4}/gamdl/cli/constants.py +0 -0
  27. {gamdl-3.2 → gamdl-3.4}/gamdl/cli/interactive_prompts.py +0 -0
  28. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/__init__.py +0 -0
  29. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/amdecrypt.py +0 -0
  30. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/base.py +0 -0
  31. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/constants.py +0 -0
  32. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/enums.py +0 -0
  33. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/exceptions.py +0 -0
  34. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/music_video.py +0 -0
  35. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/song.py +0 -0
  36. {gamdl-3.2 → gamdl-3.4}/gamdl/downloader/uploaded_video.py +0 -0
  37. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/__init__.py +0 -0
  38. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/constants.py +0 -0
  39. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/enums.py +0 -0
  40. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/music_video.py +0 -0
  41. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/song.py +0 -0
  42. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/types.py +0 -0
  43. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/uploaded_video.py +0 -0
  44. {gamdl-3.2 → gamdl-3.4}/gamdl/interface/wvd.py +0 -0
  45. {gamdl-3.2 → gamdl-3.4}/gamdl.egg-info/SOURCES.txt +0 -0
  46. {gamdl-3.2 → gamdl-3.4}/gamdl.egg-info/dependency_links.txt +0 -0
  47. {gamdl-3.2 → gamdl-3.4}/gamdl.egg-info/entry_points.txt +0 -0
  48. {gamdl-3.2 → gamdl-3.4}/gamdl.egg-info/requires.txt +0 -0
  49. {gamdl-3.2 → gamdl-3.4}/gamdl.egg-info/top_level.txt +0 -0
  50. {gamdl-3.2 → gamdl-3.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gamdl
3
- Version: 3.2
3
+ Version: 3.4
4
4
  Summary: A command-line app for downloading Apple Music songs, music videos and post videos.
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/glomatico/gamdl
@@ -0,0 +1 @@
1
+ __version__ = "3.4"
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- import logging
3
2
  from functools import wraps
4
3
  from pathlib import Path
5
4
 
@@ -38,7 +37,7 @@ from .cli_config import CliConfig
38
37
  from .config_file import ConfigFile
39
38
  from .database import Database
40
39
  from .interactive_prompts import InteractivePrompts
41
- from .utils import custom_structlog_formatter, prompt_path
40
+ from .utils import CustomOutputWriter, custom_structlog_formatter, prompt_path
42
41
 
43
42
  logger = structlog.get_logger(__name__)
44
43
 
@@ -60,18 +59,10 @@ def make_sync(func):
60
59
  async def main(config: CliConfig):
61
60
  colorama.just_fix_windows_console()
62
61
 
63
- root_logger = logging.getLogger(__name__.split(".")[0])
64
- root_logger.setLevel(config.log_level)
65
- root_logger.propagate = False
66
-
67
- stream_handler = logging.StreamHandler()
68
- stream_handler.setFormatter(logging.Formatter("%(message)s"))
69
- root_logger.addHandler(stream_handler)
62
+ log_output = CustomOutputWriter()
70
63
 
71
64
  if config.log_file:
72
- file_handler = logging.FileHandler(config.log_file, encoding="utf-8")
73
- file_handler.setFormatter(logging.Formatter("%(message)s"))
74
- root_logger.addHandler(file_handler)
65
+ log_output.add_file(config.log_file)
75
66
 
76
67
  structlog.configure(
77
68
  processors=[
@@ -79,7 +70,8 @@ async def main(config: CliConfig):
79
70
  structlog.processors.ExceptionPrettyPrinter(),
80
71
  custom_structlog_formatter,
81
72
  ],
82
- logger_factory=structlog.stdlib.LoggerFactory(),
73
+ logger_factory=structlog.PrintLoggerFactory(file=log_output),
74
+ wrapper_class=structlog.make_filtering_bound_logger(config.log_level),
83
75
  )
84
76
 
85
77
  logger.info(f"Starting Gamdl {__version__}")
@@ -127,7 +119,7 @@ async def main(config: CliConfig):
127
119
  )
128
120
 
129
121
  if config.database_path:
130
- database = Database(config.database_path)
122
+ database = Database(config.database_path, config.overwrite)
131
123
  flat_filter = database.flat_filter
132
124
  else:
133
125
  database = None
@@ -3,7 +3,13 @@ from pathlib import Path
3
3
 
4
4
 
5
5
  class Database:
6
- def __init__(self, path: Path):
6
+ def __init__(
7
+ self,
8
+ path: Path,
9
+ overwrite: bool,
10
+ ):
11
+ self.overwrite = overwrite
12
+
7
13
  self.connection = sqlite3.connect(path)
8
14
  self.cursor = self.connection.cursor()
9
15
  self._create_tables()
@@ -45,4 +51,8 @@ class Database:
45
51
  if not result:
46
52
  return None
47
53
 
48
- return result if Path(result).exists() else None
54
+ return (
55
+ "Registered in database"
56
+ if Path(result).exists() and not self.overwrite
57
+ else None
58
+ )
@@ -1,3 +1,5 @@
1
+ import atexit
2
+ import sys
1
3
  from datetime import datetime
2
4
  from enum import Enum
3
5
  from pathlib import Path
@@ -39,12 +41,33 @@ class Csv(click.ParamType):
39
41
  return result
40
42
 
41
43
 
44
+ class CustomOutputWriter:
45
+ def __init__(
46
+ self,
47
+ streams: list[Any] = [sys.stdout],
48
+ ):
49
+ self.streams = streams
50
+
51
+ def add_file(self, path: str):
52
+ file_stream = open(path, "a")
53
+ atexit.register(file_stream.close)
54
+ self.streams.append(file_stream)
55
+
56
+ def write(self, message: str):
57
+ for stream in self.streams:
58
+ stream.write(message)
59
+
60
+ def flush(self):
61
+ for stream in self.streams:
62
+ stream.flush()
63
+
64
+
42
65
  def custom_structlog_formatter(
43
66
  logger: Any,
44
67
  name: str,
45
68
  event_dict: dict[str, Any],
46
69
  ) -> str:
47
- level = event_dict.get("level", "INFO").upper()
70
+ level = event_dict.pop("level", "INFO").upper()
48
71
  timestamp = datetime.now().strftime("%H:%M:%S")
49
72
 
50
73
  level_colors = {
@@ -63,7 +86,7 @@ def custom_structlog_formatter(
63
86
  prefix += click.style(f" [{action}]", dim=True)
64
87
 
65
88
  if level in {"INFO", "WARNING", "ERROR", "CRITICAL"}:
66
- message = event_dict.get("event", "")
89
+ message = event_dict.pop("event", "")
67
90
  return f"{prefix} {message}"
68
91
  else:
69
92
  return f"{prefix} {event_dict}"
@@ -88,7 +88,8 @@ class AppleMusicDownloader:
88
88
  await self._download(item)
89
89
  await self._final_processing(item)
90
90
  finally:
91
- self._cleanup_temp(item.uuid_)
91
+ if not self.skip_cleanup:
92
+ self._cleanup_temp(item.uuid_)
92
93
 
93
94
  def _update_playlist_file(
94
95
  self,
@@ -263,6 +264,6 @@ class AppleMusicDownloader:
263
264
  log = logger.bind(action="cleanup_temp", folder_tag=folder_tag)
264
265
 
265
266
  temp_path = Path(self.base.temp_path) / TEMP_PATH_TEMPLATE.format(folder_tag)
266
- if temp_path.exists() and temp_path.is_dir() and not self.skip_cleanup:
267
+ if temp_path.exists() and temp_path.is_dir():
267
268
  shutil.rmtree(temp_path, ignore_errors=True)
268
269
  log.debug("success")
@@ -1,5 +1,5 @@
1
1
  import uuid
2
- from dataclasses import dataclass
2
+ from dataclasses import dataclass, field
3
3
 
4
4
  from ..interface.types import AppleMusicMedia
5
5
 
@@ -7,7 +7,7 @@ from ..interface.types import AppleMusicMedia
7
7
  @dataclass
8
8
  class DownloadItem:
9
9
  media: AppleMusicMedia
10
- uuid_: str = uuid.uuid4().hex[:8]
10
+ uuid_: str = field(default_factory=lambda: uuid.uuid4().hex[:8])
11
11
  staged_path: str = None
12
12
  final_path: str = None
13
13
  playlist_file_path: str = None
@@ -223,12 +223,16 @@ class AppleMusicBaseInterface:
223
223
 
224
224
  def _get_raw_cover_url(self, cover_url_template: str) -> str:
225
225
  return re.sub(
226
- r"image/thumb/",
226
+ r"/\{w\}x\{h\}bb\.jpg",
227
227
  "",
228
228
  re.sub(
229
- r"is1-ssl",
230
- "a1",
231
- cover_url_template,
229
+ r"image/thumb/",
230
+ "",
231
+ re.sub(
232
+ r"is1-ssl",
233
+ "a1",
234
+ cover_url_template,
235
+ ),
232
236
  ),
233
237
  )
234
238
 
@@ -46,6 +46,8 @@ class GamdlInterfaceArtistMediaTypeError(GamdlInterfaceError):
46
46
 
47
47
  class GamdlInterfaceFlatFilterExcludedError(GamdlInterfaceError):
48
48
  def __init__(self, media_id: str, result: Any):
49
- super().__init__(f"Media excluded by flat filter: {media_id}")
49
+ super().__init__(
50
+ f"Media excluded by flat filter (media ID: {media_id}): {result}"
51
+ )
50
52
 
51
53
  self.result = result
@@ -281,7 +281,6 @@ class AppleMusicInterface:
281
281
  self._get_song_media(
282
282
  media_id=track["id"],
283
283
  index=index,
284
- total=base_media.media_metadata["attributes"]["trackCount"],
285
284
  media_metadata=track,
286
285
  playlist_metadata=base_media.media_metadata,
287
286
  )
@@ -289,7 +288,6 @@ class AppleMusicInterface:
289
288
  else self._get_music_video_media(
290
289
  media_id=track["id"],
291
290
  index=index,
292
- total=base_media.media_metadata["attributes"]["trackCount"],
293
291
  media_metadata=track,
294
292
  playlist_metadata=base_media.media_metadata,
295
293
  )
@@ -1,14 +1,13 @@
1
1
  import asyncio
2
2
  import string
3
- import subprocess
4
3
  import typing
5
4
 
6
5
 
7
6
  async def async_subprocess(*args: str, silent: bool = False) -> None:
8
7
  if silent:
9
8
  additional_args = {
10
- "stdout": subprocess.DEVNULL,
11
- "stderr": subprocess.DEVNULL,
9
+ "stdout": asyncio.subprocess.PIPE,
10
+ "stderr": asyncio.subprocess.PIPE,
12
11
  }
13
12
  else:
14
13
  additional_args = {}
@@ -17,10 +16,20 @@ async def async_subprocess(*args: str, silent: bool = False) -> None:
17
16
  *args,
18
17
  **additional_args,
19
18
  )
20
- await proc.communicate()
19
+
20
+ stdout, stderr = await proc.communicate()
21
21
 
22
22
  if proc.returncode != 0:
23
- raise Exception(f'"{args[0]}" exited with code {proc.returncode}')
23
+ msg = (
24
+ f"Exited with code {proc.returncode}: {' '.join(str(arg) for arg in args)}"
25
+ )
26
+
27
+ if stdout:
28
+ msg += f"\nstdout:\n{stdout.decode()}"
29
+ if stderr:
30
+ msg += f"\nstderr:\n{stderr.decode()}"
31
+
32
+ raise Exception(msg)
24
33
 
25
34
 
26
35
  async def safe_gather(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gamdl
3
- Version: 3.2
3
+ Version: 3.4
4
4
  Summary: A command-line app for downloading Apple Music songs, music videos and post videos.
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/glomatico/gamdl
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gamdl"
3
- version = "3.2"
3
+ version = "3.4"
4
4
  description = "A command-line app for downloading Apple Music songs, music videos and post videos."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1 +0,0 @@
1
- __version__ = "3.2"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes