travail d'un we (j'en ai big marre)
1
old/.cache
Normal 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
@@ -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
|
After Width: | Height: | Size: 240 KiB |
43
old/shapeDetectionImageCrop.py
Normal 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()"""
|
||||
1
old/spotify-codes-part-2/.cache
Normal 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}
|
||||
32
old/spotify-codes-part-2/README.md
Normal 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'
|
||||
}
|
||||
```
|
||||
BIN
old/spotify-codes-part-2/aa.jpg
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
old/spotify-codes-part-2/allo.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
old/spotify-codes-part-2/aloorrrrr.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
old/spotify-codes-part-2/biz.jpg
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
old/spotify-codes-part-2/biz.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
old/spotify-codes-part-2/bizrog.jpg
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
old/spotify-codes-part-2/bw.jpg
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
old/spotify-codes-part-2/bz.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
old/spotify-codes-part-2/code.jpeg
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
old/spotify-codes-part-2/csq.jpg
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
old/spotify-codes-part-2/imperdable.jpg
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
old/spotify-codes-part-2/jtd.jpg
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
old/spotify-codes-part-2/jtd.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
old/spotify-codes-part-2/lala.jpeg
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
old/spotify-codes-part-2/layayaya.jpg
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
old/spotify-codes-part-2/maybe.jpg
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
old/spotify-codes-part-2/maybe.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
old/spotify-codes-part-2/nggyu.jpg
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
old/spotify-codes-part-2/nggyu.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
old/spotify-codes-part-2/parfd.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
old/spotify-codes-part-2/print.jpg
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
old/spotify-codes-part-2/rage.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
old/spotify-codes-part-2/rbw.jpg
Normal file
|
After Width: | Height: | Size: 109 KiB |
21
old/spotify-codes-part-2/requirements.txt
Normal 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
|
||||
BIN
old/spotify-codes-part-2/rog.jpg
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
old/spotify-codes-part-2/rogprint.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
old/spotify-codes-part-2/rouuuge.jpg
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
old/spotify-codes-part-2/rr.jpeg
Normal file
|
After Width: | Height: | Size: 12 KiB |
31
old/spotify-codes-part-2/src/crc.py
Normal 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}")
|
||||
29
old/spotify-codes-part-2/src/decode_barcode.py
Normal 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)
|
||||
|
||||
118
old/spotify-codes-part-2/src/encode_decode.py
Normal 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
|
||||
45
old/spotify-codes-part-2/src/get_heights.py
Normal 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
|
||||
56
old/spotify-codes-part-2/src/get_uri.py
Normal 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
|
||||
|
||||
11
old/spotify-codes-part-2/src/permute.py
Normal 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
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
35
old/spotify-codes-part-2/src/simple_convolutional_code.py
Normal 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))
|
||||
15
old/spotify-codes-part-2/src/step_by_step.py
Normal 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))
|
||||
BIN
old/spotify-codes-part-2/st.jpeg
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
old/spotify-codes-part-2/test.jpg
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
old/spotify-codes-part-2/testrouge.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
11
old/test/compose.yml
Normal 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
@@ -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"
|
||||