travail d'un we (j'en ai big marre)

This commit is contained in:
2025-11-23 23:39:43 +01:00
parent 1ff888cf34
commit 4c486199ed
69 changed files with 208 additions and 111 deletions

1
old/.cache Normal file
View File

@@ -0,0 +1 @@
{"access_token": "BQBR3Dnu7QMSOnq8G_8lOiz858WKJmMxiCgskqjp3QDu17EcQWx3Y8ALVQGSXzUcTgkJb-z5oHjJxEQJNfz8tSYREfJGGMKQfoCo0P2tCUbnB0wdGm7iSmWOlYuvrnDTVleJ9nJBGG6vVGc2ZEIl43T54_TmKbkPzampV8FuuGIATUvzbjRO6uEHqzc3ImuBELCZJjj0n8-h4zImXifkJP6pssEY76_GWgKo_HVx", "token_type": "Bearer", "expires_in": 3600, "scope": "user-modify-playback-state user-read-playback-state", "expires_at": 1763737778, "refresh_token": "AQAxD3wm_8aDlK9w2ZsibKsrPY0SxUJb2UaqiT2ygxK-pEq2ojxPzgLa6DNt9EpP3mjFDCLmGO5glj5fSpR3ZnqEa26belG4EQv5FOeee38ix31o2xuxPbDsY44KR5Uj1KE"}

79
old/anaislpb.py Normal file
View File

@@ -0,0 +1,79 @@
import cv2
import numpy as np
def image_for_mathias(name="1.jpg"):
# Load and resize
image = cv2.imread(name)
height, width = image.shape[:2]
image = cv2.resize(image, (int(width * 0.2), int(height * 0.2)))
# Grayscale + blur
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# Threshold to isolate object
_, thresh = cv2.threshold(blurred, 50, 255, cv2.THRESH_BINARY_INV)
# Find contours
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
c = max(contours, key=cv2.contourArea)
# --- Step 1: Create mask ---
mask = np.zeros_like(gray)
cv2.drawContours(mask, [c], -1, 255, -1)
# Add alpha channel
b, g, r = cv2.split(image)
rgba = cv2.merge([b, g, r, mask])
# --- Step 2: Crop tightly with padding ---
x, y, w, h = cv2.boundingRect(c)
padding = 10
x, y = max(0, x-padding), max(0, y-padding)
w, h = min(rgba.shape[1]-x, w+2*padding), min(rgba.shape[0]-y, h+2*padding)
cropped = rgba[y:y+h, x:x+w]
# --- Step 3: Perspective transform ---
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect) # 4 corner points
box = np.array(box, dtype="float32")
# Order the points
def order_points(pts):
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)] # top-left
rect[2] = pts[np.argmax(s)] # bottom-right
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)] # top-right
rect[3] = pts[np.argmax(diff)] # bottom-left
return rect
src_pts = order_points(box)
# Desired output size (tune for your code dimensions)
W, H = 600, 200
dst_pts = np.array([
[0, 0],
[W-1, 0],
[W-1, H-1],
[0, H-1]
], dtype="float32")
# Warp with perspective correction
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warped = cv2.warpPerspective(rgba, M, (W, H), borderMode=cv2.BORDER_CONSTANT, borderValue=(0,0,0,0))
# Save transparent deskewed image
cv2.imwrite("spotify_code_deskewed.jpg", warped)
# Show result
cv2.imshow("Spotify Code Deskewed", warped)
cv2.waitKey(0)
cv2.destroyAllWindows()
l = ["rebirth.jpg"]
for name in l:
image_for_mathias(name)

BIN
old/rebirth.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

View File

