1.2.0
This commit is contained in:
43
.github/workflows/publish-to-pypi.yml
vendored
Normal file
43
.github/workflows/publish-to-pypi.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Publish Python Package to PyPI
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
name: Build and publish Python package to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Install pypa/build
|
||||
run: >-
|
||||
python -m
|
||||
pip install
|
||||
build
|
||||
--user
|
||||
|
||||
- name: Build a binary wheel and a source tarball
|
||||
run: >-
|
||||
python -m
|
||||
build
|
||||
--sdist
|
||||
--wheel
|
||||
--outdir dist/
|
||||
.
|
||||
|
||||
- name: Publish package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
# You may want to publish to a test PyPI repository first
|
||||
# repository_url: https://test.pypi.org/legacy/
|
||||
# package_name: deezspot-spotizerr # Ensure this matches your package name if needed explicitly
|
||||
917
README.md
917
README.md
@@ -1,712 +1,265 @@
|
||||
# DeezSpot
|
||||
|
||||
DeezSpot is a Python library that enables downloading songs, albums, playlists, and podcasts from both Deezer and Spotify. This fork includes tweaks for use with the [spotizerr](https://github.com/Xoconoch/spotizerr) project.
|
||||
## Description
|
||||
|
||||
DeezSpot is a Python library designed for downloading music and episodes from Deezer and Spotify. It allows users to fetch tracks, albums, playlists, and podcast episodes, with support for various audio qualities and tagging.
|
||||
|
||||
## Features
|
||||
|
||||
- Download tracks, albums, playlists, and podcasts from both Deezer and Spotify
|
||||
- Search for music by name or download directly using links
|
||||
- Support for different audio quality options
|
||||
- Download an artist's discography
|
||||
- Smart link detection to identify and process different types of content
|
||||
- Tag downloaded files with correct metadata
|
||||
- Customizable file and directory naming formats
|
||||
- **Download from Deezer and Spotify:** Supports downloading content from both major streaming platforms.
|
||||
- **Content Types:**
|
||||
* Tracks
|
||||
* Albums
|
||||
* Playlists
|
||||
* Podcast Episodes
|
||||
- **Audio Quality:** Choose from multiple audio quality options (e.g., MP3\_320, FLAC for Deezer; NORMAL, HIGH, VERY\_HIGH for Spotify).
|
||||
- **Audio Conversion:** Option to convert downloaded audio to different formats (e.g., mp3, flac, opus, m4a).
|
||||
- **Metadata Tagging:** Automatically tags downloaded files with relevant metadata (title, artist, album, cover art, etc.).
|
||||
- **Spotify to Deezer Link Conversion:** Convert Spotify track and album links to their Deezer equivalents to facilitate downloading via Deezer.
|
||||
- **Login Management:** Securely handles login credentials for Deezer (ARL cookie or email/password) and Spotify (client ID/secret, credentials file).
|
||||
- **Progress Reporting:** Provides callbacks for real-time progress updates during downloads.
|
||||
- **Customizable Output:** Flexible options for naming and organizing downloaded files and directories.
|
||||
- **Resilient Downloads:** Includes retry mechanisms for handling network issues.
|
||||
- **M3U Playlist Generation:** Automatically creates M3U playlist files for downloaded playlists.
|
||||
- **ZIP Archiving:** Option to create ZIP archives of downloaded albums and playlists.
|
||||
|
||||
## Installation
|
||||
|
||||
### From PyPI (recommended)
|
||||
(Provide instructions on how to install the library, e.g., using pip)
|
||||
|
||||
```bash
|
||||
pip install git+https://github.com/Xoconoch/deezspot-fork-again.git
|
||||
pip install deezspot-spotizerr
|
||||
```
|
||||
*(Note: Adjust the package name if it's different on PyPI)*
|
||||
|
||||
Or, if installing from a local source:
|
||||
```bash
|
||||
pip install .
|
||||
```
|
||||
|
||||
### From Source
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Xoconoch/deezspot-fork-again.git
|
||||
cd deezspot-fork-again
|
||||
pip install -e .
|
||||
### Initialization
|
||||
|
||||
**For Deezer:**
|
||||
|
||||
```python
|
||||
from deezspot import DeeLogin
|
||||
|
||||
# Using ARL cookie (recommended)
|
||||
deezer_downloader = DeeLogin(arl="YOUR_DEEZER_ARL_COOKIE")
|
||||
|
||||
# Or using email and password (less secure, might be deprecated by Deezer)
|
||||
# deezer_downloader = DeeLogin(email="your_email", password="your_password")
|
||||
```
|
||||
|
||||
**For Spotify:**
|
||||
|
||||
```python
|
||||
from deezspot.spotloader import SpoLogin # Corrected import path
|
||||
|
||||
# You need Spotify API credentials (client_id, client_secret)
|
||||
# and a credentials.json file for librespot.
|
||||
spotify_downloader = SpoLogin(
|
||||
credentials_path="path/to/your/credentials.json",
|
||||
spotify_client_id="YOUR_SPOTIFY_CLIENT_ID",
|
||||
spotify_client_secret="YOUR_SPOTIFY_CLIENT_SECRET"
|
||||
)
|
||||
```
|
||||
|
||||
### Downloading Content
|
||||
|
||||
**Deezer Examples:**
|
||||
|
||||
```python
|
||||
# Download a track from Deezer
|
||||
track_link_dee = "https://www.deezer.com/track/TRACK_ID"
|
||||
downloaded_track = deezer_downloader.download_trackdee(
|
||||
link_track=track_link_dee,
|
||||
output_dir="./music_downloads",
|
||||
quality_download="FLAC", # or "MP3_320"
|
||||
convert_to="mp3-320" # Optional: convert to mp3 at 320kbps
|
||||
)
|
||||
print(f"Downloaded Deezer track: {downloaded_track.song_path}")
|
||||
|
||||
# Download an album from Deezer
|
||||
album_link_dee = "https://www.deezer.com/album/ALBUM_ID"
|
||||
downloaded_album = deezer_downloader.download_albumdee(
|
||||
link_album=album_link_dee,
|
||||
output_dir="./music_downloads",
|
||||
make_zip=True
|
||||
)
|
||||
print(f"Downloaded Deezer album: {downloaded_album.album_name}")
|
||||
if downloaded_album.zip_path:
|
||||
print(f"Album ZIP created at: {downloaded_album.zip_path}")
|
||||
|
||||
|
||||
# Download a playlist from Deezer
|
||||
playlist_link_dee = "https://www.deezer.com/playlist/PLAYLIST_ID"
|
||||
downloaded_playlist = deezer_downloader.download_playlistdee(
|
||||
link_playlist=playlist_link_dee,
|
||||
output_dir="./music_downloads"
|
||||
)
|
||||
print(f"Downloaded Deezer playlist: {len(downloaded_playlist.tracks)} tracks")
|
||||
|
||||
# Download an episode from Deezer
|
||||
episode_link_dee = "https://www.deezer.com/episode/EPISODE_ID"
|
||||
downloaded_episode = deezer_downloader.download_episode(
|
||||
link_episode=episode_link_dee,
|
||||
output_dir="./podcast_downloads"
|
||||
)
|
||||
print(f"Downloaded Deezer episode: {downloaded_episode.episode_path}")
|
||||
```
|
||||
|
||||
**Spotify Examples:**
|
||||
|
||||
```python
|
||||
# Download a track from Spotify
|
||||
track_link_spo = "https://open.spotify.com/track/TRACK_ID"
|
||||
downloaded_track_spo = spotify_downloader.download_track(
|
||||
link_track=track_link_spo,
|
||||
output_dir="./music_downloads_spotify",
|
||||
quality_download="VERY_HIGH", # or "HIGH", "NORMAL"
|
||||
convert_to="flac" # Optional: convert to FLAC
|
||||
)
|
||||
print(f"Downloaded Spotify track: {downloaded_track_spo.song_path}")
|
||||
|
||||
# Download an album from Spotify
|
||||
album_link_spo = "https://open.spotify.com/album/ALBUM_ID"
|
||||
downloaded_album_spo = spotify_downloader.download_album(
|
||||
link_album=album_link_spo,
|
||||
output_dir="./music_downloads_spotify",
|
||||
make_zip=True
|
||||
)
|
||||
print(f"Downloaded Spotify album: {downloaded_album_spo.album_name}")
|
||||
|
||||
# Download a playlist from Spotify
|
||||
playlist_link_spo = "https://open.spotify.com/playlist/PLAYLIST_ID"
|
||||
downloaded_playlist_spo = spotify_downloader.download_playlist(
|
||||
link_playlist=playlist_link_spo,
|
||||
output_dir="./music_downloads_spotify"
|
||||
)
|
||||
print(f"Downloaded Spotify playlist: {len(downloaded_playlist_spo.tracks)} tracks")
|
||||
|
||||
# Download an episode from Spotify
|
||||
episode_link_spo = "https://open.spotify.com/episode/EPISODE_ID"
|
||||
downloaded_episode_spo = spotify_downloader.download_episode(
|
||||
link_episode=episode_link_spo,
|
||||
output_dir="./podcast_downloads_spotify"
|
||||
)
|
||||
print(f"Downloaded Spotify episode: {downloaded_episode_spo.episode_path}")
|
||||
```
|
||||
|
||||
**Smart Downloader (Deezer only for now):**
|
||||
|
||||
The smart downloader automatically detects the content type (track, album, playlist) from the URL.
|
||||
|
||||
```python
|
||||
smart_link_dee = "https://www.deezer.com/album/ALBUM_ID_OR_TRACK_ID_OR_PLAYLIST_ID"
|
||||
smart_download_result = deezer_downloader.download_smart(
|
||||
link=smart_link_dee,
|
||||
output_dir="./smart_downloads"
|
||||
)
|
||||
print(f"Smart download type: {smart_download_result.type}")
|
||||
if smart_download_result.type == "track":
|
||||
print(f"Downloaded track: {smart_download_result.track.song_path}")
|
||||
elif smart_download_result.type == "album":
|
||||
print(f"Downloaded album: {smart_download_result.album.album_name}")
|
||||
# ... and so on for playlist
|
||||
```
|
||||
|
||||
### Customizing Output Paths
|
||||
|
||||
You can customize the directory structure and track filenames using `custom_dir_format` and `custom_track_format` parameters available in most download methods. These parameters accept strings with placeholders that will be replaced with metadata.
|
||||
|
||||
**Available placeholders:**
|
||||
|
||||
* `%artist%`
|
||||
* `%albumartist%`
|
||||
* `%album%`
|
||||
* `%title%`
|
||||
* `%tracknumber%` (can be padded with `pad_tracks=True/False`)
|
||||
* `%discnumber%`
|
||||
* `%year%`
|
||||
* `%genre%`
|
||||
* `%isrc%`
|
||||
* `%upc%` (for albums)
|
||||
* `%quality%` (e.g., "FLAC", "320")
|
||||
* `%explicit%` (e.g. "Explicit" or empty string)
|
||||
* `%showname%` (for episodes)
|
||||
* `%episodetitle%` (for episodes)
|
||||
* `%podcastgenre%` (for episodes)
|
||||
|
||||
**Example:**
|
||||
|
||||
```python
|
||||
# For Deezer
|
||||
deezer_downloader.download_trackdee(
|
||||
link_track="some_track_link",
|
||||
output_dir="./custom_music",
|
||||
custom_dir_format="%artist%/%album% (%year%)",
|
||||
custom_track_format="%tracknumber%. %title% [%quality%]",
|
||||
pad_tracks=True # Results in "01. Song Title [FLAC]"
|
||||
)
|
||||
|
||||
# For Spotify
|
||||
spotify_downloader.download_track(
|
||||
link_track="some_spotify_track_link",
|
||||
output_dir="./custom_music_spotify",
|
||||
custom_dir_format="%artist% - %album%",
|
||||
custom_track_format="%title% - %artist%",
|
||||
pad_tracks=False
|
||||
)
|
||||
```
|
||||
|
||||
### Progress Reporting
|
||||
|
||||
You can provide a callback function during initialization to receive progress updates.
|
||||
|
||||
```python
|
||||
def my_progress_callback(progress_data):
|
||||
# progress_data is a dictionary with information about the download progress
|
||||
# Example keys: "type", "status", "song", "artist", "progress", "total_tracks", "current_track", "parent", etc.
|
||||
print(f"Progress: {progress_data}")
|
||||
|
||||
# For Deezer
|
||||
# deezer_downloader = DeeLogin(arl="YOUR_ARL", progress_callback=my_progress_callback)
|
||||
|
||||
# For Spotify
|
||||
# spotify_downloader = SpoLogin(
|
||||
# credentials_path="path/to/credentials.json",
|
||||
# spotify_client_id="YOUR_ID",
|
||||
# spotify_client_secret="YOUR_SECRET",
|
||||
# progress_callback=my_progress_callback
|
||||
# )
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Deezer Authentication
|
||||
|
||||
DeezSpot supports two methods of authentication for Deezer:
|
||||
|
||||
1. Using ARL token:
|
||||
```python
|
||||
from deezspot.deezloader import DeeLogin
|
||||
|
||||
# Authenticate with ARL
|
||||
downloader = DeeLogin(arl="your_arl_token")
|
||||
```
|
||||
|
||||
2. Using email and password:
|
||||
```python
|
||||
from deezspot.deezloader import DeeLogin
|
||||
|
||||
# Authenticate with email and password
|
||||
downloader = DeeLogin(email="your_email", password="your_password")
|
||||
```
|
||||
|
||||
### Spotify Authentication
|
||||
|
||||
For Spotify, you'll need a credentials file:
|
||||
|
||||
```python
|
||||
from deezspot.spotloader import SpoLogin
|
||||
|
||||
# Authenticate with credentials file
|
||||
downloader = SpoLogin(credentials_path="/path/to/credentials.json")
|
||||
```
|
||||
|
||||
To create a credentials file, use a tool like [librespot-java](https://github.com/librespot-org/librespot-java) to generate it.
|
||||
|
||||
## Usage
|
||||
|
||||
### Downloading from Deezer
|
||||
|
||||
```python
|
||||
from deezspot.deezloader import DeeLogin
|
||||
|
||||
# Initialize with your credentials
|
||||
downloader = DeeLogin(arl="your_arl_token")
|
||||
|
||||
# Download a track
|
||||
track = downloader.download_trackdee(
|
||||
"https://www.deezer.com/track/123456789",
|
||||
output_dir="./downloads",
|
||||
quality_download="FLAC" # Options: MP3_320, FLAC, MP3_128
|
||||
)
|
||||
|
||||
# Download an album
|
||||
album = downloader.download_albumdee(
|
||||
"https://www.deezer.com/album/123456789",
|
||||
output_dir="./downloads",
|
||||
quality_download="MP3_320",
|
||||
make_zip=True # Create a zip archive of the album
|
||||
)
|
||||
|
||||
# Download a playlist
|
||||
playlist = downloader.download_playlistdee(
|
||||
"https://www.deezer.com/playlist/123456789",
|
||||
output_dir="./downloads",
|
||||
quality_download="MP3_320"
|
||||
)
|
||||
|
||||
# Download an artist's top tracks
|
||||
tracks = downloader.download_artisttopdee(
|
||||
"https://www.deezer.com/artist/123456789",
|
||||
output_dir="./downloads"
|
||||
)
|
||||
|
||||
# Search and download by name
|
||||
track = downloader.download_name(
|
||||
artist="Artist Name",
|
||||
song="Song Title",
|
||||
output_dir="./downloads"
|
||||
)
|
||||
```
|
||||
|
||||
### Downloading from Spotify
|
||||
|
||||
```python
|
||||
from deezspot.spotloader import SpoLogin
|
||||
import logging
|
||||
from deezspot import set_log_level, enable_file_logging
|
||||
|
||||
# Configure logging
|
||||
set_log_level(logging.INFO)
|
||||
enable_file_logging("spotify_downloads.log")
|
||||
|
||||
# Custom progress callback
|
||||
def spotify_progress_callback(progress_data):
|
||||
status = progress_data.get("status")
|
||||
if status == "real_time":
|
||||
song = progress_data.get("song", "Unknown")
|
||||
percentage = progress_data.get("percentage", 0) * 100
|
||||
print(f"Downloading '{song}': {percentage:.1f}%")
|
||||
elif status == "downloading":
|
||||
print(f"Starting download: {progress_data.get('song', 'Unknown')}")
|
||||
elif status == "done":
|
||||
print(f"Completed: {progress_data.get('song', 'Unknown')}")
|
||||
|
||||
# Initialize Spotify client with progress callback
|
||||
spotify = SpoLogin(
|
||||
credentials_path="credentials.json",
|
||||
spotify_client_id="your_client_id",
|
||||
spotify_client_secret="your_client_secret",
|
||||
progress_callback=spotify_progress_callback
|
||||
)
|
||||
|
||||
# Or use silent mode for background operations
|
||||
spotify_silent = SpoLogin(
|
||||
credentials_path="credentials.json",
|
||||
spotify_client_id="your_client_id",
|
||||
spotify_client_secret="your_client_secret",
|
||||
silent=True
|
||||
)
|
||||
|
||||
# Download a track
|
||||
spotify.download_track(
|
||||
"https://open.spotify.com/track/4cOdK2wGLETKBW3PvgPWqT",
|
||||
output_dir="downloads",
|
||||
quality_download="HIGH",
|
||||
real_time_dl=True
|
||||
)
|
||||
|
||||
# Download an album
|
||||
album = spotify.download_album(
|
||||
"https://open.spotify.com/album/123456789",
|
||||
output_dir="./downloads",
|
||||
quality_download="HIGH",
|
||||
make_zip=True # Create a zip archive of the album
|
||||
)
|
||||
|
||||
# Download a playlist
|
||||
playlist = spotify.download_playlist(
|
||||
"https://open.spotify.com/playlist/123456789",
|
||||
output_dir="./downloads"
|
||||
)
|
||||
|
||||
# Download a podcast episode
|
||||
episode = spotify.download_episode(
|
||||
"https://open.spotify.com/episode/123456789",
|
||||
output_dir="./downloads"
|
||||
)
|
||||
|
||||
# Download an artist's discography
|
||||
spotify.download_artist(
|
||||
"https://open.spotify.com/artist/123456789",
|
||||
album_type="album,single", # Options: album, single, compilation, appears_on
|
||||
limit=50, # Number of albums to retrieve
|
||||
output_dir="./downloads"
|
||||
)
|
||||
```
|
||||
|
||||
### Smart Download
|
||||
|
||||
Both the Deezer and Spotify interfaces provide a "smart" download function that automatically detects the type of content from the link:
|
||||
|
||||
```python
|
||||
# For Deezer
|
||||
result = downloader.download_smart("https://www.deezer.com/track/123456789")
|
||||
|
||||
# For Spotify
|
||||
result = downloader.download_smart("https://open.spotify.com/album/123456789")
|
||||
```
|
||||
|
||||
### Converting Spotify links to Deezer
|
||||
|
||||
DeezSpot can also convert Spotify links to Deezer for downloading with higher quality:
|
||||
|
||||
```python
|
||||
# Convert and download a Spotify track using Deezer
|
||||
track = downloader.download_trackspo("https://open.spotify.com/track/123456789")
|
||||
|
||||
# Convert and download a Spotify album using Deezer
|
||||
album = downloader.download_albumspo("https://open.spotify.com/album/123456789")
|
||||
|
||||
# Convert and download a Spotify playlist using Deezer
|
||||
playlist = downloader.download_playlistspo("https://open.spotify.com/playlist/123456789")
|
||||
```
|
||||
|
||||
## Available Quality Options
|
||||
|
||||
### Deezer
|
||||
- `MP3_320`: 320 kbps MP3
|
||||
- `FLAC`: Lossless audio
|
||||
- `MP3_128`: 128 kbps MP3
|
||||
- **ARL Cookie:** The primary method for Deezer authentication. You can obtain this from your browser's developer tools when logged into Deezer.
|
||||
- **Email/Password:** Can be used but is less secure and may have limitations.
|
||||
|
||||
### Spotify
|
||||
- `VERY_HIGH`: 320 kbps OGG
|
||||
- `HIGH`: 160 kbps OGG
|
||||
- `NORMAL`: 96 kbps OGG
|
||||
- **Spotify API Credentials:** You'll need a Client ID and Client Secret from the Spotify Developer Dashboard.
|
||||
- **`credentials.json`:** This file is used by the underlying `librespot` library for session management. The `SpoLogin` class requires the path to this file. If it doesn't exist, `librespot` might attempt to create it or guide you through an authentication flow (behavior depends on `librespot`).
|
||||
|
||||
## Common Parameters
|
||||
## Logging
|
||||
|
||||
Most download methods accept these common parameters:
|
||||
|
||||
- `output_dir`: Output directory for downloaded files (default: "Songs/")
|
||||
- `quality_download`: Quality of audio files (see options above)
|
||||
- `recursive_quality`: Try another quality if the selected one is not available (default: True)
|
||||
- `recursive_download`: Try another API if the current one fails (default: True)
|
||||
- `not_interface`: Hide download progress (default: False)
|
||||
- `make_zip`: Create a zip archive for albums/playlists (default: False)
|
||||
- `method_save`: How to save the downloads (default: varies by function)
|
||||
- `custom_dir_format`: Custom directory naming format
|
||||
- `custom_track_format`: Custom track naming format
|
||||
|
||||
## Custom Naming Formats
|
||||
|
||||
You can customize the output directory and file naming patterns:
|
||||
The library uses Python's standard `logging` module. You can configure the logging level and output:
|
||||
|
||||
```python
|
||||
# Example of custom directory format
|
||||
result = downloader.download_albumdee(
|
||||
"https://www.deezer.com/album/123456789",
|
||||
custom_dir_format="{artist}/{album} [{year}]"
|
||||
)
|
||||
|
||||
# Example of custom track format
|
||||
result = downloader.download_trackdee(
|
||||
"https://www.deezer.com/track/123456789",
|
||||
custom_track_format="{tracknumber} - {title}"
|
||||
)
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the GNU Affero General Public License v3.0 - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## Credits
|
||||
|
||||
This project is a fork of the [original deezspot library](https://github.com/jakiepari/deezspot).
|
||||
|
||||
# Deezspot Logging System
|
||||
|
||||
This document explains the enhanced logging system implemented in the Deezspot library, making it more suitable for production environments, Celery integrations, and other enterprise applications.
|
||||
|
||||
## Overview
|
||||
|
||||
The logging system in Deezspot provides:
|
||||
|
||||
- Standardized, structured log messages
|
||||
- Multiple logging levels for different verbosity needs
|
||||
- File logging capabilities for persistent logs
|
||||
- Console output for interactive use
|
||||
- JSON-formatted progress updates
|
||||
- Custom progress callbacks for integration with other systems
|
||||
- Silent mode for background operation
|
||||
|
||||
## Basic Configuration
|
||||
|
||||
### Setting Log Level
|
||||
|
||||
```python
|
||||
from deezspot import set_log_level
|
||||
import logging
|
||||
from deezspot import set_log_level, enable_file_logging, disable_logging
|
||||
|
||||
# Available log levels:
|
||||
# - logging.DEBUG (most verbose)
|
||||
# - logging.INFO (default)
|
||||
# - logging.WARNING
|
||||
# - logging.ERROR
|
||||
# - logging.CRITICAL (least verbose)
|
||||
# Set logging level (e.g., INFO, DEBUG, WARNING)
|
||||
set_log_level(logging.DEBUG)
|
||||
|
||||
set_log_level(logging.INFO) # Default level shows important information
|
||||
set_log_level(logging.DEBUG) # For detailed debugging information
|
||||
set_log_level(logging.WARNING) # For warnings and errors only
|
||||
# Enable logging to a file
|
||||
# enable_file_logging("deezspot.log", level=logging.INFO)
|
||||
|
||||
# Disable all logging
|
||||
# disable_logging()
|
||||
```
|
||||
|
||||
### Enabling File Logging
|
||||
|
||||
```python
|
||||
from deezspot import enable_file_logging
|
||||
|
||||
# Enable logging to a file (in addition to console)
|
||||
enable_file_logging("/path/to/logs/deezspot.log")
|
||||
|
||||
# With custom log level
|
||||
enable_file_logging("/path/to/logs/deezspot.log", level=logging.DEBUG)
|
||||
```
|
||||
|
||||
### Disabling Logging
|
||||
|
||||
```python
|
||||
from deezspot import disable_logging
|
||||
|
||||
# Completely disable logging (except critical errors)
|
||||
disable_logging()
|
||||
```
|
||||
|
||||
## Progress Reporting
|
||||
|
||||
The library uses a structured JSON format for progress reporting, making it easy to integrate with other systems.
|
||||
|
||||
### Progress JSON Structure
|
||||
|
||||
For tracks:
|
||||
```json
|
||||
{
|
||||
"status": "downloading|progress|done|skipped|retrying",
|
||||
"type": "track",
|
||||
"album": "Album Name",
|
||||
"song": "Song Title",
|
||||
"artist": "Artist Name"
|
||||
}
|
||||
```
|
||||
|
||||
For real-time downloads:
|
||||
```json
|
||||
{
|
||||
"status": "real_time",
|
||||
"song": "Song Title",
|
||||
"artist": "Artist Name",
|
||||
"time_elapsed": 1500,
|
||||
"percentage": 0.75
|
||||
}
|
||||
```
|
||||
|
||||
For albums:
|
||||
```json
|
||||
{
|
||||
"status": "initializing|progress|done",
|
||||
"type": "album",
|
||||
"album": "Album Name",
|
||||
"artist": "Artist Name",
|
||||
"track": "Current Track Title",
|
||||
"current_track": "3/12"
|
||||
}
|
||||
```
|
||||
|
||||
For playlists:
|
||||
```json
|
||||
{
|
||||
"status": "initializing|progress|done",
|
||||
"type": "playlist",
|
||||
"name": "Playlist Name",
|
||||
"track": "Current Track Title",
|
||||
"current_track": "5/25",
|
||||
"total_tracks": 25
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Progress Callbacks
|
||||
|
||||
For integration with other systems (like Celery), you can provide a custom progress callback function when initializing the library.
|
||||
|
||||
### Example with Custom Callback
|
||||
|
||||
```python
|
||||
from deezspot.deezloader import DeeLogin
|
||||
|
||||
def my_progress_callback(progress_data):
|
||||
"""
|
||||
Custom callback function to handle progress updates
|
||||
|
||||
Args:
|
||||
progress_data: Dictionary containing progress information
|
||||
"""
|
||||
status = progress_data.get("status")
|
||||
track_title = progress_data.get("song", "")
|
||||
|
||||
if status == "downloading":
|
||||
print(f"Starting download: {track_title}")
|
||||
elif status == "progress":
|
||||
current = progress_data.get("current_track", "")
|
||||
print(f"Progress: {current} - {track_title}")
|
||||
elif status == "done":
|
||||
print(f"Completed: {track_title}")
|
||||
elif status == "real_time":
|
||||
percentage = progress_data.get("percentage", 0) * 100
|
||||
print(f"Downloading: {track_title} - {percentage:.1f}%")
|
||||
|
||||
# Initialize with custom callback
|
||||
deezer = DeeLogin(
|
||||
arl="your_arl_token",
|
||||
progress_callback=my_progress_callback
|
||||
)
|
||||
```
|
||||
|
||||
### Silent Mode
|
||||
|
||||
If you want to disable progress reporting completely (for background operations), use silent mode:
|
||||
|
||||
```python
|
||||
deezer = DeeLogin(
|
||||
arl="your_arl_token",
|
||||
silent=True
|
||||
)
|
||||
```
|
||||
|
||||
## Spotify Integration
|
||||
|
||||
For Spotify downloads, the same logging principles apply. Here's an example using the Spotify client:
|
||||
|
||||
```python
|
||||
from deezspot.spotloader import SpoLogin
|
||||
import logging
|
||||
from deezspot import set_log_level, enable_file_logging
|
||||
|
||||
# Configure logging
|
||||
set_log_level(logging.INFO)
|
||||
enable_file_logging("spotify_downloads.log")
|
||||
|
||||
# Custom progress callback
|
||||
def spotify_progress_callback(progress_data):
|
||||
status = progress_data.get("status")
|
||||
if status == "real_time":
|
||||
song = progress_data.get("song", "Unknown")
|
||||
percentage = progress_data.get("percentage", 0) * 100
|
||||
print(f"Downloading '{song}': {percentage:.1f}%")
|
||||
elif status == "downloading":
|
||||
print(f"Starting download: {progress_data.get('song', 'Unknown')}")
|
||||
elif status == "done":
|
||||
print(f"Completed: {progress_data.get('song', 'Unknown')}")
|
||||
|
||||
# Initialize Spotify client with progress callback
|
||||
spotify = SpoLogin(
|
||||
credentials_path="credentials.json",
|
||||
spotify_client_id="your_client_id",
|
||||
spotify_client_secret="your_client_secret",
|
||||
progress_callback=spotify_progress_callback
|
||||
)
|
||||
|
||||
# Or use silent mode for background operations
|
||||
spotify_silent = SpoLogin(
|
||||
credentials_path="credentials.json",
|
||||
spotify_client_id="your_client_id",
|
||||
spotify_client_secret="your_client_secret",
|
||||
silent=True
|
||||
)
|
||||
|
||||
# Download a track
|
||||
spotify.download_track(
|
||||
"https://open.spotify.com/track/4cOdK2wGLETKBW3PvgPWqT",
|
||||
output_dir="downloads",
|
||||
quality_download="HIGH",
|
||||
real_time_dl=True
|
||||
)
|
||||
```
|
||||
|
||||
## Celery Integration Example
|
||||
|
||||
Here's how to integrate the logging system with Celery for task progress reporting:
|
||||
|
||||
```python
|
||||
from celery import Celery
|
||||
from deezspot.deezloader import DeeLogin
|
||||
import logging
|
||||
from deezspot import enable_file_logging
|
||||
|
||||
# Configure Celery
|
||||
app = Celery('tasks', broker='pyamqp://guest@localhost//')
|
||||
|
||||
# Configure logging
|
||||
enable_file_logging("/path/to/logs/deezspot.log", level=logging.INFO)
|
||||
|
||||
@app.task(bind=True)
|
||||
def download_music(self, link, output_dir):
|
||||
# Create a progress callback that updates the Celery task state
|
||||
def update_progress(progress_data):
|
||||
status = progress_data.get("status")
|
||||
|
||||
if status == "downloading":
|
||||
self.update_state(
|
||||
state="DOWNLOADING",
|
||||
meta={
|
||||
"track": progress_data.get("song", ""),
|
||||
"progress": 0
|
||||
}
|
||||
)
|
||||
elif status == "progress":
|
||||
current, total = progress_data.get("current_track", "1/1").split("/")
|
||||
progress = int(current) / int(total)
|
||||
self.update_state(
|
||||
state="PROGRESS",
|
||||
meta={
|
||||
"track": progress_data.get("track", ""),
|
||||
"progress": progress
|
||||
}
|
||||
)
|
||||
elif status == "real_time":
|
||||
self.update_state(
|
||||
state="PROGRESS",
|
||||
meta={
|
||||
"track": progress_data.get("song", ""),
|
||||
"progress": progress_data.get("percentage", 0)
|
||||
}
|
||||
)
|
||||
elif status == "done":
|
||||
self.update_state(
|
||||
state="COMPLETED",
|
||||
meta={
|
||||
"track": progress_data.get("song", ""),
|
||||
"progress": 1.0
|
||||
}
|
||||
)
|
||||
|
||||
# Initialize the client with the progress callback
|
||||
deezer = DeeLogin(
|
||||
arl="your_arl_token",
|
||||
progress_callback=update_progress
|
||||
)
|
||||
|
||||
# Download the content
|
||||
result = deezer.download_smart(
|
||||
link=link,
|
||||
output_dir=output_dir,
|
||||
quality_download="MP3_320"
|
||||
)
|
||||
|
||||
return {"status": "completed", "output": result.track.song_path}
|
||||
```
|
||||
|
||||
## Direct Logger Access
|
||||
|
||||
For advanced use cases, you can directly access and use the logger:
|
||||
|
||||
```python
|
||||
from deezspot.libutils.logging_utils import logger
|
||||
|
||||
# Use the logger directly
|
||||
logger.debug("Detailed debugging information")
|
||||
logger.info("General information")
|
||||
logger.warning("Warning message")
|
||||
logger.error("Error message")
|
||||
logger.critical("Critical error message")
|
||||
|
||||
# Log structured data
|
||||
import json
|
||||
logger.info(json.dumps({
|
||||
"custom_event": "download_started",
|
||||
"metadata": {
|
||||
"source": "spotify",
|
||||
"track_id": "1234567890"
|
||||
}
|
||||
}))
|
||||
```
|
||||
|
||||
## Log Format
|
||||
|
||||
The default log format is:
|
||||
```
|
||||
%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
||||
```
|
||||
|
||||
Example:
|
||||
```
|
||||
2023-10-15 12:34:56,789 - deezspot - INFO - {"status": "downloading", "type": "track", "album": "Album Name", "song": "Song Title", "artist": "Artist Name"}
|
||||
```
|
||||
|
||||
## Console vs File Logging
|
||||
|
||||
- By default, the library is configured to log at WARNING level to the console only
|
||||
- You can enable file logging in addition to console logging
|
||||
- File and console logging can have different log levels
|
||||
|
||||
## Using the Logger in Your Code
|
||||
|
||||
If you're extending the library or integrating it deeply into your application, you can use the logger directly:
|
||||
|
||||
```python
|
||||
from deezspot.libutils.logging_utils import logger, ProgressReporter
|
||||
|
||||
# Create a custom progress reporter
|
||||
my_reporter = ProgressReporter(
|
||||
callback=my_callback_function,
|
||||
silent=False,
|
||||
log_level=logging.INFO
|
||||
)
|
||||
|
||||
# Report progress
|
||||
my_reporter.report({
|
||||
"status": "custom_status",
|
||||
"message": "Custom progress message",
|
||||
"progress": 0.5
|
||||
})
|
||||
|
||||
# Log directly
|
||||
logger.info("Processing started")
|
||||
```
|
||||
|
||||
## Test Script Example
|
||||
|
||||
Here's a complete example script that tests the Spotify functionality with logging enabled:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import logging
|
||||
from deezspot import set_log_level, enable_file_logging
|
||||
from deezspot.spotloader import SpoLogin
|
||||
|
||||
def main():
|
||||
# Configure logging
|
||||
set_log_level(logging.INFO) # Set to logging.DEBUG for more detailed output
|
||||
enable_file_logging("deezspot.log")
|
||||
|
||||
# Spotify API credentials
|
||||
SPOTIFY_CLIENT_ID = "your_client_id"
|
||||
SPOTIFY_CLIENT_SECRET = "your_client_secret"
|
||||
|
||||
# Path to your Spotify credentials file (from librespot)
|
||||
CREDENTIALS_PATH = "credentials.json"
|
||||
|
||||
# Output directory for downloads
|
||||
OUTPUT_DIR = "downloads"
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
try:
|
||||
# Initialize the Spotify client
|
||||
spotify = SpoLogin(
|
||||
credentials_path=CREDENTIALS_PATH,
|
||||
spotify_client_id=SPOTIFY_CLIENT_ID,
|
||||
spotify_client_secret=SPOTIFY_CLIENT_SECRET
|
||||
)
|
||||
|
||||
# Test track download
|
||||
print("\nTesting track download...")
|
||||
track_url = "https://open.spotify.com/track/4cOdK2wGLETKBW3PvgPWqT"
|
||||
spotify.download_track(
|
||||
track_url,
|
||||
output_dir=OUTPUT_DIR,
|
||||
quality_download="HIGH",
|
||||
real_time_dl=True,
|
||||
custom_dir_format="{artist}/{album}",
|
||||
custom_track_format="{tracknum} - {title}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {str(e)}")
|
||||
logging.error(f"Test failed: {str(e)}", exc_info=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
This logging system provides flexibility for both simple scripts and complex production applications, making it easier to monitor and integrate Deezspot in any environment.
|
||||
|
||||
## Callback Functionality
|
||||
|
||||
Both the Deezer and Spotify components of the deezspot library now support progress callbacks, allowing you to integrate download progress into your applications. This feature enables:
|
||||
|
||||
1. **Real-time Progress Tracking**: Monitor download progress for tracks, albums, playlists, and episodes
|
||||
2. **Custom UI Integration**: Update your application's UI with download status
|
||||
3. **Background Processing**: Run downloads silently in background tasks
|
||||
4. **Task Management**: Integrate with task systems like Celery for distributed processing
|
||||
|
||||
### Common Callback Events
|
||||
|
||||
The progress callback function receives a dictionary with the following common fields:
|
||||
|
||||
- `status`: The current status of the operation (`initializing`, `downloading`, `progress`, `done`, `skipped`, `retrying`, `real_time`)
|
||||
- `type`: The type of content (`track`, `album`, `playlist`, `episode`)
|
||||
- Additional fields depending on the status and content type
|
||||
|
||||
### Usage in Both Components
|
||||
|
||||
Both the Deezer and Spotify components use the same callback system:
|
||||
|
||||
```python
|
||||
# For Deezer
|
||||
deezer = DeeLogin(
|
||||
arl="your_arl_token",
|
||||
progress_callback=my_callback_function,
|
||||
silent=False # Set to True to disable progress reporting
|
||||
)
|
||||
|
||||
# For Spotify
|
||||
spotify = SpoLogin(
|
||||
credentials_path="credentials.json",
|
||||
spotify_client_id="your_client_id",
|
||||
spotify_client_secret="your_client_secret",
|
||||
progress_callback=my_callback_function,
|
||||
silent=False # Set to True to disable progress reporting
|
||||
)
|
||||
```
|
||||
|
||||
The standardized callback system ensures that your application can handle progress updates consistently regardless of whether the content is being downloaded from Deezer or Spotify.
|
||||
|
||||
@@ -11,7 +11,7 @@ from deezspot.libutils.logging_utils import configure_logger, logger
|
||||
from deezspot.deezloader import DeeLogin
|
||||
from deezspot.models import Track, Album, Playlist, Smart, Episode
|
||||
|
||||
__version__ = "1.0.1"
|
||||
__version__ = "1.2.0"
|
||||
|
||||
# Configure default logging (silent by default)
|
||||
configure_logger(level=logging.WARNING, to_console=False)
|
||||
|
||||
@@ -3,7 +3,7 @@ requires = ["setuptools>=42", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deezspot"
|
||||
name = "deezspot-spotizerr"
|
||||
version = "1.1"
|
||||
description = "Downloads songs, albums or playlists from deezer and spotify (clone from deezloader)"
|
||||
readme = "README.md"
|
||||
@@ -22,13 +22,13 @@ dependencies = [
|
||||
"fastapi",
|
||||
"uvicorn[standard]",
|
||||
"spotipy-anon",
|
||||
"librespot"
|
||||
"librespot-spotizerr"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/jakiepari/deezspot"
|
||||
Repository = "https://github.com/jakiepari/deezspot.git"
|
||||
Documentation = "https://github.com/jakiepari/deezspot/blob/main/README.md"
|
||||
Homepage = "https://github.com/Xoconoch/deezspot-spotizerr"
|
||||
Repository = "https://github.com/Xoconoch/deezspot-spotizerr.git"
|
||||
Documentation = "https://github.com/Xoconoch/deezspot-spotizerr/blob/main/README.md"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = [
|
||||
|
||||
14
setup.py
14
setup.py
@@ -5,9 +5,9 @@ readmed = README.read()
|
||||
README.close()
|
||||
|
||||
setup(
|
||||
name = "deezspot",
|
||||
version = "1.1",
|
||||
description = "Downloads songs, albums or playlists from deezer and spotify (clone from https://pypi.org/project/deezloader/)",
|
||||
name = "deezspot-spotizerr",
|
||||
version = "1.2.0",
|
||||
description = "Spotizerr's implementation of deezspot",
|
||||
long_description = readmed,
|
||||
long_description_content_type = "text/markdown",
|
||||
license = "GNU Affero General Public License v3",
|
||||
@@ -18,8 +18,10 @@ setup(
|
||||
|
||||
packages = [
|
||||
"deezspot",
|
||||
"deezspot/models", "deezspot/spotloader",
|
||||
"deezspot/deezloader", "deezspot/libutils"
|
||||
"deezspot.models",
|
||||
"deezspot.spotloader",
|
||||
"deezspot.deezloader",
|
||||
"deezspot.libutils"
|
||||
],
|
||||
|
||||
install_requires = [
|
||||
@@ -27,6 +29,6 @@ setup(
|
||||
"spotipy", "tqdm", "fastapi",
|
||||
"uvicorn[standard]",
|
||||
"spotipy-anon",
|
||||
"librespot"
|
||||
"librespot-spotizerr"
|
||||
],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user