forked from CopyBot/sptnr
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f3fe277017 | |||
| 1396d6d3ed |
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Ensure the script stops if there is an error
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Read version from the VERSION file
|
||||||
|
VERSION=$(cat VERSION)
|
||||||
|
|
||||||
|
# Build the Docker image with the version tag
|
||||||
|
docker build -t krestaino/sptnr:$VERSION .
|
||||||
|
|
||||||
|
# Tag the built image as latest
|
||||||
|
docker tag krestaino/sptnr:$VERSION krestaino/sptnr:latest
|
||||||
|
|
||||||
|
# Push both tags to the Docker registry
|
||||||
|
docker push krestaino/sptnr:$VERSION
|
||||||
|
docker push krestaino/sptnr:latest
|
||||||
|
|
||||||
|
echo "Docker images tagged and pushed: $VERSION and latest"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
with open('VERSION', 'r') as file:
|
with open("VERSION", "r") as file:
|
||||||
__version__ = file.read().strip()
|
__version__ = file.read().strip()
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -30,28 +30,7 @@ NAV_PASS = os.getenv("NAV_PASS")
|
|||||||
SPOTIFY_CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID")
|
SPOTIFY_CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID")
|
||||||
SPOTIFY_CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET")
|
SPOTIFY_CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET")
|
||||||
|
|
||||||
# Setup logs
|
# Colors
|
||||||
LOG_DIR = "logs"
|
|
||||||
if not os.path.exists(LOG_DIR):
|
|
||||||
os.makedirs(LOG_DIR)
|
|
||||||
|
|
||||||
LOGFILE = os.path.join(LOG_DIR, f"spotify-popularity_{int(time.time())}.log")
|
|
||||||
|
|
||||||
# Auth
|
|
||||||
HEX_ENCODED_PASS = NAV_PASS.encode().hex()
|
|
||||||
TOKEN_AUTH = base64.b64encode(
|
|
||||||
f"{SPOTIFY_CLIENT_ID}:{SPOTIFY_CLIENT_SECRET}".encode()
|
|
||||||
).decode()
|
|
||||||
TOKEN_URL = "https://accounts.spotify.com/api/token"
|
|
||||||
response = requests.post(
|
|
||||||
TOKEN_URL,
|
|
||||||
headers={"Authorization": f"Basic {TOKEN_AUTH}"},
|
|
||||||
data={"grant_type": "client_credentials"},
|
|
||||||
)
|
|
||||||
SPOTIFY_TOKEN = json.loads(response.text)["access_token"]
|
|
||||||
|
|
||||||
init(autoreset=True)
|
|
||||||
|
|
||||||
LIGHT_PURPLE = Fore.MAGENTA + Style.BRIGHT
|
LIGHT_PURPLE = Fore.MAGENTA + Style.BRIGHT
|
||||||
LIGHT_GREEN = Fore.GREEN + Style.BRIGHT
|
LIGHT_GREEN = Fore.GREEN + Style.BRIGHT
|
||||||
LIGHT_RED = Fore.RED + Style.BRIGHT
|
LIGHT_RED = Fore.RED + Style.BRIGHT
|
||||||
@@ -61,22 +40,14 @@ LIGHT_YELLOW = Fore.YELLOW + Style.BRIGHT
|
|||||||
BOLD = Style.BRIGHT
|
BOLD = Style.BRIGHT
|
||||||
RESET = Style.RESET_ALL
|
RESET = Style.RESET_ALL
|
||||||
|
|
||||||
# Default flags
|
# Setup logs
|
||||||
PREVIEW = 0
|
LOG_DIR = "logs"
|
||||||
START = 0
|
if not os.path.exists(LOG_DIR):
|
||||||
LIMIT = 0
|
os.makedirs(LOG_DIR)
|
||||||
ARTIST_IDs = []
|
|
||||||
ALBUM_IDs = []
|
|
||||||
|
|
||||||
# Variables
|
LOGFILE = os.path.join(LOG_DIR, f"spotify-popularity_{int(time.time())}.log")
|
||||||
ARTISTS_PROCESSED = 0
|
|
||||||
TOTAL_TRACKS = 0
|
|
||||||
FOUND_AND_UPDATED = 0
|
|
||||||
NOT_FOUND = 0
|
|
||||||
UNMATCHED_TRACKS = []
|
|
||||||
|
|
||||||
|
|
||||||
# Setup logging
|
|
||||||
class NoColorFormatter(logging.Formatter):
|
class NoColorFormatter(logging.Formatter):
|
||||||
ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]")
|
ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]")
|
||||||
|
|
||||||
@@ -95,6 +66,44 @@ file_handler = logging.FileHandler(LOGFILE, "a")
|
|||||||
file_handler.setFormatter(NoColorFormatter("[%(asctime)s] %(message)s"))
|
file_handler.setFormatter(NoColorFormatter("[%(asctime)s] %(message)s"))
|
||||||
logging.getLogger().addHandler(file_handler)
|
logging.getLogger().addHandler(file_handler)
|
||||||
|
|
||||||
|
# Auth
|
||||||
|
HEX_ENCODED_PASS = NAV_PASS.encode().hex()
|
||||||
|
TOKEN_AUTH = base64.b64encode(
|
||||||
|
f"{SPOTIFY_CLIENT_ID}:{SPOTIFY_CLIENT_SECRET}".encode()
|
||||||
|
).decode()
|
||||||
|
TOKEN_URL = "https://accounts.spotify.com/api/token"
|
||||||
|
response = requests.post(
|
||||||
|
TOKEN_URL,
|
||||||
|
headers={"Authorization": f"Basic {TOKEN_AUTH}"},
|
||||||
|
data={"grant_type": "client_credentials"},
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
error_info = response.json() # Assuming the error response is in JSON format
|
||||||
|
error_description = error_info.get("error_description", "Unknown error")
|
||||||
|
logging.error(
|
||||||
|
f"{LIGHT_RED}Spotify Authentication Error: {error_description}{RESET}"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
SPOTIFY_TOKEN = response.json()["access_token"]
|
||||||
|
|
||||||
|
init(autoreset=True)
|
||||||
|
|
||||||
|
# Default flags
|
||||||
|
PREVIEW = 0
|
||||||
|
START = 0
|
||||||
|
LIMIT = 0
|
||||||
|
ARTIST_IDs = []
|
||||||
|
ALBUM_IDs = []
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
ARTISTS_PROCESSED = 0
|
||||||
|
TOTAL_TRACKS = 0
|
||||||
|
FOUND_AND_UPDATED = 0
|
||||||
|
NOT_FOUND = 0
|
||||||
|
UNMATCHED_TRACKS = []
|
||||||
|
|
||||||
# Parse arguments
|
# Parse arguments
|
||||||
description_text = "process command-line flags for sync"
|
description_text = "process command-line flags for sync"
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@@ -134,6 +143,11 @@ parser.add_argument(
|
|||||||
help="limit to processing [NUM] artists from the start index",
|
help="limit to processing [NUM] artists from the start index",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-v", "--version", action="version", version=f"%(prog)s {__version__}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
ARTIST_IDs = args.artist if args.artist else []
|
ARTIST_IDs = args.artist if args.artist else []
|
||||||
@@ -141,6 +155,8 @@ ALBUM_IDs = args.album if args.album else []
|
|||||||
START = args.start
|
START = args.start
|
||||||
LIMIT = args.limit
|
LIMIT = args.limit
|
||||||
|
|
||||||
|
logging.info(f"{BOLD}Version:{RESET} {LIGHT_YELLOW}sptnr v{__version__}{RESET}")
|
||||||
|
|
||||||
if args.preview:
|
if args.preview:
|
||||||
logging.info(f"{LIGHT_YELLOW}Preview mode, no changes will be made.{RESET}")
|
logging.info(f"{LIGHT_YELLOW}Preview mode, no changes will be made.{RESET}")
|
||||||
PREVIEW = 1
|
PREVIEW = 1
|
||||||
@@ -159,6 +175,20 @@ if not args.preview:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_url(url):
|
||||||
|
if not re.match(r"https?://", url):
|
||||||
|
logging.error(
|
||||||
|
f"{LIGHT_RED}Config Error: URL must start with 'http://' or 'https://'.{RESET}"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
if url.endswith("/"):
|
||||||
|
logging.error(
|
||||||
|
f"{LIGHT_RED}Config Error: URL must not end with a trailing slash.{RESET}"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def url_encode(string):
|
def url_encode(string):
|
||||||
return urllib.parse.quote_plus(string)
|
return urllib.parse.quote_plus(string)
|
||||||
|
|
||||||
@@ -187,24 +217,24 @@ def process_track(track_id, artist_name, album, track_name):
|
|||||||
try:
|
try:
|
||||||
response = requests.get(spotify_url, headers=headers)
|
response = requests.get(spotify_url, headers=headers)
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
logging.error(f"{LIGHT_RED}Error: Unable to reach server.{RESET}")
|
logging.error(f"{LIGHT_RED}Spotify Error: Unable to reach server.{RESET}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
if response.status_code == 429:
|
if response.status_code == 429:
|
||||||
logging.error(
|
logging.error(
|
||||||
f"{LIGHT_RED}Error {response.status_code}: Retry after {BOLD}{response.headers.get('Retry-After', 'some time')}s{RESET}"
|
f"{LIGHT_RED}Spotify Error {response.status_code}: Retry after {BOLD}{response.headers.get('Retry-After', 'some time')}s{RESET}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logging.error(
|
||||||
f"{LIGHT_RED}Error {response.status_code}: {response.text}{RESET}"
|
f"{LIGHT_RED}Spotify Error {response.status_code}: {response.text}{RESET}"
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
return response.json()
|
return response.json()
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logging.error(
|
logging.error(
|
||||||
f"{LIGHT_RED}Error decoding JSON from Spotify API: {e}{RESET}"
|
f"{LIGHT_RED}Spotify Error: Error decoding JSON from Spotify API: {e}{RESET}"
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -285,9 +315,47 @@ def process_artist(artist_id):
|
|||||||
|
|
||||||
|
|
||||||
def fetch_data(url):
|
def fetch_data(url):
|
||||||
response = requests.get(url)
|
try:
|
||||||
return json.loads(response.text)["subsonic-response"]
|
response = requests.get(url)
|
||||||
|
response_data = json.loads(response.text)
|
||||||
|
|
||||||
|
if "subsonic-response" not in response_data:
|
||||||
|
logging.error(
|
||||||
|
f"{LIGHT_RED}Unexpected response format from Navidrome.{RESET}"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
nav_response = response_data["subsonic-response"]
|
||||||
|
|
||||||
|
if "error" in nav_response:
|
||||||
|
error_message = nav_response["error"].get("message", "Unknown error")
|
||||||
|
logging.error(f"{LIGHT_RED}Navidrome Error: {error_message}{RESET}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return nav_response
|
||||||
|
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
logging.error(
|
||||||
|
f"{LIGHT_RED}Connection Error: Failed to connect to the provided URL. Please check if the URL is correct and the server is reachable.{RESET}"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logging.error(
|
||||||
|
f"{LIGHT_RED}Connection Error: An error occurred while trying to connect to Navidrome: {e}{RESET}"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
logging.error(
|
||||||
|
f"{LIGHT_RED}JSON Parsing Error: Failed to parse JSON response from Navidrome. Please check if the provided URL is a valid Navidrome server.{RESET}"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate_url(NAV_BASE_URL)
|
||||||
|
except ValueError as e:
|
||||||
|
logging.error(f"{LIGHT_RED}{e}{RESET}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if ARTIST_IDs:
|
if ARTIST_IDs:
|
||||||
for ARTIST_ID in ARTIST_IDs:
|
for ARTIST_ID in ARTIST_IDs:
|
||||||
|
|||||||
Reference in New Issue
Block a user