Saltar al contenido

Codificación de URI de Spotify a códigos de Spotify

Buscamos en todo el mundo on line y así mostrarte la solución a tu problema, si continúas con dificultades puedes dejarnos la inquietud y te respondemos con mucho gusto.

Solución:

La patente explica el proceso general, esto es lo que he encontrado.

Esta es una patente más reciente

Cuando se utiliza el generador de código de Spotify, el sitio web realiza una solicitud a https://scannables.scdn.co/uri/plain/[format]/[background-color-in-hex]/[code-color-in-text]/[size]/[spotify-URI].

Con Burp Suite, al escanear un código a través de Spotify, la aplicación envía una solicitud a la API de Spotify: https://spclient.wg.spotify.com/scannable-id/id/[CODE]? formato = json donde [CODE] es la referencia mediática que estabas buscando. Esta solicitud se puede realizar a través de Python pero solo con el [TOKEN] que se generó a través de la aplicación, ya que esta es la única forma de obtener el alcance correcto. El token de la aplicación caduca en aproximadamente media hora.

import requests

head=
"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",
"Authorization": "Bearer [TOKEN]", 
"Spotify-App-Version": "8.5.68"

response = requests.get('https://spclient.wg.spotify.com:443/scannable-id/id/26560102031?format=json', headers=head)

print(response)
print(response.json())

Que devuelve:


'target': 'spotify:playlist:37i9dQZF1DXcBWIGoYBM5M'

Por lo tanto, 26560102031 es la referencia multimedia para su lista de reproducción.

La patente establece que el código se detecta primero y luego posiblemente se convierte en 63 bits utilizando una tabla de Gray. Por ejemplo, 361354354471425226605 se codifica en 010101001010111110 01011111011010000110 011111011 0111011010111.

Sin embargo, el código enviado a la API es 6875667268, no estoy seguro de cómo se genera la referencia de medios, pero este es el número utilizado en la tabla de búsqueda.

La referencia contiene los números enteros 0-9 en comparación con la tabla gris de 0-7, lo que implica que se ha utilizado un algoritmo que usa binario normal. La patente habla sobre el uso de un código convolucional y luego el algoritmo de Viterbi para la corrección de errores, por lo que este puede ser el resultado de eso. Algo que es imposible de recrear sin los estados que creo. Sin embargo, me interesaría si puede interpretar mejor la patente.

Esta referencia de medios tiene 10 dígitos, sin embargo, otras tienen 11 o 12.

Aquí hay dos ejemplos más de las distancias sin procesar, el binario de la tabla gris y luego la referencia de medios:

1.

022673352171662032460

000 011 011 101 100 010 010 111 011 001 100 001 101 101 011 000 010 011 110 101 000

67775490487

2. 574146602473467556050

111100110001110101101 000 011110100 0101101011001111101000111000

57639171874

editar:

Información adicional: hay algunas publicaciones en línea que describen cómo puede codificar cualquier texto como spotify: playlist: HelloWorld en un código, sin embargo, esto ya no funciona.

También descubrí a través del proxy que puedes usar el dominio para buscar la carátula del álbum de una pista por encima del código. Esto sugiere una integración más cercana de la API de Spotify y esta URL escaneable de lo que se pensaba anteriormente. Ya que no solo almacena los URI y sus códigos, sino que también puede validar los URI y devolver la carátula del álbum actualizada.

https://scannables.scdn.co/uri/800/spotify%3Atrack%3A0J8oh5MAMyUPRIgflnjwmB

Tu sospecha era correcta: están usando una tabla de búsqueda. Para conocer todos los detalles técnicos divertidos, la patente correspondiente está disponible aquí: https://data.epo.org/publication-server/rest/v1.0/publication-dates/20190220/patents/EP3444755NWA1/document.pdf

Discusión muy interesante. Siempre me han atraído los códigos de barras, así que tuve que echar un vistazo. Hice un análisis de los códigos de barras solo (no accedí a la API para las referencias de medios) y creo que he resuelto el proceso básico de codificación. Sin embargo, en base a los dos ejemplos anteriores, no estoy convencido de tener el mapeo de la referencia de medios al vector de 37 bits correcto (es decir, funciona en el caso 2 pero no en el caso 1). En cualquier caso, si tiene algunos pares más, esa última parte debería ser fácil de resolver. Hágamelo saber.

