todo: add global config support
This commit is contained in:
parent
1eef9756fd
commit
70da426463
15 changed files with 197 additions and 157 deletions
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -1,6 +1,21 @@
|
||||||
# Changelog:
|
# Changelog
|
||||||
### v0.5.2:
|
## v0.6
|
||||||
**General changes:**
|
**General changes**
|
||||||
|
- Switched from os.path to pathlib
|
||||||
|
- Zotify can now be installed with pip \
|
||||||
|
`pip install https://gitlab.com/team-zotify/zotify/-/archive/main/zotify-main.zip`
|
||||||
|
- Zotify can be ran from any directory with `zotify <args>`, you no longer need to prefix `python` in the command.
|
||||||
|
|
||||||
|
**Docker**
|
||||||
|
- Dockerfile is currently broken, it will be fixed soon. \
|
||||||
|
The Dockerhub image is now discontinued, we will try to switch to GitLab's container registry.
|
||||||
|
|
||||||
|
**Windows installer**
|
||||||
|
- The Windows installer is unavilable with this release.
|
||||||
|
- The current installation system will be replaced and a new version will be available with the next release.
|
||||||
|
|
||||||
|
## v0.5.2
|
||||||
|
**General changes**
|
||||||
- Fixed filenaming on Windows
|
- Fixed filenaming on Windows
|
||||||
- Fixed removal of special characters metadata
|
- Fixed removal of special characters metadata
|
||||||
- Can now download different songs with the same name
|
- Can now download different songs with the same name
|
||||||
|
@ -17,10 +32,10 @@
|
||||||
- Added options to regulate terminal output
|
- Added options to regulate terminal output
|
||||||
- Direct download support for certain podcasts
|
- Direct download support for certain podcasts
|
||||||
|
|
||||||
**Docker images:**
|
**Docker images**
|
||||||
- Remember credentials between container starts
|
- Remember credentials between container starts
|
||||||
- Use same uid/gid in container as on host
|
- Use same uid/gid in container as on host
|
||||||
|
|
||||||
**Windows installer:**
|
**Windows installer**
|
||||||
- Now comes with full installer
|
- Now comes with full installer
|
||||||
- Dependencies are installed if not found
|
- Dependencies are installed if not found
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
# Introduction
|
|
||||||
|
|
||||||
Below will contain sets of errors that you might get running zotify. Below will also contain possible fixes to these errors. It is advisable that you read this before posting your error in any support channel.
|
|
||||||
|
|
||||||
## AttributeError: module 'google.protobuf.descriptor' has no attribute '\_internal_create_key
|
|
||||||
|
|
||||||
_Answer(s):_
|
|
||||||
|
|
||||||
`pip install --upgrade protobuf`
|
|
22
setup.py
22
setup.py
|
@ -1,7 +1,6 @@
|
||||||
import pathlib
|
import pathlib
|
||||||
from setuptools import setup
|
from distutils.core import setup
|
||||||
import setuptools
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# The directory containing this file
|
# The directory containing this file
|
||||||
|
@ -13,17 +12,24 @@ README = (HERE / "README.md").read_text()
|
||||||
# This call to setup() does all the work
|
# This call to setup() does all the work
|
||||||
setup(
|
setup(
|
||||||
name="zotify",
|
name="zotify",
|
||||||
version="0.5.3",
|
version="0.6.0",
|
||||||
|
author="Zotify",
|
||||||
description="A music and podcast downloader.",
|
description="A music and podcast downloader.",
|
||||||
long_description=README,
|
long_description=README,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
url="https://gitlab.com/zotify/zotify.git",
|
url="https://gitlab.com/team-zotify/zotify.git",
|
||||||
author="zotify",
|
package_data={'': ['README.md', 'LICENSE']},
|
||||||
|
packages=['zotify'],
|
||||||
|
include_package_data=True,
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': [
|
||||||
|
'zotify=zotify.__main__:main',
|
||||||
|
],
|
||||||
|
},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
],
|
],
|
||||||
packages=['zotify'],
|
|
||||||
install_requires=['ffmpy', 'music_tag', 'Pillow', 'protobuf', 'tabulate', 'tqdm',
|
install_requires=['ffmpy', 'music_tag', 'Pillow', 'protobuf', 'tabulate', 'tqdm',
|
||||||
'librespot @ https://github.com/kokarare1212/librespot-python/archive/refs/heads/rewrite.zip'],
|
'librespot @ https://github.com/kokarare1212/librespot-python/archive/refs/heads/rewrite.zip'],
|
||||||
include_package_data=True,
|
|
||||||
)
|
)
|
||||||
|
|
0
zotify/__init__.py
Normal file
0
zotify/__init__.py
Normal file
|
@ -7,10 +7,10 @@ It's like youtube-dl, but for that other music platform.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from app import client
|
from zotify.app import client
|
||||||
from config import CONFIG_VALUES
|
from zotify.config import CONFIG_VALUES
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def main():
|
||||||
parser = argparse.ArgumentParser(prog='zotify',
|
parser = argparse.ArgumentParser(prog='zotify',
|
||||||
description='A music and podcast downloader needing only a python interpreter and ffmpeg.')
|
description='A music and podcast downloader needing only a python interpreter and ffmpeg.')
|
||||||
parser.add_argument('-ns', '--no-splash',
|
parser.add_argument('-ns', '--no-splash',
|
||||||
|
@ -51,3 +51,6 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
args.func(args)
|
args.func(args)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from const import ITEMS, ARTISTS, NAME, ID
|
from zotify.const import ITEMS, ARTISTS, NAME, ID
|
||||||
from termoutput import Printer
|
from zotify.termoutput import Printer
|
||||||
from track import download_track
|
from zotify.track import download_track
|
||||||
from utils import fix_filename
|
from zotify.utils import fix_filename
|
||||||
from zotify import Zotify
|
from zotify.zotify import Zotify
|
||||||
|
|
||||||
ALBUM_URL = 'https://api.spotify.com/v1/albums'
|
ALBUM_URL = 'https://api.spotify.com/v1/albums'
|
||||||
ARTIST_URL = 'https://api.spotify.com/v1/artists'
|
ARTIST_URL = 'https://api.spotify.com/v1/artists'
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
from librespot.audio.decoders import AudioQuality
|
from librespot.audio.decoders import AudioQuality
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
import os
|
#import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from album import download_album, download_artist_albums
|
from zotify.album import download_album, download_artist_albums
|
||||||
from const import TRACK, NAME, ID, ARTIST, ARTISTS, ITEMS, TRACKS, EXPLICIT, ALBUM, ALBUMS, \
|
from zotify.const import TRACK, NAME, ID, ARTIST, ARTISTS, ITEMS, TRACKS, EXPLICIT, ALBUM, ALBUMS, \
|
||||||
OWNER, PLAYLIST, PLAYLISTS, DISPLAY_NAME
|
OWNER, PLAYLIST, PLAYLISTS, DISPLAY_NAME, TYPE
|
||||||
from playlist import get_playlist_songs, get_playlist_info, download_from_user_playlist, download_playlist
|
from zotify.playlist import get_playlist_songs, get_playlist_info, download_from_user_playlist, download_playlist
|
||||||
from podcast import download_episode, get_show_episodes
|
from zotify.podcast import download_episode, get_show_episodes
|
||||||
from termoutput import Printer, PrintChannel
|
from zotify.termoutput import Printer, PrintChannel
|
||||||
from track import download_track, get_saved_tracks
|
from zotify.track import download_track, get_saved_tracks
|
||||||
from utils import splash, split_input, regex_input_for_urls
|
from zotify.utils import splash, split_input, regex_input_for_urls
|
||||||
from zotify import Zotify
|
from zotify.zotify import Zotify
|
||||||
|
|
||||||
SEARCH_URL = 'https://api.spotify.com/v1/search'
|
SEARCH_URL = 'https://api.spotify.com/v1/search'
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ def client(args) -> None:
|
||||||
if args.download:
|
if args.download:
|
||||||
urls = []
|
urls = []
|
||||||
filename = args.download
|
filename = args.download
|
||||||
if os.path.exists(filename):
|
if Path(filename).exists():
|
||||||
with open(filename, 'r', encoding='utf-8') as file:
|
with open(filename, 'r', encoding='utf-8') as file:
|
||||||
urls.extend([line.strip() for line in file.readlines()])
|
urls.extend([line.strip() for line in file.readlines()])
|
||||||
|
|
||||||
|
@ -88,14 +89,17 @@ def download_from_urls(urls: list[str]) -> bool:
|
||||||
if not song[TRACK][NAME] or not song[TRACK][ID]:
|
if not song[TRACK][NAME] or not song[TRACK][ID]:
|
||||||
Printer.print(PrintChannel.SKIPS, '### SKIPPING: SONG DOES NOT EXIST ANYMORE ###' + "\n")
|
Printer.print(PrintChannel.SKIPS, '### SKIPPING: SONG DOES NOT EXIST ANYMORE ###' + "\n")
|
||||||
else:
|
else:
|
||||||
download_track('playlist', song[TRACK][ID], extra_keys=
|
if song[TRACK][TYPE] == "episode": # Playlist item is a podcast episode
|
||||||
{
|
download_episode(song[TRACK][ID])
|
||||||
'playlist_song_name': song[TRACK][NAME],
|
else:
|
||||||
'playlist': name,
|
download_track('playlist', song[TRACK][ID], extra_keys=
|
||||||
'playlist_num': str(enum).zfill(char_num),
|
{
|
||||||
'playlist_id': playlist_id,
|
'playlist_song_name': song[TRACK][NAME],
|
||||||
'playlist_track_id': song[TRACK][ID]
|
'playlist': name,
|
||||||
})
|
'playlist_num': str(enum).zfill(char_num),
|
||||||
|
'playlist_id': playlist_id,
|
||||||
|
'playlist_track_id': song[TRACK][ID]
|
||||||
|
})
|
||||||
enum += 1
|
enum += 1
|
||||||
elif episode_id is not None:
|
elif episode_id is not None:
|
||||||
download = True
|
download = True
|
||||||
|
|
108
zotify/config.py
108
zotify/config.py
|
@ -1,8 +1,9 @@
|
||||||
import json
|
import json
|
||||||
import os
|
# import os
|
||||||
|
from pathlib import Path, PurePath
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
CONFIG_FILE_PATH = '../zconfig.json'
|
CONFIG_FILE_PATH = './zconfig.json'
|
||||||
|
|
||||||
ROOT_PATH = 'ROOT_PATH'
|
ROOT_PATH = 'ROOT_PATH'
|
||||||
ROOT_PODCAST_PATH = 'ROOT_PODCAST_PATH'
|
ROOT_PODCAST_PATH = 'ROOT_PODCAST_PATH'
|
||||||
|
@ -34,34 +35,34 @@ PRINT_WARNINGS = 'PRINT_WARNINGS'
|
||||||
RETRY_ATTEMPTS = 'RETRY_ATTEMPTS'
|
RETRY_ATTEMPTS = 'RETRY_ATTEMPTS'
|
||||||
|
|
||||||
CONFIG_VALUES = {
|
CONFIG_VALUES = {
|
||||||
ROOT_PATH: { 'default': '../Zotify Music/', 'type': str, 'arg': '--root-path' },
|
ROOT_PATH: { 'default': './Zotify Music/', 'type': str, 'arg': '--root-path' },
|
||||||
ROOT_PODCAST_PATH: { 'default': '../Zotify Podcasts/', 'type': str, 'arg': '--root-podcast-path' },
|
ROOT_PODCAST_PATH: { 'default': './Zotify Podcasts/', 'type': str, 'arg': '--root-podcast-path' },
|
||||||
SKIP_EXISTING_FILES: { 'default': 'True', 'type': bool, 'arg': '--skip-existing-files' },
|
SKIP_EXISTING_FILES: { 'default': 'True', 'type': bool, 'arg': '--skip-existing-files' },
|
||||||
SKIP_PREVIOUSLY_DOWNLOADED: { 'default': 'False', 'type': bool, 'arg': '--skip-previously-downloaded' },
|
SKIP_PREVIOUSLY_DOWNLOADED: { 'default': 'False', 'type': bool, 'arg': '--skip-previously-downloaded' },
|
||||||
RETRY_ATTEMPTS: { 'default': '5', 'type': int, 'arg': '--retry-attemps' },
|
RETRY_ATTEMPTS: { 'default': '5', 'type': int, 'arg': '--retry-attemps' },
|
||||||
DOWNLOAD_FORMAT: { 'default': 'ogg', 'type': str, 'arg': '--download-format' },
|
DOWNLOAD_FORMAT: { 'default': 'ogg', 'type': str, 'arg': '--download-format' },
|
||||||
FORCE_PREMIUM: { 'default': 'False', 'type': bool, 'arg': '--force-premium' },
|
FORCE_PREMIUM: { 'default': 'False', 'type': bool, 'arg': '--force-premium' },
|
||||||
ANTI_BAN_WAIT_TIME: { 'default': '1', 'type': int, 'arg': '--anti-ban-wait-time' },
|
ANTI_BAN_WAIT_TIME: { 'default': '1', 'type': int, 'arg': '--anti-ban-wait-time' },
|
||||||
OVERRIDE_AUTO_WAIT: { 'default': 'False', 'type': bool, 'arg': '--override-auto-wait' },
|
OVERRIDE_AUTO_WAIT: { 'default': 'False', 'type': bool, 'arg': '--override-auto-wait' },
|
||||||
CHUNK_SIZE: { 'default': '50000', 'type': int, 'arg': '--chunk-size' },
|
CHUNK_SIZE: { 'default': '50000', 'type': int, 'arg': '--chunk-size' },
|
||||||
SPLIT_ALBUM_DISCS: { 'default': 'False', 'type': bool, 'arg': '--split-album-discs' },
|
SPLIT_ALBUM_DISCS: { 'default': 'False', 'type': bool, 'arg': '--split-album-discs' },
|
||||||
DOWNLOAD_REAL_TIME: { 'default': 'False', 'type': bool, 'arg': '--download-real-time' },
|
DOWNLOAD_REAL_TIME: { 'default': 'False', 'type': bool, 'arg': '--download-real-time' },
|
||||||
LANGUAGE: { 'default': 'en', 'type': str, 'arg': '--language' },
|
LANGUAGE: { 'default': 'en', 'type': str, 'arg': '--language' },
|
||||||
BITRATE: { 'default': '', 'type': str, 'arg': '--bitrate' },
|
BITRATE: { 'default': '', 'type': str, 'arg': '--bitrate' },
|
||||||
SONG_ARCHIVE: { 'default': '.song_archive', 'type': str, 'arg': '--song-archive' },
|
SONG_ARCHIVE: { 'default': '.song_archive', 'type': str, 'arg': '--song-archive' },
|
||||||
CREDENTIALS_LOCATION: { 'default': 'credentials.json', 'type': str, 'arg': '--credentials-location' },
|
CREDENTIALS_LOCATION: { 'default': 'credentials.json', 'type': str, 'arg': '--credentials-location' },
|
||||||
OUTPUT: { 'default': '', 'type': str, 'arg': '--output' },
|
OUTPUT: { 'default': '', 'type': str, 'arg': '--output' },
|
||||||
PRINT_SPLASH: { 'default': 'False', 'type': bool, 'arg': '--print-splash' },
|
PRINT_SPLASH: { 'default': 'False', 'type': bool, 'arg': '--print-splash' },
|
||||||
PRINT_SKIPS: { 'default': 'True', 'type': bool, 'arg': '--print-skips' },
|
PRINT_SKIPS: { 'default': 'True', 'type': bool, 'arg': '--print-skips' },
|
||||||
PRINT_DOWNLOAD_PROGRESS: { 'default': 'True', 'type': bool, 'arg': '--print-download-progress' },
|
PRINT_DOWNLOAD_PROGRESS: { 'default': 'True', 'type': bool, 'arg': '--print-download-progress' },
|
||||||
PRINT_ERRORS: { 'default': 'True', 'type': bool, 'arg': '--print-errors' },
|
PRINT_ERRORS: { 'default': 'True', 'type': bool, 'arg': '--print-errors' },
|
||||||
PRINT_DOWNLOADS: { 'default': 'False', 'type': bool, 'arg': '--print-downloads' },
|
PRINT_DOWNLOADS: { 'default': 'False', 'type': bool, 'arg': '--print-downloads' },
|
||||||
PRINT_API_ERRORS: { 'default': 'False', 'type': bool, 'arg': '--print-api-errors' },
|
PRINT_API_ERRORS: { 'default': 'False', 'type': bool, 'arg': '--print-api-errors' },
|
||||||
PRINT_PROGRESS_INFO: { 'default': 'True', 'type': bool, 'arg': '--print-progress-info' },
|
PRINT_PROGRESS_INFO: { 'default': 'True', 'type': bool, 'arg': '--print-progress-info' },
|
||||||
PRINT_WARNINGS: { 'default': 'True', 'type': bool, 'arg': '--print-warnings' },
|
PRINT_WARNINGS: { 'default': 'True', 'type': bool, 'arg': '--print-warnings' },
|
||||||
MD_ALLGENRES: { 'default': 'False', 'type': bool, 'arg': '--md-allgenres' },
|
MD_ALLGENRES: { 'default': 'False', 'type': bool, 'arg': '--md-allgenres' },
|
||||||
MD_GENREDELIMITER: { 'default': ';', 'type': str, 'arg': '--md-genredelimiter' },
|
MD_GENREDELIMITER: { 'default': ';', 'type': str, 'arg': '--md-genredelimiter' },
|
||||||
TEMP_DOWNLOAD_DIR: { 'default': '', 'type': str, 'arg': '--temp-download-dir' }
|
TEMP_DOWNLOAD_DIR: { 'default': '', 'type': str, 'arg': '--temp-download-dir' }
|
||||||
}
|
}
|
||||||
|
|
||||||
OUTPUT_DEFAULT_PLAYLIST = '{playlist}/{artist} - {song_name}.{ext}'
|
OUTPUT_DEFAULT_PLAYLIST = '{playlist}/{artist} - {song_name}.{ext}'
|
||||||
|
@ -76,17 +77,18 @@ class Config:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, args) -> None:
|
def load(cls, args) -> None:
|
||||||
app_dir = os.path.dirname(__file__)
|
#app_dir = PurePath(__file__).parent
|
||||||
|
app_dir = Path.cwd()
|
||||||
|
|
||||||
config_fp = CONFIG_FILE_PATH
|
config_fp = CONFIG_FILE_PATH
|
||||||
if args.config_location:
|
if args.config_location:
|
||||||
config_fp = args.config_location
|
config_fp = args.config_location
|
||||||
|
|
||||||
true_config_file_path = os.path.join(app_dir, config_fp)
|
true_config_file_path = PurePath(app_dir).joinpath(config_fp)
|
||||||
|
|
||||||
# Load config from zconfig.json
|
# Load config from zconfig.json
|
||||||
|
|
||||||
if not os.path.exists(true_config_file_path):
|
if not Path(true_config_file_path).exists():
|
||||||
with open(true_config_file_path, 'w', encoding='utf-8') as config_file:
|
with open(true_config_file_path, 'w', encoding='utf-8') as config_file:
|
||||||
json.dump(cls.get_default_json(), config_file, indent=4)
|
json.dump(cls.get_default_json(), config_file, indent=4)
|
||||||
cls.Values = cls.get_default_json()
|
cls.Values = cls.get_default_json()
|
||||||
|
@ -142,11 +144,11 @@ class Config:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_root_path(cls) -> str:
|
def get_root_path(cls) -> str:
|
||||||
return os.path.join(os.path.dirname(__file__), cls.get(ROOT_PATH))
|
return PurePath(Path.cwd()).joinpath(cls.get(ROOT_PATH))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_root_podcast_path(cls) -> str:
|
def get_root_podcast_path(cls) -> str:
|
||||||
return os.path.join(os.path.dirname(__file__), cls.get(ROOT_PODCAST_PATH))
|
return PurePath(Path.cwd()).joinpath(cls.get(ROOT_PODCAST_PATH))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_skip_existing_files(cls) -> bool:
|
def get_skip_existing_files(cls) -> bool:
|
||||||
|
@ -194,17 +196,17 @@ class Config:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_song_archive(cls) -> str:
|
def get_song_archive(cls) -> str:
|
||||||
return os.path.join(cls.get_root_path(), cls.get(SONG_ARCHIVE))
|
return PurePath(cls.get_root_path()).joinpath(cls.get(SONG_ARCHIVE))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_credentials_location(cls) -> str:
|
def get_credentials_location(cls) -> str:
|
||||||
return os.path.join(os.getcwd(), cls.get(CREDENTIALS_LOCATION))
|
return PurePath(Path.cwd()).joinpath(cls.get(CREDENTIALS_LOCATION))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_temp_download_dir(cls) -> str:
|
def get_temp_download_dir(cls) -> str:
|
||||||
if cls.get(TEMP_DOWNLOAD_DIR) == '':
|
if cls.get(TEMP_DOWNLOAD_DIR) == '':
|
||||||
return ''
|
return ''
|
||||||
return os.path.join(cls.get_root_path(), cls.get(TEMP_DOWNLOAD_DIR))
|
return PurePath(cls.get_root_path()).joinpath(cls.get(TEMP_DOWNLOAD_DIR))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_all_genres(cls) -> bool:
|
def get_all_genres(cls) -> bool:
|
||||||
|
@ -221,28 +223,38 @@ class Config:
|
||||||
return v
|
return v
|
||||||
if mode == 'playlist':
|
if mode == 'playlist':
|
||||||
if cls.get_split_album_discs():
|
if cls.get_split_album_discs():
|
||||||
split = os.path.split(OUTPUT_DEFAULT_PLAYLIST)
|
# split = os.path.split(OUTPUT_DEFAULT_PLAYLIST)
|
||||||
return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
# return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
||||||
|
split = PurePath(OUTPUT_DEFAULT_PLAYLIST).parent
|
||||||
|
return PurePath(split).joinpath('Disc {disc_number}').joinpath(split)
|
||||||
return OUTPUT_DEFAULT_PLAYLIST
|
return OUTPUT_DEFAULT_PLAYLIST
|
||||||
if mode == 'extplaylist':
|
if mode == 'extplaylist':
|
||||||
if cls.get_split_album_discs():
|
if cls.get_split_album_discs():
|
||||||
split = os.path.split(OUTPUT_DEFAULT_PLAYLIST_EXT)
|
# split = os.path.split(OUTPUT_DEFAULT_PLAYLIST_EXT)
|
||||||
return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
# return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
||||||
|
split = PurePath(OUTPUT_DEFAULT_PLAYLIST_EXT).parent
|
||||||
|
return PurePath(split).joinpath('Disc {disc_number}').joinpath(split)
|
||||||
return OUTPUT_DEFAULT_PLAYLIST_EXT
|
return OUTPUT_DEFAULT_PLAYLIST_EXT
|
||||||
if mode == 'liked':
|
if mode == 'liked':
|
||||||
if cls.get_split_album_discs():
|
if cls.get_split_album_discs():
|
||||||
split = os.path.split(OUTPUT_DEFAULT_LIKED_SONGS)
|
# split = os.path.split(OUTPUT_DEFAULT_LIKED_SONGS)
|
||||||
return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
# return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
||||||
|
split = PurePath(OUTPUT_DEFAULT_LIKED_SONGS).parent
|
||||||
|
return PurePath(split).joinpath('Disc {disc_number}').joinpath(split)
|
||||||
return OUTPUT_DEFAULT_LIKED_SONGS
|
return OUTPUT_DEFAULT_LIKED_SONGS
|
||||||
if mode == 'single':
|
if mode == 'single':
|
||||||
if cls.get_split_album_discs():
|
if cls.get_split_album_discs():
|
||||||
split = os.path.split(OUTPUT_DEFAULT_SINGLE)
|
# split = os.path.split(OUTPUT_DEFAULT_SINGLE)
|
||||||
return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
# return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
||||||
|
split = PurePath(OUTPUT_DEFAULT_SINGLE).parent
|
||||||
|
return PurePath(split).joinpath('Disc {disc_number}').joinpath(split)
|
||||||
return OUTPUT_DEFAULT_SINGLE
|
return OUTPUT_DEFAULT_SINGLE
|
||||||
if mode == 'album':
|
if mode == 'album':
|
||||||
if cls.get_split_album_discs():
|
if cls.get_split_album_discs():
|
||||||
split = os.path.split(OUTPUT_DEFAULT_ALBUM)
|
# split = os.path.split(OUTPUT_DEFAULT_ALBUM)
|
||||||
return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
# return os.path.join(split[0], 'Disc {disc_number}', split[0])
|
||||||
|
split = PurePath(OUTPUT_DEFAULT_ALBUM).parent
|
||||||
|
return PurePath(split).joinpath('Disc {disc_number}').joinpath(split)
|
||||||
return OUTPUT_DEFAULT_ALBUM
|
return OUTPUT_DEFAULT_ALBUM
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from shutil import get_terminal_size
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from termoutput import Printer
|
from zotify.termoutput import Printer
|
||||||
|
|
||||||
|
|
||||||
class Loader:
|
class Loader:
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from const import ITEMS, ID, TRACK, NAME
|
from zotify.const import ITEMS, ID, TRACK, NAME
|
||||||
from termoutput import Printer
|
from zotify.termoutput import Printer
|
||||||
from track import download_track
|
from zotify.track import download_track
|
||||||
from utils import split_input
|
from zotify.utils import split_input
|
||||||
from zotify import Zotify
|
from zotify.zotify import Zotify
|
||||||
|
|
||||||
MY_PLAYLISTS_URL = 'https://api.spotify.com/v1/me/playlists'
|
MY_PLAYLISTS_URL = 'https://api.spotify.com/v1/me/playlists'
|
||||||
PLAYLISTS_URL = 'https://api.spotify.com/v1/playlists'
|
PLAYLISTS_URL = 'https://api.spotify.com/v1/playlists'
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import os
|
# import os
|
||||||
|
from pathlib import PurePath, Path
|
||||||
import time
|
import time
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
from librespot.metadata import EpisodeId
|
from librespot.metadata import EpisodeId
|
||||||
|
|
||||||
from const import ERROR, ID, ITEMS, NAME, SHOW, DURATION_MS
|
from zotify.const import ERROR, ID, ITEMS, NAME, SHOW, DURATION_MS
|
||||||
from termoutput import PrintChannel, Printer
|
from zotify.termoutput import PrintChannel, Printer
|
||||||
from utils import create_download_directory, fix_filename
|
from zotify.utils import create_download_directory, fix_filename
|
||||||
from zotify import Zotify
|
from zotify.zotify import Zotify
|
||||||
from loader import Loader
|
from zotify.loader import Loader
|
||||||
|
|
||||||
|
|
||||||
EPISODE_INFO_URL = 'https://api.spotify.com/v1/episodes'
|
EPISODE_INFO_URL = 'https://api.spotify.com/v1/episodes'
|
||||||
|
@ -46,7 +47,7 @@ def get_show_episodes(show_id_str) -> list:
|
||||||
|
|
||||||
def download_podcast_directly(url, filename):
|
def download_podcast_directly(url, filename):
|
||||||
import functools
|
import functools
|
||||||
import pathlib
|
# import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
import requests
|
import requests
|
||||||
from tqdm.auto import tqdm
|
from tqdm.auto import tqdm
|
||||||
|
@ -58,7 +59,8 @@ def download_podcast_directly(url, filename):
|
||||||
f"Request to {url} returned status code {r.status_code}")
|
f"Request to {url} returned status code {r.status_code}")
|
||||||
file_size = int(r.headers.get('Content-Length', 0))
|
file_size = int(r.headers.get('Content-Length', 0))
|
||||||
|
|
||||||
path = pathlib.Path(filename).expanduser().resolve()
|
# path = pathlib.Path(filename).expanduser().resolve()
|
||||||
|
path = Path(filename).expanduser().resolve()
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
desc = "(Unknown total file size)" if file_size == 0 else ""
|
desc = "(Unknown total file size)" if file_size == 0 else ""
|
||||||
|
@ -86,8 +88,8 @@ def download_episode(episode_id) -> None:
|
||||||
direct_download_url = Zotify.invoke_url(
|
direct_download_url = Zotify.invoke_url(
|
||||||
'https://api-partner.spotify.com/pathfinder/v1/query?operationName=getEpisode&variables={"uri":"spotify:episode:' + episode_id + '"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"224ba0fd89fcfdfb3a15fa2d82a6112d3f4e2ac88fba5c6713de04d1b72cf482"}}')[1]["data"]["episode"]["audio"]["items"][-1]["url"]
|
'https://api-partner.spotify.com/pathfinder/v1/query?operationName=getEpisode&variables={"uri":"spotify:episode:' + episode_id + '"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"224ba0fd89fcfdfb3a15fa2d82a6112d3f4e2ac88fba5c6713de04d1b72cf482"}}')[1]["data"]["episode"]["audio"]["items"][-1]["url"]
|
||||||
|
|
||||||
download_directory = os.path.join(Zotify.CONFIG.get_root_podcast_path(), extra_paths)
|
download_directory = PurePath(Zotify.CONFIG.get_root_podcast_path()).joinpath(extra_paths)
|
||||||
download_directory = os.path.realpath(download_directory)
|
# download_directory = os.path.realpath(download_directory)
|
||||||
create_download_directory(download_directory)
|
create_download_directory(download_directory)
|
||||||
|
|
||||||
if "anon-podcast.scdn.co" in direct_download_url:
|
if "anon-podcast.scdn.co" in direct_download_url:
|
||||||
|
@ -97,10 +99,10 @@ def download_episode(episode_id) -> None:
|
||||||
|
|
||||||
total_size = stream.input_stream.size
|
total_size = stream.input_stream.size
|
||||||
|
|
||||||
filepath = os.path.join(download_directory, f"{filename}.ogg")
|
filepath = PurePath(download_directory).joinpath(f"{filename}.ogg")
|
||||||
if (
|
if (
|
||||||
os.path.isfile(filepath)
|
Path(filepath).isfile()
|
||||||
and os.path.getsize(filepath) == total_size
|
and Path(filepath).stat().st_size == total_size
|
||||||
and Zotify.CONFIG.get_skip_existing_files()
|
and Zotify.CONFIG.get_skip_existing_files()
|
||||||
):
|
):
|
||||||
Printer.print(PrintChannel.SKIPS, "\n### SKIPPING: " + podcast_name + " - " + episode_name + " (EPISODE ALREADY EXISTS) ###")
|
Printer.print(PrintChannel.SKIPS, "\n### SKIPPING: " + podcast_name + " - " + episode_name + " (EPISODE ALREADY EXISTS) ###")
|
||||||
|
@ -128,7 +130,7 @@ def download_episode(episode_id) -> None:
|
||||||
if delta_want > delta_real:
|
if delta_want > delta_real:
|
||||||
time.sleep(delta_want - delta_real)
|
time.sleep(delta_want - delta_real)
|
||||||
else:
|
else:
|
||||||
filepath = os.path.join(download_directory, f"{filename}.mp3")
|
filepath = PurePath(download_directory).joinpath(f"{filename}.mp3")
|
||||||
download_podcast_directly(direct_download_url, filepath)
|
download_podcast_directly(direct_download_url, filepath)
|
||||||
|
|
||||||
prepare_download_loader.stop()
|
prepare_download_loader.stop()
|
||||||
|
|
|
@ -2,8 +2,8 @@ import sys
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from config import *
|
from zotify.config import *
|
||||||
from zotify import Zotify
|
from zotify.zotify import Zotify
|
||||||
|
|
||||||
|
|
||||||
class PrintChannel(Enum):
|
class PrintChannel(Enum):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
# import os
|
||||||
|
from pathlib import Path, PurePath
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
@ -8,14 +9,14 @@ from librespot.audio.decoders import AudioQuality
|
||||||
from librespot.metadata import TrackId
|
from librespot.metadata import TrackId
|
||||||
from ffmpy import FFmpeg
|
from ffmpy import FFmpeg
|
||||||
|
|
||||||
from const import TRACKS, ALBUM, GENRES, NAME, ITEMS, DISC_NUMBER, TRACK_NUMBER, IS_PLAYABLE, ARTISTS, IMAGES, URL, \
|
from zotify.const import TRACKS, ALBUM, GENRES, NAME, ITEMS, DISC_NUMBER, TRACK_NUMBER, IS_PLAYABLE, ARTISTS, IMAGES, URL, \
|
||||||
RELEASE_DATE, ID, TRACKS_URL, SAVED_TRACKS_URL, TRACK_STATS_URL, CODEC_MAP, EXT_MAP, DURATION_MS, HREF
|
RELEASE_DATE, ID, TRACKS_URL, SAVED_TRACKS_URL, TRACK_STATS_URL, CODEC_MAP, EXT_MAP, DURATION_MS, HREF
|
||||||
from termoutput import Printer, PrintChannel
|
from zotify.termoutput import Printer, PrintChannel
|
||||||
from utils import fix_filename, set_audio_tags, set_music_thumbnail, create_download_directory, \
|
from zotify.utils import fix_filename, set_audio_tags, set_music_thumbnail, create_download_directory, \
|
||||||
get_directory_song_ids, add_to_directory_song_ids, get_previously_downloaded, add_to_archive, fmt_seconds
|
get_directory_song_ids, add_to_directory_song_ids, get_previously_downloaded, add_to_archive, fmt_seconds
|
||||||
from zotify import Zotify
|
from zotify.zotify import Zotify
|
||||||
import traceback
|
import traceback
|
||||||
from loader import Loader
|
from zotify.loader import Loader
|
||||||
|
|
||||||
|
|
||||||
def get_saved_tracks() -> list:
|
def get_saved_tracks() -> list:
|
||||||
|
@ -136,25 +137,27 @@ def download_track(mode: str, track_id: str, extra_keys=None, disable_progressba
|
||||||
output_template = output_template.replace("{track_id}", fix_filename(track_id))
|
output_template = output_template.replace("{track_id}", fix_filename(track_id))
|
||||||
output_template = output_template.replace("{ext}", ext)
|
output_template = output_template.replace("{ext}", ext)
|
||||||
|
|
||||||
filename = os.path.join(Zotify.CONFIG.get_root_path(), output_template)
|
filename = PurePath(Zotify.CONFIG.get_root_path()).joinpath(output_template)
|
||||||
filedir = os.path.dirname(filename)
|
filedir = PurePath(filename).parent
|
||||||
|
|
||||||
filename_temp = filename
|
filename_temp = filename
|
||||||
if Zotify.CONFIG.get_temp_download_dir() != '':
|
if Zotify.CONFIG.get_temp_download_dir() != '':
|
||||||
filename_temp = os.path.join(Zotify.CONFIG.get_temp_download_dir(), f'zotify_{str(uuid.uuid4())}_{track_id}.{ext}')
|
filename_temp = PurePath(Zotify.CONFIG.get_temp_download_dir()).joinpath(f'zotify_{str(uuid.uuid4())}_{track_id}.{ext}')
|
||||||
|
|
||||||
check_name = os.path.isfile(filename) and os.path.getsize(filename)
|
check_name = Path(filename).is_file() and Path(filename).stat().st_size
|
||||||
check_id = scraped_song_id in get_directory_song_ids(filedir)
|
check_id = scraped_song_id in get_directory_song_ids(filedir)
|
||||||
check_all_time = scraped_song_id in get_previously_downloaded()
|
check_all_time = scraped_song_id in get_previously_downloaded()
|
||||||
|
|
||||||
# a song with the same name is installed
|
# a song with the same name is installed
|
||||||
if not check_id and check_name:
|
if not check_id and check_name:
|
||||||
c = len([file for file in os.listdir(filedir) if re.search(f'^{filename}_', str(file))]) + 1
|
c = len([file for file in Path(filedir).iterdir() if re.search(f'^{filename}_', str(file))]) + 1
|
||||||
|
|
||||||
fname = os.path.splitext(os.path.basename(filename))[0]
|
# fname = os.path.splitext(os.path.basename(filename))[0]
|
||||||
ext = os.path.splitext(os.path.basename(filename))[1]
|
# ext = os.path.splitext(os.path.basename(filename))[1]
|
||||||
|
fname = PurePath(PurePath(filename).name).parent
|
||||||
|
ext = PurePath(PurePath(filename).name).suffix
|
||||||
|
|
||||||
filename = os.path.join(filedir, f'{fname}_{c}{ext}')
|
filename = PurePath(filedir).joinpath(f'{fname}_{c}{ext}')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Printer.print(PrintChannel.ERRORS, '### SKIPPING SONG - FAILED TO QUERY METADATA ###')
|
Printer.print(PrintChannel.ERRORS, '### SKIPPING SONG - FAILED TO QUERY METADATA ###')
|
||||||
|
@ -218,18 +221,18 @@ def download_track(mode: str, track_id: str, extra_keys=None, disable_progressba
|
||||||
set_music_thumbnail(filename_temp, image_url)
|
set_music_thumbnail(filename_temp, image_url)
|
||||||
|
|
||||||
if filename_temp != filename:
|
if filename_temp != filename:
|
||||||
os.rename(filename_temp, filename)
|
Path(filename_temp).rename(filename)
|
||||||
|
|
||||||
time_finished = time.time()
|
time_finished = time.time()
|
||||||
|
|
||||||
Printer.print(PrintChannel.DOWNLOADS, f'### Downloaded "{song_name}" to "{os.path.relpath(filename, Zotify.CONFIG.get_root_path())}" in {fmt_seconds(time_downloaded - time_start)} (plus {fmt_seconds(time_finished - time_downloaded)} converting) ###' + "\n")
|
Printer.print(PrintChannel.DOWNLOADS, f'### Downloaded "{song_name}" to "{Path(filename).relative_to(Zotify.CONFIG.get_root_path())}" in {fmt_seconds(time_downloaded - time_start)} (plus {fmt_seconds(time_finished - time_downloaded)} converting) ###' + "\n")
|
||||||
|
|
||||||
# add song id to archive file
|
# add song id to archive file
|
||||||
if Zotify.CONFIG.get_skip_previously_downloaded():
|
if Zotify.CONFIG.get_skip_previously_downloaded():
|
||||||
add_to_archive(scraped_song_id, os.path.basename(filename), artists[0], name)
|
add_to_archive(scraped_song_id, PurePath(filename).name, artists[0], name)
|
||||||
# add song id to download directory's .song_ids file
|
# add song id to download directory's .song_ids file
|
||||||
if not check_id:
|
if not check_id:
|
||||||
add_to_directory_song_ids(filedir, scraped_song_id, os.path.basename(filename), artists[0], name)
|
add_to_directory_song_ids(filedir, scraped_song_id, PurePath(filename).name, artists[0], name)
|
||||||
|
|
||||||
if not Zotify.CONFIG.get_anti_ban_wait_time():
|
if not Zotify.CONFIG.get_anti_ban_wait_time():
|
||||||
time.sleep(Zotify.CONFIG.get_anti_ban_wait_time())
|
time.sleep(Zotify.CONFIG.get_anti_ban_wait_time())
|
||||||
|
@ -241,16 +244,17 @@ def download_track(mode: str, track_id: str, extra_keys=None, disable_progressba
|
||||||
Printer.print(PrintChannel.ERRORS, "\n")
|
Printer.print(PrintChannel.ERRORS, "\n")
|
||||||
Printer.print(PrintChannel.ERRORS, str(e) + "\n")
|
Printer.print(PrintChannel.ERRORS, str(e) + "\n")
|
||||||
Printer.print(PrintChannel.ERRORS, "".join(traceback.TracebackException.from_exception(e).format()) + "\n")
|
Printer.print(PrintChannel.ERRORS, "".join(traceback.TracebackException.from_exception(e).format()) + "\n")
|
||||||
if os.path.exists(filename_temp):
|
if Path(filename_temp).exists():
|
||||||
os.remove(filename_temp)
|
Path(filename_temp).unlink()
|
||||||
|
|
||||||
prepare_download_loader.stop()
|
prepare_download_loader.stop()
|
||||||
|
|
||||||
|
|
||||||
def convert_audio_format(filename) -> None:
|
def convert_audio_format(filename) -> None:
|
||||||
""" Converts raw audio into playable file """
|
""" Converts raw audio into playable file """
|
||||||
temp_filename = f'{os.path.splitext(filename)[0]}.tmp'
|
# temp_filename = f'{os.path.splitext(filename)[0]}.tmp'
|
||||||
os.replace(filename, temp_filename)
|
temp_filename = f'{PurePath(filename).parent}.tmp'
|
||||||
|
Path(filename).replace(temp_filename)
|
||||||
|
|
||||||
download_format = Zotify.CONFIG.get_download_format().lower()
|
download_format = Zotify.CONFIG.get_download_format().lower()
|
||||||
file_codec = CODEC_MAP.get(download_format, 'copy')
|
file_codec = CODEC_MAP.get(download_format, 'copy')
|
||||||
|
@ -277,5 +281,5 @@ def convert_audio_format(filename) -> None:
|
||||||
with Loader(PrintChannel.PROGRESS_INFO, "Converting file..."):
|
with Loader(PrintChannel.PROGRESS_INFO, "Converting file..."):
|
||||||
ff_m.run()
|
ff_m.run()
|
||||||
|
|
||||||
if os.path.exists(temp_filename):
|
if Path(temp_filename).exists():
|
||||||
os.remove(temp_filename)
|
Path(temp_filename).unlink()
|
||||||
|
|
|
@ -5,14 +5,15 @@ import platform
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from pathlib import Path, PurePath
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
import music_tag
|
import music_tag
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from const import ARTIST, GENRE, TRACKTITLE, ALBUM, YEAR, DISCNUMBER, TRACKNUMBER, ARTWORK, \
|
from zotify.const import ARTIST, GENRE, TRACKTITLE, ALBUM, YEAR, DISCNUMBER, TRACKNUMBER, ARTWORK, \
|
||||||
WINDOWS_SYSTEM, ALBUMARTIST
|
WINDOWS_SYSTEM, ALBUMARTIST
|
||||||
from zotify import Zotify
|
from zotify.zotify import Zotify
|
||||||
|
|
||||||
|
|
||||||
class MusicFormat(str, Enum):
|
class MusicFormat(str, Enum):
|
||||||
|
@ -22,11 +23,12 @@ class MusicFormat(str, Enum):
|
||||||
|
|
||||||
def create_download_directory(download_path: str) -> None:
|
def create_download_directory(download_path: str) -> None:
|
||||||
""" Create directory and add a hidden file with song ids """
|
""" Create directory and add a hidden file with song ids """
|
||||||
os.makedirs(download_path, exist_ok=True)
|
# os.makedirs(download_path, exist_ok=True)
|
||||||
|
Path(download_path).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# add hidden file with song ids
|
# add hidden file with song ids
|
||||||
hidden_file_path = os.path.join(download_path, '.song_ids')
|
hidden_file_path = PurePath(download_path).joinpath('.song_ids')
|
||||||
if not os.path.isfile(hidden_file_path):
|
if not Path(hidden_file_path).is_file():
|
||||||
with open(hidden_file_path, 'w', encoding='utf-8') as f:
|
with open(hidden_file_path, 'w', encoding='utf-8') as f:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -37,7 +39,7 @@ def get_previously_downloaded() -> List[str]:
|
||||||
ids = []
|
ids = []
|
||||||
archive_path = Zotify.CONFIG.get_song_archive()
|
archive_path = Zotify.CONFIG.get_song_archive()
|
||||||
|
|
||||||
if os.path.exists(archive_path):
|
if Path(archive_path).exists():
|
||||||
with open(archive_path, 'r', encoding='utf-8') as f:
|
with open(archive_path, 'r', encoding='utf-8') as f:
|
||||||
ids = [line.strip().split('\t')[0] for line in f.readlines()]
|
ids = [line.strip().split('\t')[0] for line in f.readlines()]
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ def add_to_archive(song_id: str, filename: str, author_name: str, song_name: str
|
||||||
|
|
||||||
archive_path = Zotify.CONFIG.get_song_archive()
|
archive_path = Zotify.CONFIG.get_song_archive()
|
||||||
|
|
||||||
if os.path.exists(archive_path):
|
if Path(archive_path).exists():
|
||||||
with open(archive_path, 'a', encoding='utf-8') as file:
|
with open(archive_path, 'a', encoding='utf-8') as file:
|
||||||
file.write(f'{song_id}\t{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\t{author_name}\t{song_name}\t{filename}\n')
|
file.write(f'{song_id}\t{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\t{author_name}\t{song_name}\t{filename}\n')
|
||||||
else:
|
else:
|
||||||
|
@ -62,8 +64,8 @@ def get_directory_song_ids(download_path: str) -> List[str]:
|
||||||
|
|
||||||
song_ids = []
|
song_ids = []
|
||||||
|
|
||||||
hidden_file_path = os.path.join(download_path, '.song_ids')
|
hidden_file_path = PurePath(download_path).joinpath('.song_ids')
|
||||||
if os.path.isfile(hidden_file_path):
|
if Path(hidden_file_path).is_file():
|
||||||
with open(hidden_file_path, 'r', encoding='utf-8') as file:
|
with open(hidden_file_path, 'r', encoding='utf-8') as file:
|
||||||
song_ids.extend([line.strip().split('\t')[0] for line in file.readlines()])
|
song_ids.extend([line.strip().split('\t')[0] for line in file.readlines()])
|
||||||
|
|
||||||
|
@ -73,7 +75,7 @@ def get_directory_song_ids(download_path: str) -> List[str]:
|
||||||
def add_to_directory_song_ids(download_path: str, song_id: str, filename: str, author_name: str, song_name: str) -> None:
|
def add_to_directory_song_ids(download_path: str, song_id: str, filename: str, author_name: str, song_name: str) -> None:
|
||||||
""" Appends song_id to .song_ids file in directory """
|
""" Appends song_id to .song_ids file in directory """
|
||||||
|
|
||||||
hidden_file_path = os.path.join(download_path, '.song_ids')
|
hidden_file_path = PurePath(download_path).joinpath('.song_ids')
|
||||||
# not checking if file exists because we need an exception
|
# not checking if file exists because we need an exception
|
||||||
# to be raised if something is wrong
|
# to be raised if something is wrong
|
||||||
with open(hidden_file_path, 'a', encoding='utf-8') as file:
|
with open(hidden_file_path, 'a', encoding='utf-8') as file:
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
from pathlib import Path
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
import time
|
import time
|
||||||
import requests
|
import requests
|
||||||
from librespot.audio.decoders import VorbisOnlyAudioQuality
|
from librespot.audio.decoders import VorbisOnlyAudioQuality
|
||||||
from librespot.core import Session
|
from librespot.core import Session
|
||||||
|
|
||||||
from const import TYPE, \
|
from zotify.const import TYPE, \
|
||||||
PREMIUM, USER_READ_EMAIL, OFFSET, LIMIT, \
|
PREMIUM, USER_READ_EMAIL, OFFSET, LIMIT, \
|
||||||
PLAYLIST_READ_PRIVATE, USER_LIBRARY_READ
|
PLAYLIST_READ_PRIVATE, USER_LIBRARY_READ
|
||||||
from config import Config
|
from zotify.config import Config
|
||||||
|
|
||||||
class Zotify:
|
class Zotify:
|
||||||
SESSION: Session = None
|
SESSION: Session = None
|
||||||
|
@ -26,7 +27,7 @@ class Zotify:
|
||||||
|
|
||||||
cred_location = Config.get_credentials_location()
|
cred_location = Config.get_credentials_location()
|
||||||
|
|
||||||
if os.path.isfile(cred_location):
|
if Path(cred_location).is_file():
|
||||||
try:
|
try:
|
||||||
cls.SESSION = Session.Builder().stored_file(cred_location).create()
|
cls.SESSION = Session.Builder().stored_file(cred_location).create()
|
||||||
return
|
return
|
||||||
|
@ -75,7 +76,7 @@ class Zotify:
|
||||||
@classmethod
|
@classmethod
|
||||||
def invoke_url(cls, url, tryCount=0):
|
def invoke_url(cls, url, tryCount=0):
|
||||||
# we need to import that here, otherwise we will get circular imports!
|
# we need to import that here, otherwise we will get circular imports!
|
||||||
from termoutput import Printer, PrintChannel
|
from zotify.termoutput import Printer, PrintChannel
|
||||||
headers = cls.get_auth_header()
|
headers = cls.get_auth_header()
|
||||||
response = requests.get(url, headers=headers)
|
response = requests.get(url, headers=headers)
|
||||||
responsetext = response.text
|
responsetext = response.text
|
||||||
|
|
Loading…
Add table
Reference in a new issue