Поддержка LRC

This commit is contained in:
Lev Plyusnin 2024-11-21 18:30:05 +07:00
parent 7bdbee96fe
commit 6dc2d0dc56
No known key found for this signature in database
GPG key ID: 21C6C2C9C0A4460D
4 changed files with 67 additions and 16 deletions

View file

@ -54,9 +54,9 @@ https://yandex-music.readthedocs.io/en/main/token.html
yandex-music-downloader --token "<Токен>" --quality 2 --url "https://music.yandex.ru/artist/208167"
```
### Скачать альбом [Nevermind](https://music.yandex.ru/album/294912) в высоком качестве, загружая тексты песен
### Скачать альбом [Nevermind](https://music.yandex.ru/album/294912) в высоком качестве, загружая тексты песен в формате LRC (с временными метками)
```
yandex-music-downloader --token "<Токен>" --quality 1 --add-lyrics --url "https://music.yandex.ru/album/294912"
yandex-music-downloader --token "<Токен>" --quality 1 --lyrics-format lrc --url "https://music.yandex.ru/album/294912"
```
### Скачать трек [Seven Nation Army](https://music.yandex.ru/album/11644078/track/6705392)
@ -67,7 +67,8 @@ yandex-music-downloader --token "<Токен>" --url "https://music.yandex.ru/al
## Использование
```
usage: yandex-music-downloader [-h] [--quality <Качество>] [--skip-existing]
[--add-lyrics] [--embed-cover]
[--lyrics-format {none,text,lrc}]
[--embed-cover]
[--cover-resolution <Разрешение обложки>]
[--delay <Задержка>] [--stick-to-artist]
[--only-music]
@ -88,7 +89,8 @@ options:
2 - Лучшее (FLAC)
(по умолчанию: 0)
--skip-existing Пропускать уже загруженные треки
--add-lyrics Загружать тексты песен
--lyrics-format {none,text,lrc}
Формат текста песни (по умолчанию: none)
--embed-cover Встраивать обложку в аудиофайл
--cover-resolution <Разрешение обложки>
Разрешение обложки (в пикселях). Передайте "original" для загрузки в оригинальном (наилучшем) разрешении (по умолчанию: 400)

View file

@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "yandex-music-downloader"
version = "3.2.0b4"
version = "3.2.1b0"
description = "Загрузчик музыки с сервиса Яндекс.Музыка"
requires-python = ">=3.9"
readme = "README.md"

View file