Para aquellos que quieran resolver esto, ¡no lean los spoilers a continuación!

Resulta que el proceso básico descrito en la patente es correcto, pero carece de detalles. Resumiré a continuación usando el ejemplo anterior. De hecho, analicé esto a la inversa, por lo que creo que la descripción del código es básicamente correcta, excepto por el paso (1), es decir, genere 45 códigos de barras y todos ellos coincidieron con este código.

1. Map the media reference as integer to 37 bit vector. 
Something like write number in base 2, with lowest significant bit 
on the left and zero-padding on right if necessary. 
   57639171874 -> 0100010011101111111100011101011010110

2. Calculate CRC-8-CCITT, i.e. generator x^8 + x^2 + x + 1
   The following steps are needed to calculate the 8 CRC bits:

   Pad with 3 bits on the right:
   01000100 11101111 11110001 11010110 10110000
   Reverse bytes:
   00100010 11110111 10001111 01101011 00001101
   Calculate CRC as normal (highest order degree on the left):
   -> 11001100
   Reverse CRC:
   -> 00110011
   Invert check:
   -> 11001100
   Finally append to step 1 result:
   01000100 11101111 11110001 11010110 10110110 01100

3. Convolutionally encode the 45 bits using the common generator
polynomials (1011011, 1111001) in binary with puncture pattern 
110110 (or 101, 110 on each stream). The result of step 2 is 
encoded using tail-biting, meaning we begin the shift register 
in the state of the last 6 bits of the 45 long input vector. 

  Prepend stream with last 6 bits of data:
  001100 01000100 11101111 11110001 11010110 10110110 01100
  Encode using first generator:
  (a) 100011100111110100110011110100000010001001011
  Encode using 2nd generator:
  (b) 110011100010110110110100101101011100110011011
  Interleave bits (abab...):
  11010000111111000010111011110011010011110001...
  1010111001110001000101011000010110000111001111
  Puncture every third bit:
  111000111100101111101110111001011100110000100100011100110011

4. Permute data by choosing indices 0, 7, 14, 21, 28, 35, 42, 49, 
56, 3, 10..., i.e. incrementing 7 modulo 60. (Note: unpermute by 
incrementing 43 mod 60).

  The encoded sequence after permuting is
  111100110001110101101000011110010110101100111111101000111000

5. The final step is to map back to bar lengths 0 to 7 using the
gray map (000,001,011,010,110,111,101,100). This gives the 20 bar 
encoding. As noted before, add three bars: short one on each end 
and a long one in the middle. 

ACTUALIZACIÓN: He agregado un decodificador de código de barras (niveles) (asumiendo que no hay errores) y un codificador alternativo que sigue la descripción anterior en lugar del método de álgebra lineal equivalente. Ojalá esté un poco más claro.

ACTUALIZACIÓN 2: eliminó la mayoría de las matrices codificadas para ilustrar cómo se generan.

El método de álgebra lineal define la transformación lineal (spotify_generator) y la máscara para mapear la entrada de 37 bits en los datos codificados convolucionalmente de 60 bits. La máscara es el resultado de la codificación convolucional del CRC invertido de 8 bits. Spotify_generator es una matriz de 37×60 que implementa el producto de generadores para el CRC (una matriz de 37×45) y códigos convolucionales (una matriz de 45×60). Puede crear la matriz generadora a partir de una función de codificación aplicando la función a cada fila de una matriz generadora de tamaño apropiado. Por ejemplo, una función CRC que agrega 8 bits a cada vector de datos de 37 bits aplicado a cada fila de una matriz de identidad de 37×37.

import numpy as np
import crccheck


# 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

Y ejemplo:

>>> levels = [5,7,4,1,4,6,6,0,2,4,3,4,6,7,5,5,6,0,5,0]
>>> spotify_bar_decode(levels)
57639171874
>>> spotify_barcode(57639171874)
[5, 7, 4, 1, 4, 6, 6, 0, 2, 4, 3, 4, 6, 7, 5, 5, 6, 0, 5, 0]

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *