Typecheck with mypy, lint and format with ruff (#499)
* Lint and format with ruff, check types with mypy * Fix mypy checks * Update CI * Remove shebangs * Ignore EXE rules * Fix EOL * Fix mypy for 3.7 * Fix CI * Fix CI * Ensure filelock path exists * ci: various ci fixes (#500) --------- Co-authored-by: Arsenii es3n1n <me@es3n.in>
This commit is contained in:
parent
399ea88eab
commit
9fd4814e1f
35
.github/workflows/pypi.yml
vendored
35
.github/workflows/pypi.yml
vendored
|
|
@ -1,10 +1,45 @@
|
|||
name: pypi-publish
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install ffmpeg
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -yq --no-install-recommends ffmpeg
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -e .[dev]
|
||||
- name: Lint
|
||||
run: ruff check
|
||||
- name: Format check
|
||||
run: ruff format --check
|
||||
- name: Type check
|
||||
run: mypy
|
||||
- name: Test
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }}
|
||||
run: |
|
||||
pytest --exitfirst
|
||||
publish:
|
||||
needs: test
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,3 +9,4 @@ __pycache__/
|
|||
.env
|
||||
.coverage*
|
||||
.idea
|
||||
.python-version
|
||||
3
mypy.ini
Normal file
3
mypy.ini
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[mypy]
|
||||
packages = scdl, tests
|
||||
check_untyped_defs = true
|
||||
|
|
@ -1,2 +1,6 @@
|
|||
pytest
|
||||
music-tag
|
||||
ruff
|
||||
mypy
|
||||
types-requests
|
||||
types-tqdm
|
||||
13
ruff.toml
Normal file
13
ruff.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
target-version = "py37"
|
||||
line-length = 100
|
||||
|
||||
[lint]
|
||||
select = ["ALL"]
|
||||
ignore = [
|
||||
"C90", "D",
|
||||
"S", "BLE", "FBT", "A", "EM", "FA", "G", "SLF", "PTH",
|
||||
"PLR", "TRY",
|
||||
"PLW2901", "ANN204",
|
||||
"COM812", "ISC001",
|
||||
"EXE"
|
||||
]
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
"""Python Soundcloud Music Downloader."""
|
||||
|
||||
__version__ = "v2.10.0"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
from base64 import b64encode
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, Type, TypeVar, Union, Callable
|
||||
from types import MappingProxyType
|
||||
from functools import singledispatch
|
||||
from typing import Optional, Union
|
||||
|
||||
from mutagen import FileType, flac, oggopus, id3, wave, mp3, mp4
|
||||
from mutagen import FileType, flac, id3, mp3, mp4, oggopus, wave
|
||||
|
||||
|
||||
JPEG_MIME_TYPE: str = 'image/jpeg'
|
||||
JPEG_MIME_TYPE: str = "image/jpeg"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -26,6 +25,11 @@ class MetadataInfo:
|
|||
album_track_num: Optional[int]
|
||||
|
||||
|
||||
@singledispatch
|
||||
def assemble_metadata(file: FileType, meta: MetadataInfo) -> None: # noqa: ARG001
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def _get_flac_pic(jpeg_data: bytes) -> flac.Picture:
|
||||
pic = flac.Picture()
|
||||
pic.data = jpeg_data
|
||||
|
|
@ -39,119 +43,113 @@ def _get_apic(jpeg_data: bytes) -> id3.APIC:
|
|||
encoding=3,
|
||||
mime=JPEG_MIME_TYPE,
|
||||
type=3,
|
||||
desc='Cover',
|
||||
desc="Cover",
|
||||
data=jpeg_data,
|
||||
)
|
||||
|
||||
|
||||
def _assemble_common(file: FileType, meta: MetadataInfo) -> None:
|
||||
file['artist'] = meta.artist
|
||||
file['title'] = meta.title
|
||||
file["artist"] = meta.artist
|
||||
file["title"] = meta.title
|
||||
|
||||
if meta.genre:
|
||||
file['genre'] = meta.genre
|
||||
file["genre"] = meta.genre
|
||||
|
||||
if meta.link:
|
||||
file['website'] = meta.link
|
||||
file["website"] = meta.link
|
||||
|
||||
if meta.date:
|
||||
file['date'] = meta.date
|
||||
file["date"] = meta.date
|
||||
|
||||
if meta.album_title:
|
||||
file['album'] = meta.album_title
|
||||
file["album"] = meta.album_title
|
||||
|
||||
if meta.album_author:
|
||||
file['albumartist'] = meta.album_author
|
||||
file["albumartist"] = meta.album_author
|
||||
|
||||
if meta.album_track_num is not None:
|
||||
file['tracknumber'] = str(meta.album_track_num)
|
||||
file["tracknumber"] = str(meta.album_track_num)
|
||||
|
||||
|
||||
def _assemble_flac(file: flac.FLAC, meta: MetadataInfo) -> None:
|
||||
@assemble_metadata.register(flac.FLAC)
|
||||
def _(file: flac.FLAC, meta: MetadataInfo) -> None:
|
||||
_assemble_common(file, meta)
|
||||
|
||||
if meta.description:
|
||||
file['description'] = meta.description
|
||||
file["description"] = meta.description
|
||||
|
||||
if meta.artwork_jpeg:
|
||||
file.add_picture(_get_flac_pic(meta.artwork_jpeg))
|
||||
|
||||
|
||||
def _assemble_opus(file: oggopus.OggOpus, meta: MetadataInfo) -> None:
|
||||
@assemble_metadata.register(oggopus.OggOpus)
|
||||
def _(file: oggopus.OggOpus, meta: MetadataInfo) -> None:
|
||||
_assemble_common(file, meta)
|
||||
|
||||
if meta.description:
|
||||
file['comment'] = meta.description
|
||||
file["comment"] = meta.description
|
||||
|
||||
if meta.artwork_jpeg:
|
||||
pic = _get_flac_pic(meta.artwork_jpeg).write()
|
||||
file['metadata_block_picture'] = b64encode(pic).decode()
|
||||
file["metadata_block_picture"] = b64encode(pic).decode()
|
||||
|
||||
|
||||
def _assemble_wav_or_mp3(file: Union[wave.WAVE, mp3.MP3], meta: MetadataInfo) -> None:
|
||||
file['TIT2'] = id3.TIT2(encoding=3, text=meta.title)
|
||||
file['TPE1'] = id3.TPE1(encoding=3, text=meta.artist)
|
||||
@assemble_metadata.register(mp3.MP3)
|
||||
@assemble_metadata.register(wave.WAVE)
|
||||
def _(file: Union[wave.WAVE, mp3.MP3], meta: MetadataInfo) -> None:
|
||||
file["TIT2"] = id3.TIT2(encoding=3, text=meta.title)
|
||||
file["TPE1"] = id3.TPE1(encoding=3, text=meta.artist)
|
||||
|
||||
if meta.description:
|
||||
file['COMM'] = id3.COMM(encoding=3, lang='ENG', text=meta.description)
|
||||
file["COMM"] = id3.COMM(encoding=3, lang="ENG", text=meta.description)
|
||||
|
||||
if meta.genre:
|
||||
file['TCON'] = id3.TCON(encoding=3, text=meta.genre)
|
||||
file["TCON"] = id3.TCON(encoding=3, text=meta.genre)
|
||||
|
||||
if meta.link:
|
||||
file['WOAS'] = id3.WOAS(url=meta.link)
|
||||
file["WOAS"] = id3.WOAS(url=meta.link)
|
||||
|
||||
if meta.date:
|
||||
file['TDAT'] = id3.TDAT(encoding=3, text=meta.date)
|
||||
file["TDAT"] = id3.TDAT(encoding=3, text=meta.date)
|
||||
|
||||
if meta.album_title:
|
||||
file['TALB'] = id3.TALB(encoding=3, text=meta.album_title)
|
||||
file["TALB"] = id3.TALB(encoding=3, text=meta.album_title)
|
||||
|
||||
if meta.album_author:
|
||||
file['TPE2'] = id3.TPE2(encoding=3, text=meta.album_author)
|
||||
file["TPE2"] = id3.TPE2(encoding=3, text=meta.album_author)
|
||||
|
||||
if meta.album_track_num is not None:
|
||||
file['TRCK'] = id3.TRCK(encoding=3, text=str(meta.album_track_num))
|
||||
file["TRCK"] = id3.TRCK(encoding=3, text=str(meta.album_track_num))
|
||||
|
||||
if meta.artwork_jpeg:
|
||||
file['APIC'] = _get_apic(meta.artwork_jpeg)
|
||||
file["APIC"] = _get_apic(meta.artwork_jpeg)
|
||||
|
||||
|
||||
def _assemble_mp4(file: mp4.MP4, meta: MetadataInfo) -> None:
|
||||
file['\251ART'] = meta.artist
|
||||
file['\251nam'] = meta.title
|
||||
@assemble_metadata.register(mp4.MP4)
|
||||
def _(file: mp4.MP4, meta: MetadataInfo) -> None:
|
||||
file["\251ART"] = meta.artist
|
||||
file["\251nam"] = meta.title
|
||||
|
||||
if meta.genre:
|
||||
file['\251gen'] = meta.genre
|
||||
file["\251gen"] = meta.genre
|
||||
|
||||
if meta.link:
|
||||
file['\251cmt'] = meta.link
|
||||
file["\251cmt"] = meta.link
|
||||
|
||||
if meta.date:
|
||||
file['\251day'] = meta.date
|
||||
file["\251day"] = meta.date
|
||||
|
||||
if meta.album_title:
|
||||
file['\251alb'] = meta.album_title
|
||||
file["\251alb"] = meta.album_title
|
||||
|
||||
if meta.album_author:
|
||||
file['aART'] = meta.album_author
|
||||
file["aART"] = meta.album_author
|
||||
|
||||
if meta.album_track_num is not None:
|
||||
file['trkn'] = str(meta.album_track_num)
|
||||
file["trkn"] = str(meta.album_track_num)
|
||||
|
||||
if meta.description:
|
||||
file['desc'] = meta.description
|
||||
file["desc"] = meta.description
|
||||
|
||||
if meta.artwork_jpeg:
|
||||
file['covr'] = [mp4.MP4Cover(meta.artwork_jpeg)]
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
METADATA_ASSEMBLERS: MappingProxyType[Type[T], Callable[[T, MetadataInfo], None]] = MappingProxyType({
|
||||
flac.FLAC: _assemble_flac,
|
||||
oggopus.OggOpus: _assemble_opus,
|
||||
wave.WAVE: _assemble_wav_or_mp3,
|
||||
mp3.MP3: _assemble_wav_or_mp3,
|
||||
mp4.MP4: _assemble_mp4,
|
||||
})
|
||||
|
||||
file["covr"] = [mp4.MP4Cover(meta.artwork_jpeg)]
|
||||
|
|
|
|||
911
scdl/scdl.py
911
scdl/scdl.py
File diff suppressed because it is too large
Load Diff
|
|
@ -1,38 +1,38 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Copied from
|
||||
"""Copied from
|
||||
https://github.com/davidfischer-ch/pytoolbox/blob/master/pytoolbox/logging.py
|
||||
"""
|
||||
|
||||
import email.message
|
||||
import logging
|
||||
import re
|
||||
from types import MappingProxyType
|
||||
from typing import Dict, Optional
|
||||
|
||||
from termcolor import colored
|
||||
|
||||
__all__ = ('ColorizeFilter', )
|
||||
__all__ = ("ColorizeFilter",)
|
||||
|
||||
|
||||
class ColorizeFilter(logging.Filter):
|
||||
COLOR_BY_LEVEL = MappingProxyType(
|
||||
{
|
||||
logging.DEBUG: "blue",
|
||||
logging.WARNING: "yellow",
|
||||
logging.ERROR: "red",
|
||||
logging.INFO: "white",
|
||||
},
|
||||
)
|
||||
|
||||
color_by_level = {
|
||||
logging.DEBUG: 'blue',
|
||||
logging.WARNING: 'yellow',
|
||||
logging.ERROR: 'red',
|
||||
logging.INFO: 'white'
|
||||
}
|
||||
|
||||
def filter(self, record):
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
record.raw_msg = record.msg
|
||||
color = self.color_by_level.get(record.levelno)
|
||||
color = self.COLOR_BY_LEVEL.get(record.levelno)
|
||||
if color:
|
||||
record.msg = colored(record.msg, color)
|
||||
record.msg = colored(record.msg, color) # type: ignore[arg-type]
|
||||
return True
|
||||
|
||||
|
||||
def size_in_bytes(insize):
|
||||
"""
|
||||
Returns the size in bytes from strings such as '5 mb' into 5242880.
|
||||
def size_in_bytes(insize: str) -> int:
|
||||
"""Return the size in bytes from strings such as '5 mb' into 5242880.
|
||||
|
||||
>>> size_in_bytes('1m')
|
||||
1048576
|
||||
|
|
@ -49,20 +49,20 @@ def size_in_bytes(insize):
|
|||
raise ValueError('no string specified')
|
||||
ValueError: no string specified
|
||||
"""
|
||||
if insize is None or insize.strip() == '':
|
||||
raise ValueError('no string specified')
|
||||
if insize is None or insize.strip() == "":
|
||||
raise ValueError("no string specified")
|
||||
|
||||
units = {
|
||||
'k': 1024,
|
||||
'm': 1024 ** 2,
|
||||
'g': 1024 ** 3,
|
||||
't': 1024 ** 4,
|
||||
'p': 1024 ** 5,
|
||||
"k": 1024,
|
||||
"m": 1024**2,
|
||||
"g": 1024**3,
|
||||
"t": 1024**4,
|
||||
"p": 1024**5,
|
||||
}
|
||||
match = re.search(r'^\s*([0-9\.]+)\s*([kmgtp])?', insize, re.I)
|
||||
match = re.search(r"^\s*([0-9\.]+)\s*([kmgtp])?", insize, re.IGNORECASE)
|
||||
|
||||
if match is None:
|
||||
raise ValueError('match not found')
|
||||
raise ValueError("match not found")
|
||||
|
||||
size, unit = match.groups()
|
||||
|
||||
|
|
@ -75,7 +75,9 @@ def size_in_bytes(insize):
|
|||
return int(size)
|
||||
|
||||
|
||||
def parse_header(content_disposition):
|
||||
def parse_header(content_disposition: Optional[str]) -> Dict[str, str]:
|
||||
if not content_disposition:
|
||||
return {}
|
||||
message = email.message.Message()
|
||||
message['content-type'] = content_disposition
|
||||
return dict(message.get_params())
|
||||
message["content-type"] = content_disposition
|
||||
return dict(message.get_params({}))
|
||||
|
|
|
|||
19
setup.py
19
setup.py
|
|
@ -1,6 +1,3 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from os import path
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
|
@ -28,10 +25,22 @@ setup(
|
|||
"requests",
|
||||
"tqdm",
|
||||
"pathvalidate",
|
||||
"soundcloud-v2>=1.3.10",
|
||||
"soundcloud-v2>=1.5.2",
|
||||
"filelock>=3.0.0",
|
||||
"typing_extensions; python_version < '3.11'",
|
||||
],
|
||||
extras_require={"test": ["pytest", "pytest-cov", "pytest-dotenv", "music-tag"]},
|
||||
extras_require={
|
||||
"dev": [
|
||||
"pytest",
|
||||
"pytest-cov",
|
||||
"pytest-dotenv",
|
||||
"music-tag",
|
||||
"ruff",
|
||||
"mypy",
|
||||
"types-requests",
|
||||
"types-tqdm",
|
||||
],
|
||||
},
|
||||
url="https://github.com/flyingrub/scdl",
|
||||
classifiers=[
|
||||
"Programming Language :: Python",
|
||||
|
|
|
|||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
|
|
@ -5,8 +5,10 @@ from tests.utils import assert_not_track, assert_track, call_scdl_with_auth
|
|||
|
||||
|
||||
def assert_track_playlist_1(
|
||||
tmp_path: Path, playlist_folder: str = "test playlist", check_metadata: bool = True
|
||||
):
|
||||
tmp_path: Path,
|
||||
playlist_folder: str = "test playlist",
|
||||
check_metadata: bool = True,
|
||||
) -> None:
|
||||
expected_name = "1_testing - test track.mp3"
|
||||
assert_track(
|
||||
tmp_path / playlist_folder,
|
||||
|
|
@ -19,8 +21,10 @@ def assert_track_playlist_1(
|
|||
|
||||
|
||||
def assert_track_playlist_2(
|
||||
tmp_path: Path, playlist_folder: str = "test playlist", check_metadata: bool = True
|
||||
):
|
||||
tmp_path: Path,
|
||||
playlist_folder: str = "test playlist",
|
||||
check_metadata: bool = True,
|
||||
) -> None:
|
||||
expected_name = "2_test track 2.mp3"
|
||||
assert_track(
|
||||
tmp_path / playlist_folder,
|
||||
|
|
@ -33,7 +37,7 @@ def assert_track_playlist_2(
|
|||
)
|
||||
|
||||
|
||||
def test_playlist(tmp_path: Path):
|
||||
def test_playlist(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -47,7 +51,7 @@ def test_playlist(tmp_path: Path):
|
|||
assert_track_playlist_2(tmp_path)
|
||||
|
||||
|
||||
def test_n(tmp_path: Path):
|
||||
def test_n(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -63,7 +67,7 @@ def test_n(tmp_path: Path):
|
|||
assert_not_track(tmp_path / "test playlist", "2_testing - test track.mp3")
|
||||
|
||||
|
||||
def test_offset(tmp_path: Path):
|
||||
def test_offset(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -82,7 +86,7 @@ def test_offset(tmp_path: Path):
|
|||
assert_track_playlist_2(tmp_path)
|
||||
|
||||
|
||||
def test_no_playlist_folder(tmp_path: Path):
|
||||
def test_no_playlist_folder(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -99,7 +103,7 @@ def test_no_playlist_folder(tmp_path: Path):
|
|||
assert_not_track(tmp_path / "test playlist", "2_test track 2.mp3")
|
||||
|
||||
|
||||
def test_no_strict_playlist(tmp_path: Path):
|
||||
def test_no_strict_playlist(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -114,7 +118,7 @@ def test_no_strict_playlist(tmp_path: Path):
|
|||
assert_not_track(tmp_path / "test playlist", "2_test track 2.mp3")
|
||||
|
||||
|
||||
def test_strict_playlist(tmp_path: Path):
|
||||
def test_strict_playlist(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -130,7 +134,7 @@ def test_strict_playlist(tmp_path: Path):
|
|||
assert_not_track(tmp_path / "test playlist", "2_test track 2.mp3")
|
||||
|
||||
|
||||
def test_sync(tmp_path: Path):
|
||||
def test_sync(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
os.makedirs("test playlist")
|
||||
r = call_scdl_with_auth(
|
||||
|
|
@ -159,8 +163,6 @@ def test_sync(tmp_path: Path):
|
|||
"archive.txt",
|
||||
)
|
||||
assert r.returncode == 0
|
||||
assert_not_track(
|
||||
tmp_path / "test playlist", "Wan Bushi - Eurodance Vibes (part 1+2+3).mp3"
|
||||
)
|
||||
with open("archive.txt", "r") as f:
|
||||
assert_not_track(tmp_path / "test playlist", "Wan Bushi - Eurodance Vibes (part 1+2+3).mp3")
|
||||
with open("archive.txt") as f:
|
||||
assert f.read().split() == ["1855267053", "1855318536"]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import math
|
||||
import os
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.utils import assert_not_track, assert_track, call_scdl_with_auth
|
||||
|
||||
|
||||
def test_original_download(tmp_path: Path):
|
||||
def test_original_download(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -17,7 +19,7 @@ def test_original_download(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.wav", "copy", "saves", None)
|
||||
|
||||
|
||||
def test_original_to_stdout(tmp_path: Path):
|
||||
def test_original_to_stdout(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -27,12 +29,13 @@ def test_original_to_stdout(tmp_path: Path):
|
|||
encoding=None,
|
||||
)
|
||||
assert r.returncode == 0
|
||||
with open('track.wav', 'wb') as f:
|
||||
with open("track.wav", "wb") as f:
|
||||
assert isinstance(r.stdout, bytes)
|
||||
f.write(r.stdout)
|
||||
assert_track(tmp_path, "track.wav", "copy", "saves", None)
|
||||
|
||||
|
||||
def test_mp3_to_stdout(tmp_path: Path):
|
||||
def test_mp3_to_stdout(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -44,13 +47,14 @@ def test_mp3_to_stdout(tmp_path: Path):
|
|||
)
|
||||
assert r.returncode == 0
|
||||
|
||||
with open('track.mp3', 'wb') as f:
|
||||
with open("track.mp3", "wb") as f:
|
||||
assert isinstance(r.stdout, bytes)
|
||||
f.write(r.stdout)
|
||||
|
||||
assert_track(tmp_path, "track.mp3")
|
||||
|
||||
|
||||
def test_flac_to_stdout(tmp_path: Path):
|
||||
def test_flac_to_stdout(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -61,14 +65,15 @@ def test_flac_to_stdout(tmp_path: Path):
|
|||
encoding=None,
|
||||
)
|
||||
|
||||
with open('track.flac', 'wb') as f:
|
||||
with open("track.flac", "wb") as f:
|
||||
assert isinstance(r.stdout, bytes)
|
||||
f.write(r.stdout)
|
||||
|
||||
assert r.returncode == 0
|
||||
assert_track(tmp_path, "track.flac", "copy", "saves", None)
|
||||
|
||||
|
||||
def test_flac(tmp_path: Path):
|
||||
def test_flac(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -81,7 +86,7 @@ def test_flac(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.flac", "copy", "saves", None)
|
||||
|
||||
|
||||
def test_m4a(tmp_path: Path):
|
||||
def test_m4a(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -92,8 +97,8 @@ def test_m4a(tmp_path: Path):
|
|||
"--opus",
|
||||
)
|
||||
assert r.returncode == 0
|
||||
if (tmp_path / 'track.opus').exists():
|
||||
pytest.skip('No go+ subscription')
|
||||
if (tmp_path / "track.opus").exists():
|
||||
pytest.skip("No go+ subscription")
|
||||
assert_track(
|
||||
tmp_path,
|
||||
"track.m4a",
|
||||
|
|
@ -104,7 +109,7 @@ def test_m4a(tmp_path: Path):
|
|||
)
|
||||
|
||||
|
||||
def test_opus(tmp_path: Path):
|
||||
def test_opus(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -118,7 +123,7 @@ def test_opus(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.opus")
|
||||
|
||||
|
||||
def test_mp3(tmp_path: Path):
|
||||
def test_mp3(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -131,7 +136,7 @@ def test_mp3(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.mp3")
|
||||
|
||||
|
||||
def test_unlisted_track(tmp_path: Path):
|
||||
def test_unlisted_track(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -144,7 +149,7 @@ def test_unlisted_track(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.mp3", "test track 2")
|
||||
|
||||
|
||||
def test_original_art(tmp_path: Path):
|
||||
def test_original_art(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -158,7 +163,7 @@ def test_original_art(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.mp3", expected_artwork_len=3409)
|
||||
|
||||
|
||||
def test_original_name(tmp_path: Path):
|
||||
def test_original_name(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -171,7 +176,7 @@ def test_original_name(tmp_path: Path):
|
|||
assert_track(tmp_path, "original.wav", check_metadata=False)
|
||||
|
||||
|
||||
def test_original_metadata(tmp_path: Path):
|
||||
def test_original_metadata(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -184,7 +189,7 @@ def test_original_metadata(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.wav", "og title", "og artist", "og genre", 0)
|
||||
|
||||
|
||||
def test_force_metadata(tmp_path: Path):
|
||||
def test_force_metadata(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -207,7 +212,7 @@ def test_force_metadata(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.wav", "copy", "saves", None)
|
||||
|
||||
|
||||
def test_addtimestamp(tmp_path: Path):
|
||||
def test_addtimestamp(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -219,7 +224,7 @@ def test_addtimestamp(tmp_path: Path):
|
|||
assert_track(tmp_path, "1719169486_testing - test track.mp3", check_metadata=False)
|
||||
|
||||
|
||||
def test_addtofile(tmp_path: Path):
|
||||
def test_addtofile(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -231,7 +236,7 @@ def test_addtofile(tmp_path: Path):
|
|||
assert_track(tmp_path, "7x11x13-testing - test track 2.mp3", check_metadata=False)
|
||||
|
||||
|
||||
def test_extract_artist(tmp_path: Path):
|
||||
def test_extract_artist(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -245,7 +250,7 @@ def test_extract_artist(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.mp3", "test track", "testing")
|
||||
|
||||
|
||||
def test_maxsize(tmp_path: Path):
|
||||
def test_maxsize(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -254,10 +259,10 @@ def test_maxsize(tmp_path: Path):
|
|||
"--max-size=10kb",
|
||||
)
|
||||
assert r.returncode == 1
|
||||
assert "not within --min-size and --max-size bounds" in r.stderr
|
||||
assert "not within --min-size=0 and --max-size=10240" in r.stderr
|
||||
|
||||
|
||||
def test_minsize(tmp_path: Path):
|
||||
def test_minsize(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -266,10 +271,10 @@ def test_minsize(tmp_path: Path):
|
|||
"--min-size=1mb",
|
||||
)
|
||||
assert r.returncode == 1
|
||||
assert "not within --min-size and --max-size bounds" in r.stderr
|
||||
assert f"not within --min-size={1024**2} and --max-size={math.inf}" in r.stderr
|
||||
|
||||
|
||||
def test_only_original(tmp_path: Path):
|
||||
def test_only_original(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -280,7 +285,7 @@ def test_only_original(tmp_path: Path):
|
|||
assert "does not have original file available" in r.stderr
|
||||
|
||||
|
||||
def test_overwrite(tmp_path: Path):
|
||||
def test_overwrite(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -312,7 +317,7 @@ def test_overwrite(tmp_path: Path):
|
|||
assert r.returncode == 0
|
||||
|
||||
|
||||
def test_path(tmp_path: Path):
|
||||
def test_path(tmp_path: Path) -> None:
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
"https://soundcloud.com/one-thousand-and-one/test-track",
|
||||
|
|
@ -326,7 +331,7 @@ def test_path(tmp_path: Path):
|
|||
assert_track(tmp_path, "track.mp3", check_metadata=False)
|
||||
|
||||
|
||||
def test_remove(tmp_path: Path):
|
||||
def test_remove(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -350,7 +355,7 @@ def test_remove(tmp_path: Path):
|
|||
assert_not_track(tmp_path, "track.mp3")
|
||||
|
||||
|
||||
def test_download_archive(tmp_path: Path):
|
||||
def test_download_archive(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ from pathlib import Path
|
|||
from tests.utils import assert_track, call_scdl_with_auth
|
||||
|
||||
|
||||
def count_files(dir: Path):
|
||||
return len(list(dir.rglob("*")))
|
||||
def count_files(folder: Path) -> int:
|
||||
return len(list(folder.rglob("*")))
|
||||
|
||||
|
||||
def test_all(tmp_path: Path):
|
||||
def test_all(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -22,7 +22,7 @@ def test_all(tmp_path: Path):
|
|||
assert count_files(tmp_path) == 3
|
||||
|
||||
|
||||
def test_tracks(tmp_path: Path):
|
||||
def test_tracks(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -36,7 +36,7 @@ def test_tracks(tmp_path: Path):
|
|||
assert count_files(tmp_path) == 1
|
||||
|
||||
|
||||
def test_likes(tmp_path: Path):
|
||||
def test_likes(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -46,13 +46,11 @@ def test_likes(tmp_path: Path):
|
|||
"--name-format={title}",
|
||||
)
|
||||
assert r.returncode == 0
|
||||
assert_track(
|
||||
tmp_path, "Wan Bushi - Eurodance Vibes (part 1+2+3).mp3", check_metadata=False
|
||||
)
|
||||
assert_track(tmp_path, "Wan Bushi - Eurodance Vibes (part 1+2+3).mp3", check_metadata=False)
|
||||
assert count_files(tmp_path) == 1
|
||||
|
||||
|
||||
def test_commented(tmp_path: Path):
|
||||
def test_commented(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -62,13 +60,11 @@ def test_commented(tmp_path: Path):
|
|||
"--name-format={title}",
|
||||
)
|
||||
assert r.returncode == 0
|
||||
assert_track(
|
||||
tmp_path, "Wan Bushi - Eurodance Vibes (part 1+2+3).mp3", check_metadata=False
|
||||
)
|
||||
assert_track(tmp_path, "Wan Bushi - Eurodance Vibes (part 1+2+3).mp3", check_metadata=False)
|
||||
assert count_files(tmp_path) == 1
|
||||
|
||||
|
||||
def test_playlists(tmp_path: Path):
|
||||
def test_playlists(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
|
|
@ -80,16 +76,15 @@ def test_playlists(tmp_path: Path):
|
|||
assert count_files(tmp_path) == 3
|
||||
|
||||
|
||||
def test_reposts(tmp_path: Path):
|
||||
def test_reposts(tmp_path: Path) -> None:
|
||||
os.chdir(tmp_path)
|
||||
r = call_scdl_with_auth(
|
||||
"-l",
|
||||
"https://soundcloud.com/one-thousand-and-one",
|
||||
"-r",
|
||||
"--name-format={title}",
|
||||
"--onlymp3",
|
||||
)
|
||||
assert r.returncode == 0
|
||||
assert_track(
|
||||
tmp_path, "Wan Bushi - Eurodance Vibes (part 1+2+3).mp3", check_metadata=False
|
||||
)
|
||||
assert_track(tmp_path, "Wan Bushi - Eurodance Vibes (part 1+2+3).mp3", check_metadata=False)
|
||||
assert count_files(tmp_path) == 1
|
||||
|
|
|
|||
|
|
@ -3,22 +3,26 @@ import subprocess
|
|||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import music_tag
|
||||
import music_tag # type: ignore[import]
|
||||
from soundcloud import SoundCloud
|
||||
|
||||
client_id = SoundCloud().client_id
|
||||
|
||||
|
||||
def call_scdl_with_auth(*args, encoding: Optional[str] = 'utf-8') -> subprocess.CompletedProcess[str]:
|
||||
def call_scdl_with_auth(
|
||||
*args: str,
|
||||
encoding: Optional[str] = "utf-8",
|
||||
) -> subprocess.CompletedProcess:
|
||||
auth_token = os.getenv("AUTH_TOKEN")
|
||||
assert auth_token
|
||||
args = (
|
||||
["scdl"]
|
||||
+ list(args)
|
||||
+ [f"--auth-token={auth_token}", f"--client-id={client_id}"]
|
||||
args = ("scdl", *args, f"--auth-token={auth_token}", f"--client-id={client_id}")
|
||||
return subprocess.run(
|
||||
args,
|
||||
capture_output=True,
|
||||
encoding=encoding,
|
||||
errors="ignore" if encoding is not None else None,
|
||||
check=False,
|
||||
)
|
||||
return subprocess.run(args, capture_output=True, encoding=encoding,
|
||||
errors='ignore' if encoding is not None else None)
|
||||
|
||||
|
||||
def assert_track(
|
||||
|
|
@ -27,12 +31,12 @@ def assert_track(
|
|||
expected_title: str = "testing - test track",
|
||||
expected_artist: str = "7x11x13-testing",
|
||||
expected_genre: Optional[str] = "Testing",
|
||||
expected_artwork_len: int = 16136,
|
||||
expected_artwork_len: Optional[int] = 16136,
|
||||
expected_album: Optional[str] = None,
|
||||
expected_albumartist: Optional[str] = None,
|
||||
expected_tracknumber: Optional[int] = None,
|
||||
check_metadata: bool = True,
|
||||
):
|
||||
) -> None:
|
||||
file = tmp_path / expected_name
|
||||
assert file.exists()
|
||||
|
||||
|
|
@ -55,6 +59,6 @@ def assert_track(
|
|||
assert f["tracknumber"].value == expected_tracknumber
|
||||
|
||||
|
||||
def assert_not_track(tmp_path: Path, expected_name: str):
|
||||
def assert_not_track(tmp_path: Path, expected_name: str) -> None:
|
||||
file = tmp_path / expected_name
|
||||
assert not file.exists()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user