@ -5,6 +5,7 @@ import logging
import re
import time
import typing
from argparse import ArgumentTypeError
from collections.abc import Generator, Iterable
from pathlib import Path
from typing import Optional, Union
@ -35,7 +36,7 @@ def quality_arg(astr: str) -> int:
aint = int(astr)
if 0 <= aint <= 2:
return aint
raise argparse.ArgumentTypeError("Значение должно быть в промежутке от 0 до 2")
raise ArgumentTypeError("Значение должно быть в промежутке от 0 до 2")
def compatibility_level_arg(astr: str) -> int:
@ -44,7 +45,7 @@ def compatibility_level_arg(astr: str) -> int:
max_val = core.MAX_COMPATIBILITY_LEVEL
if min_val <= aint <= max_val:
return aint
raise argparse.ArgumentTypeError(
raise ArgumentTypeError(
f"Значение должен быть в промежутке от {min_val} до {max_val}"
)
@ -55,6 +56,13 @@ def cover_resolution_arg(astr: str) -> int:
return int(astr)
def lyrics_format_arg(astr: str) -> core.LyricsFormat:
try:
return core.LyricsFormat(astr)
except ValueError:
raise ArgumentTypeError(f"Допустимые значения: {','.join(core.LyricsFormat)}")
def main():
parser = argparse.ArgumentParser(
description="Загрузчик музыки с сервиса Яндекс.Музыка",
@ -73,7 +81,14 @@ def main():
"--skip-existing", action="store_true", help="Пропускать уже загруженные треки"
)
common_group.add_argument(
"--add-lyrics", action="store_true", help="Загружать тексты песен"
"--lyrics-format",
type=lyrics_format_arg,
default=core.LyricsFormat.NONE,
help=show_default("Формат текста песни"),
choices=core.LyricsFormat,
)
common_group.add_argument(
"--add-lyrics", action="store_true", help=argparse.SUPPRESS
)
common_group.add_argument(
"--embed-cover", action="store_true", help="Встраивать обложку в аудиофайл"
@ -168,6 +183,12 @@ def main():
level=logging.DEBUG if args.debug else logging.ERROR,
)
if args.add_lyrics:
print(
"Аргумент --add-lyrics устарел и будет удален в будущем. Используйте --lyrics-format"
)
args.lyrics_format = core.LyricsFormat.TEXT
if args.url is not None:
parsed_url = urlparse(args.url)
path = parsed_url.path
@ -269,7 +290,7 @@ def main():
print(f"{format_info} Загружается {downloadable.path}")
core.download_track(
track_info=downloadable,
add_lyrics=args.add_lyrics,
lyrics_format=args.lyrics_format,
embed_cover=args.embed_cover,
cover_resolution=args.cover_resolution,
covers_cache=covers_cache,

View file

@ -2,9 +2,11 @@ import datetime as dt
import random
import re
import typing
from collections.abc import Iterable
from dataclasses import dataclass
from enum import Enum, StrEnum, auto
from pathlib import Path
from typing import Optional, Union
from typing import Optional, Type, TypeVar, Union
import mutagen
from mutagen.flac import FLAC, Picture
@ -39,6 +41,25 @@ MAX_COMPATIBILITY_LEVEL = 1
AUDIO_FILE_SUFFIXES = {".mp3", ".flac", ".m4a"}
_E = TypeVar("_E", bound=Enum)
class ParameterEnum(StrEnum):
@classmethod
def available_keys(cls) -> Iterable[str]:
return cls.__members__.keys()
@classmethod
def from_str(cls: Type[_E], arg: str) -> _E:
return cls[arg.lower()]
class LyricsFormat(StrEnum):
NONE = auto()
TEXT = auto()
LRC = auto()
@dataclass
class DownloadableTrack:
url: str
@ -201,7 +222,7 @@ def set_tags(
def download_track(
track_info: DownloadableTrack,
cover_resolution: int = DEFAULT_COVER_RESOLUTION,
add_lyrics: bool = False,
lyrics_format: LyricsFormat = LyricsFormat.NONE,
embed_cover: bool = False,
covers_cache: Optional[dict[int, bytes]] = None,
compatibility_level: int = 1,
@ -217,11 +238,18 @@ def download_track(
client.request.download(track_info.url, str(target_path))
lyrics = None
if add_lyrics and (lyrics_info := track.lyrics_info):
if lyrics_info.has_available_text_lyrics:
text_lyrics = None
if lyrics_format != LyricsFormat.NONE and (lyrics_info := track.lyrics_info):
if lyrics_format == LyricsFormat.LRC and lyrics_info.has_available_sync_lyrics:
if track_lyrics := track.get_lyrics(format="LRC"):
lrc_lyrics = track_lyrics.fetch_lyrics()
lrc_path = target_path.with_suffix(".lrc")
if not lrc_path.is_file():
with open(lrc_path, "w") as f:
f.write(lrc_lyrics)
elif lyrics_info.has_available_text_lyrics:
if track_lyrics := track.get_lyrics(format="TEXT"):
lyrics = track_lyrics.fetch_lyrics()
text_lyrics = track_lyrics.fetch_lyrics()
cover = None
if track.cover_uri is not None:
@ -242,7 +270,7 @@ def download_track(
if not cover_path.is_file():
track.download_cover(str(cover_path), cover_size)
set_tags(target_path, track, lyrics, cover, compatibility_level)
set_tags(target_path, track, text_lyrics, cover, compatibility_level)
def to_downloadable_track(