@@ -0,0 +1,43 @@
import cv2
# Load the image
image = cv2.imread("mid_image.jpg")
# Convert image to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Resize the image to half its original size for display (pop up window)
scale_percent = 20 # percent of original size
width = int(gray.shape[1] * scale_percent / 100)
height = int(gray.shape[0] * scale_percent / 100)
dim = (width, height)
resized_gray = cv2.resize(gray, dim, interpolation=cv2.INTER_AREA)
# Show resized image
cv2.imshow("Detected Rectangle", resized_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
"""# Apply Gaussian blur
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# Run Canny edge detector
edges = cv2.Canny(blur, 50, 150)
# Find contours
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Select the largest contour (presumed to be the rectangle)
contour = max(contours, key=cv2.contourArea)
# Draw the detected contour on a copy of the original image
output = image.copy()
cv2.drawContours(output, [contour], -1, (0, 255, 0), 3)
# Save or show the result
cv2.imwrite("rectangle_edges.jpg", output)
cv2.imshow("Detected Rectangle", output)
cv2.waitKey(0)
cv2.destroyAllWindows()"""

View File

@@ -0,0 +1 @@
{"access_token": "BQAHRFXWQMMKWNbUO3sfhk92iUGi5VJNZmB2lTjRim-g2AIe88mgDvyc1NywjCVBTeZK-b3PkXzGDANJDDOouitEt5P8z0jpVuPrpalwAg0PzEwtKBXr8PHvacajO9B3s7Wy0s7G3CFlfGfbOfmXtW_vF8G2_ooGnlFp_oM8RcOyBl7D5Okf6kr07CWIJty4mNeTf1Ifd212u1tBnbU64lckqlQ0f0W105mt", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "AQCSe9ARCDB-Q6gzna_KKAlcEuNOx2IC9BSVXS5GZxNDi4AjJdA4Kn3hp1YPfiPfogd2rz-5zLbyjUxiRj9oTd-q5EItEPZHIVt-eMf7gCGHfj6h-hLpRrnhgMZjzRAS7CY", "scope": "user-modify-playback-state user-read-playback-state", "expires_at": 1757966002}

View File

@@ -0,0 +1,32 @@
# Spotify Codes Part 2
## Install packages
```bash
pip3 install -r requirements.txt
```
## Run
```bash
python decode_barcode.py --token="YOUR_TOKEN_HERE" ./pics/spotify_playlist_37i9dQZF1DXcBWIGoYBM5M.jpg
```
## Token
This script requires your authorization token to run. You can get this (manually) by visiting [Spotify's Web client](https://open.spotify.com/). If you inspect the page source (F12), you can search for `access_token` in the Network tab after reloading the page. Copy the value and paste it into the `--token` argument.
## Output
```bash
Heights: [0, 6, 0, 2, 4, 5, 1, 4, 5, 2, 3, 7, 3, 7, 1, 5, 6, 2, 5, 7, 4, 3, 0]
Media ref: 57268659651
URI: spotify:user:spotify:playlist:37i9dQZF1DXcBWIGoYBM5M
Summary:
{
'description': 'Coldplay & BTS are on top of the Hottest 50!',
'name': "Today's Top Hits",
'type': 'playlist',
'url': 'https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M'
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

View File

@@ -0,0 +1,21 @@
certifi>=2023.5.7
chardet>=5.1.0
crccheck>=1.0
cycler>=0.11.0
decorator>=5.1.1
idna>=3.5
imageio>=2.31.1
kiwisolver>=1.4.4
matplotlib>=3.7.1
networkx>=3.1
numpy>=1.24.2
Pillow>=10.0.0
pyparsing>=3.0.9
python-dateutil>=2.8.2
PyWavelets>=1.4.1
requests>=2.31.0
scikit-image>=0.25.2
scipy>=1.11.1
six>=1.16.0
tifffile>=2023.7.23
urllib3>=2.0.4

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,31 @@
def crc(data, polynomial):
n = len(polynomial) - 1
initial_length = len(data)
check_bits = data + [0] * n
for i in range(initial_length):
if check_bits[i] == 1:
for j, p in enumerate(polynomial):
check_bits[i + j] = check_bits[i + j] ^ p
return check_bits[-n:]
def check_crc(data, polynomial, check_bits):
full_data = data + check_bits
for i in range(len(data)):
if full_data[i] == 1:
for j, p in enumerate(polynomial):
full_data[i + j] = full_data[i + j] ^ p
return 1 not in full_data
data = [0,0,1,0,0,0,1,0,1,1,1,1,0,1,1,1,1,0,0,0,1,1,1,1,0,1,1,0,1,0,1,1,0,0,0,0,1,1,0,1]
check = [1,1,0,0,1,1,0,0]
poly = [1,0,0,0,0,0,1,1,1]
print(check_crc(data, poly, check))
if __name__ == "__main__":
example = "0010001011110111100011110110101100001101"
long = [int(i) for i in example]
polynomial = [1, 0, 0, 0, 0, 0, 1, 1, 1] # crc8 polynomial
check = crc(long, polynomial)
print(f"Check bits: {check}")
checked = check_crc(long, polynomial, check)
print(f"Checked: {checked}")

View File

@@ -0,0 +1,29 @@
import argparse
import pprint
from get_heights import get_heights
from encode_decode import spotify_bar_decode
from get_uri import get_uri, get_info
parser = argparse.ArgumentParser(description="Spotify Barcode Decoder.")
parser.add_argument("filename", help="The filename of the barcode to decode.")
parser.add_argument("--token", required=True, help="Your Spotify authorization token.")
if __name__ == "__main__":
args = parser.parse_args()
filename = args.filename
token = args.token
heights = get_heights(filename)
# drop the fist, last, and 12th bar
heights = heights[1:11] + heights[12:-1]
decoded = spotify_bar_decode(heights)
print(f"Media ref: {decoded}")
uri = get_uri(decoded, token)
print(f"URI: {uri['target']}")
summary, full_response = get_info(uri["target"], token)
print("Summary:")
pprint.pprint(summary)

View File

@@ -0,0 +1,118 @@
import numpy as np
import crccheck
# This code was written by "Doyle" on Stack Overflow
# https://stackoverflow.com/a/64950150/10703868
# Utils for conversion between int, array of binary
# and array of bytes (as ints)
def int_to_bin(num, length, endian):
if endian == 'l':
return [num >> i & 1 for i in range(0, length)]
elif endian == 'b':
return [num >> i & 1 for i in range(length-1, -1, -1)]
def bin_to_int(bin,length):
return int("".join([str(bin[i]) for i in range(length-1,-1,-1)]),2)
def bin_to_bytes(bin, length):
b = bin[0:length] + [0] * (-length % 8)
return [(b[i]<<7) + (b[i+1]<<6) + (b[i+2]<<5) + (b[i+3]<<4) +
(b[i+4]<<3) + (b[i+5]<<2) + (b[i+6]<<1) + b[i+7] for i in range(0,len(b),8)]
# Return the circular right shift of an array by 'n' positions
def shift_right(arr, n):
return arr[-n % len(arr):len(arr):] + arr[0:-n % len(arr)]
gray_code = [0,1,3,2,7,6,4,5]
gray_code_inv = [[0,0,0],[0,0,1],[0,1,1],[0,1,0],
[1,1,0],[1,1,1],[1,0,1],[1,0,0]]
# CRC using Rocksoft model:
# NOTE: this is not quite any of their predefined CRC's
# 8: number of check bits (degree of poly)
# 0x7: representation of poly without high term (x^8+x^2+x+1)
# 0x0: initial fill of register
# True: byte reverse data
# True: byte reverse check
# 0xff: Mask check (i.e. invert)
spotify_crc = crccheck.crc.Crc(8, 0x7, 0x0, True, True, 0xff)
def calc_spotify_crc(bin37):
bytes = bin_to_bytes(bin37, 37)
return int_to_bin(spotify_crc.calc(bytes), 8, 'b')
def check_spotify_crc(bin45):
data = bin_to_bytes(bin45,37)
return spotify_crc.calc(data) == bin_to_bytes(bin45[37:], 8)[0]
# Simple convolutional encoder
def encode_cc(dat):
gen1 = [1,0,1,1,0,1,1]
gen2 = [1,1,1,1,0,0,1]
punct = [1,1,0]
dat_pad = dat[-6:] + dat # 6 bits are needed to initialize
# register for tail-biting
stream1 = np.convolve(dat_pad, gen1, mode='valid') % 2
stream2 = np.convolve(dat_pad, gen2, mode='valid') % 2
enc = [val for pair in zip(stream1, stream2) for val in pair]
return [enc[i] for i in range(len(enc)) if punct[i % 3]]
# To create a generator matrix for a code, we encode each row
# of the identity matrix. Note that the CRC is not quite linear
# because of the check mask so we apply the lamda function to
# invert it. Given a 37 bit media reference we can encode by
# ref * spotify_generator + spotify_mask (mod 2)
_i37 = np.identity(37, dtype=bool)
crc_generator = [_i37[r].tolist() +
list(map(lambda x : 1-x, calc_spotify_crc(_i37[r].tolist())))
for r in range(37)]
spotify_generator = 1*np.array([encode_cc(crc_generator[r]) for r in range(37)], dtype=bool)
del _i37
spotify_mask = 1*np.array(encode_cc(37*[0] + 8*[1]), dtype=bool)
# The following matrix is used to "invert" the convolutional code.
# In particular, we choose a 45 vector basis for the columns of the
# generator matrix (by deleting those in positions equal to 2 mod 4)
# and then inverting the matrix. By selecting the corresponding 45
# elements of the convolutionally encoded vector and multiplying
# on the right by this matrix, we get back to the unencoded data,
# assuming there are no errors.
# Note: numpy does not invert binary matrices, i.e. GF(2), so we
# hard code the following 3 row vectors to generate the matrix.
conv_gen = [[0,1,0,1,1,1,1,0,1,1,0,0,0,1]+31*[0],
[1,0,1,0,1,0,1,0,0,0,1,1,1] + 32*[0],
[0,0,1,0,1,1,1,1,1,1,0,0,1] + 32*[0] ]
conv_generator_inv = 1*np.array([shift_right(conv_gen[(s-27) % 3],s) for s in range(27,72)], dtype=bool)
# Given an integer media reference, returns list of 20 barcode levels
def spotify_bar_code(ref):
bin37 = np.array([int_to_bin(ref, 37, 'l')], dtype=bool)
enc = (np.add(1*np.dot(bin37, spotify_generator), spotify_mask) % 2).flatten()
perm = [enc[7*i % 60] for i in range(60)]
return [gray_code[4*perm[i]+2*perm[i+1]+perm[i+2]] for i in range(0,len(perm),3)]
# Equivalent function but using CRC and CC encoders.
def spotify_bar_code2(ref):
bin37 = int_to_bin(ref, 37, 'l')
enc_crc = bin37 + calc_spotify_crc(bin37)
enc_cc = encode_cc(enc_crc)
perm = [enc_cc[7*i % 60] for i in range(60)]
return [gray_code[4*perm[i]+2*perm[i+1]+perm[i+2]] for i in range(0,len(perm),3)]
# Given 20 (clean) barcode levels, returns media reference
def spotify_bar_decode(levels):
level_bits = np.array([gray_code_inv[levels[i]] for i in range(20)], dtype=bool).flatten()
conv_bits = [level_bits[43*i % 60] for i in range(60)]
cols = [i for i in range(60) if i % 4 != 2] # columns to invert
conv_bits45 = np.array([conv_bits[c] for c in cols], dtype=bool)
bin45 = (1*np.dot(conv_bits45, conv_generator_inv) % 2).tolist()
if check_spotify_crc(bin45):
return bin_to_int(bin45, 37)
else:
print('Error in levels; Use real decoder!!!')
return -1

View File

@@ -0,0 +1,45 @@
import numpy as np
import matplotlib.pyplot as plt
from skimage import io
from skimage.filters import threshold_otsu, gaussian
from skimage.morphology import closing, square
from skimage.measure import label, regionprops
from skimage.color import rgb2gray
import matplotlib.patches as patches
from sklearn.cluster import KMeans
def get_heights(filename: str):
image = io.imread(filename)
im = rgb2gray(image)
binary_im = im > threshold_otsu(im)
smooth_im = gaussian(binary_im.astype(float), sigma=1)
morph_im = closing(smooth_im > 0.5, square(3))
labeled = label(morph_im)
bar_dims = [r.bbox for r in regionprops(labeled)]
bar_dims.sort(key=lambda x: x[1]) # left to right
bars = bar_dims[1:] # skip logo
bar_heights_raw = []
print(len(bars))
for bar in bars:
top, left, bottom, right = bar
effective_height = bottom - top # use bounding box height directly
bar_heights_raw.append(effective_height)
# Cluster measured heights to 8 clusters representing discrete bar levels
bar_heights_raw_np = np.array(bar_heights_raw).reshape(-1, 1)
kmeans = KMeans(n_clusters=8, random_state=0).fit(bar_heights_raw_np)
cluster_centers = np.sort(kmeans.cluster_centers_.flatten())
# Assign each bar height to closest cluster center index (0 to 7)
predicted_levels = []
for h in bar_heights_raw:
diffs = np.abs(cluster_centers - h)
closest_cluster = np.argmin(diffs)
predicted_levels.append(int(closest_cluster))
print((predicted_levels))
return predicted_levels

View File

@@ -0,0 +1,56 @@
from typing import Tuple
import requests
HEADERS_LUT = {
"X-Client-Id": "58bd3c95768941ea9eb4350aaa033eb3",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"App-Platform": "iOS",
"Accept": "*/*",
"User-Agent": "Spotify/8.5.68 iOS/13.4 (iPhone9,3)",
"Accept-Language": "en",
"Spotify-App-Version": "8.5.68",
}
MEDIA_REF_LUT_URL = "https://spclient.wg.spotify.com:443/scannable-id/id"
def get_uri(media_ref: int, token: str):
"""Query Spotify internal API to get the URI of the media reference."""
header = {
**HEADERS_LUT,
"Authorization": f"Bearer {token}"
}
url = f'{MEDIA_REF_LUT_URL}/{media_ref}?format=json'
response = requests.get(url, headers=header)
response.raise_for_status()
return response.json()
def get_info(uri: str, token: str) -> Tuple[dict, dict]:
"""Query the Spotify API to get information about a URI."""
info_headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": f"Bearer {token}"
}
split = uri.split(":")
content_type = split[-2] + "s"
id = split[-1]
response = requests.get(f"https://api.spotify.com/v1/{content_type}/{id}", headers=info_headers)
response.raise_for_status()
resp = response.json()
result = {
"name": resp["name"],
"type": split[-2],
"url": resp["external_urls"]["spotify"],
}
if "artists" in resp:
result['artists'] = []
for a in resp['artists']:
result["artists"].append(a["name"])
if "album" in resp:
result['album'] = resp['album']['name']
if "description" in resp:
result["description"] = resp['description']
return result, resp

View File

@@ -0,0 +1,11 @@
def permute(bits, step=7):
for i in range(len(bits)):
yield bits[(i * step) % len(bits)]
if __name__ == "__main__":
bits = "111000111100101111101110111001011100110000100100011100110011"
print("".join(permute(bits)))
# 111100110001110101101000011110010110101100111111101000111000

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,35 @@
from typing import List
def encode(bits: List[int], polynomial: List[int], tail_bite=False):
"""Convolutionally encode the stream of bits using the generator polynomial.
If tail_bite == True, prepend the tail of the input. Otherwise use 0s to fill.
"""
if tail_bite:
tail = bits[-(len(polynomial) - 1):]
else:
tail = [0 for i in range(len(polynomial) - 1)]
full = tail + bits
polynomial.reverse() # Reverse since we're working the other direction
parity_bits = []
for i in range(len(bits)):
parity = 0
for j in range(len(polynomial)):
parity ^= full[i + j] * polynomial[j]
parity_bits.append(parity)
return parity_bits
if __name__ == "__main__":
g0 = [1, 0, 1, 1, 0, 1, 1]
g1 = [1, 1, 1, 1, 0, 0, 1]
bits = "010001001110111111110001110101101011011001100"
g0_expected = "100011100111110100110011110100000010001001011"
g1_expected = "110011100010110110110100101101011100110011011"
bits = [int(i) for i in bits]
p0 = encode(bits, g0, True)
p1 = encode(bits, g1, True)
print(g0_expected == "".join(str(i) for i in p0))
print(g1_expected == "".join(str(i) for i in p1))

View File

@@ -0,0 +1,15 @@
media_ref = 57639171874
binary = f"{bin(media_ref)[2:]:0>37}"
print(binary)
# pad with 3 bits to the right:
binary = f"{binary:0<39}"
print(binary)
a = "100011100111110100110011110100000010001001011"
b = "110011100010110110110100101101011100110011011"
c = zip(a, b)
print("".join(i + j for i, j in c))

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

11
old/test/compose.yml Normal file
View File

@@ -0,0 +1,11 @@
services:
spotifyd:
container_name: spotify
restart: always
image: hvalev/spotifyd-alsa-dbus
network_mode: host
# Keep in mind to keep the ports for zeroconf and mdns open on the host
devices:
- /dev/snd:/dev/snd
volumes:
- ./spotifyd.conf:/etc/spotifyd.conf

115
old/test/spotifyd.conf Normal file
View File

@@ -0,0 +1,115 @@
[global]
#---------#
# GENERAL #
#---------#
# The name that gets displayed under the connect tab on
# official clients.
#device_name = "device_name_in_spotify_connect"
# The displayed device type in Spotify clients.
# Can be unknown, computer, tablet, smartphone, speaker, t_v,
# a_v_r (Audio/Video Receiver), s_t_b (Set-Top Box), and audio_dongle.
#device_type = "speaker"
# The directory used to store credentials and audio cache.
# Default: infers a sensible cache directory (e.g. on Linux: $XDG_CACHE_HOME)
#
# Note: The file path does not get expanded. Environment variables and
# shell placeholders like $HOME or ~ don't work!
#cache_path = "/full/path/to/cache/directory"
# If set to true, audio data does NOT get cached.
# In this case, the cache is only used for credentials.
#no_audio_cache = true
# The maximal size of the cache directory in bytes
# The example value corresponds to ~ 1GB
#max_cache_size = 1000000000
# If set to true, `spotifyd` tries to bind to dbus (default is the session bus)
# and expose MPRIS controls. When running headless, without the session bus,
# you should set this to false, to avoid errors. If you still want to use MPRIS,
# have a look at the `dbus_type` option.
#use_mpris = true
# The bus to bind to with the MPRIS interface.
# Possible values: "session", "system"
# The system bus can be used if no graphical session is available
# (e.g. on headless systems) but you still want to be able to use MPRIS.
# NOTE: You might need to add appropriate policies to allow spotifyd to
# own the name.
#dbus_type = "session"
#-----------#
# DISCOVERY #
#-----------#
# If set to true, this disables zeroconf discovery.
# This can be useful, if one prefers to run a single-user instance.
#disable_discovery = false
# The port at which `spotifyd` is going to offer its service over the network (TCP).
# If not set, a random port > 1024 is used. For the service to be discoverable on the
# local network via mDNS, both the mDNS port (5353 UDP) and the random or fixed
# zeroconf port need to be allowed through any active firewall.
#zeroconf_port = 1234
#-------#
# AUDIO #
#-------#
# The audio backend used to play music. To get
# a list of possible backends, run `spotifyd --help`.
#backend = "alsa" # use portaudio for macOS [homebrew]
# The alsa audio device to stream audio. To get a
# list of valid devices, run `aplay -L`,
#device = "default" # omit for macOS
# The PCM sample format to use. Possible values
# are F32, S32, S24, S24_3, S16.
# Change this value if you encounter errors like
# "Alsa error PCM open ALSA function 'snd_pcm_hw_params_set_format' failed with error 'EINVAL: Invalid argument'"
#audio_format = "S16"
# The volume controller. Each one behaves different to
# volume increases. For possible values, run
# `spotifyd --help`.
#volume_controller = "softvol" # use softvol for macOS
# ! Only relevant for ALSA !
# The alsa control device. By default this is the same
# name as the `device` field.
#control = "default"
# ! Only relevant for ALSA !
# The alsa mixer used by `spotifyd`.
#mixer = "PCM" # omit for macOS
# The audio bitrate. 96, 160 or 320 kbit/s
#bitrate = 160
# Volume on startup between 0 and 100
#initial_volume = 90
# If set to true, enables volume normalisation between songs.
#volume_normalisation = true
# The normalisation pregain that is applied for each song.
#normalisation_pregain = -10
#-------ä
# OTHER #
#-------#
# After the music playback has ended, start playing similar songs based on the previous tracks.
# By default, `spotifyd` infers this setting from the user settings.
#autoplay = true
# A command that gets executed in your shell after each song changes.
#on_song_change_hook = "echo \"hook executed on $PLAYER_EVENT\""
# The proxy `spotifyd` will use to connect to spotify.
#proxy = "http://proxy.example.org:8080"