Os identificadores videoId e channelId do YouTube são valores inteiros únicos representados em uma versão ligeiramente modificada da codificação Base64 . Uma diferença em relação às recomendações IETF RFC4648 é a substituição de dois caracteres no alfabeto de codificação:
Payload ASCII/Unicode Base64 YouTube
------- ------------- --------- ---------
0...25 \x41 ... \x5A 'A'...'Z' 'A'...'Z'
26...51 \x61 ... \x7A 'a'...'z' 'a'...'z'
52...61 \x30 ... \x39 '0'...'9' '0'...'9'
62 \x2F vs. \x2D → '/' (2F) '-' (2D)
63 \x2B vs. \x5F → '+' (2B) '_' (5F)
A substituição provavelmente se deve ao fato de que, por algum motivo, o RFC4648 selecionou dois caracteres que já tinham funções importantes e bem estabelecidas nos URLs. [nota 1.] Obviamente, para o uso discutido aqui, é melhor evitar essa complicação específica.
Outra diferença da especificação oficial é que os identificadores do YouTube não usam o =
caractere de preenchimento; não é necessário porque os comprimentos codificados esperados por tamanho inteiro decodificado respectivo são fixos e conhecidos (11 e 22 'dígitos' codificados para 64 e 128 bits, respectivamente).
Com uma exceção menor (discutida abaixo), os detalhes completos do mapeamento Base64 podem ser inferidos a partir de dados acessíveis ao público. Com um mínimo de adivinhação, é provável que a Base64 esquema usado nas videoId e channelId cordas é a seguinte:
——₀————₁————₂————₃————₄————₅————₆————₇————₈————₉———₁₀———₁₁———₁₂———₁₃———₁₄———₁₅—
00ᴴ 01ᴴ 02ᴴ 03ᴴ 04ᴴ 05ᴴ 06ᴴ 07ᴴ 08ᴴ 09ᴴ 0Aᴴ 0Bᴴ 0Cᴴ 0Dᴴ 0Eᴴ 0Fᴴ
00→ 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
A B C D E F G H I J K L M N O P
—₁₆———₁₇———₁₈———₁₉———₂₀———₂₁———₂₂———₂₃———₂₄———₂₅———₂₆———₂₇———₂₈———₂₉———₃₀———₃₁—
10ᴴ 11ᴴ 12ᴴ 13ᴴ 14ᴴ 15ᴴ 16ᴴ 17ᴴ 18ᴴ 19ᴴ 1Aᴴ 1Bᴴ 1Cᴴ 1Dᴴ 1Eᴴ 1Fᴴ
01→ 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
Q R S T U V W X Y Z a b c d e f
—₃₂———₃₃———₃₄———₃₅———₃₆———₃₇———₃₈———₃₉———₄₀———₄₁———₄₂———₄₃———₄₄———₄₅———₄₆———₄₇—
20ᴴ 21ᴴ 22ᴴ 23ᴴ 24ᴴ 25ᴴ 26ᴴ 27ᴴ 28ᴴ 29ᴴ 2Aᴴ 2Bᴴ 2Cᴴ 2Dᴴ 2Eᴴ 2Fᴴ
10→ 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
g h i j k l m n o p q r s t u v
—₄₈———₄₉———₅₀———₅₁———₅₂———₅₃———₅₄———₅₅———₅₆———₅₇———₅₈———₅₉———₆₀———₆₁———₆₂———₆₃—
30ᴴ 31ᴴ 32ᴴ 33ᴴ 34ᴴ 35ᴴ 36ᴴ 37ᴴ 38ᴴ 39ᴴ 3Aᴴ 3Bᴴ 3Cᴴ 3Dᴴ 3Eᴴ 3Fᴴ
11→ 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
w x y z 0 1 2 3 4 5 6 7 8 9 - _
A razão para acreditar que Base64 está sendo usado é que, quando assumimos tamanhos inteiros padrão de 64 e 128 bits para a entrada de encoder, Base64 prevê os comprimentos incomuns caracteres (11 e 22 caracteres) do YouTube channelId e videoId identificadores exatamente. Além disso, os restantes calculados de acordo com a Base64 explicam perfeitamente a variação distributiva observada encontrada no l̲a̲s̲t̲ c̲h̲a̲r̲a̲c̲t̲e̲r̲ de cada tipo de cadeia identificadora. A discussão desses pontos segue a seguir.
Nos dois casos, os "dados" binários que são codificados em Base64 são um único inteiro, de 64 ou 128 bits, para (respectivamente) videoId vs. channelId . Consequentemente, usando um decodificador Base64, um único número inteiro pode ser recuperado a partir do identificador de string, e pode ser bastante útil fazer isso porque, enquanto cada ID de número inteiro contém exatamente as mesmas informações que a string Base64 - e também permite que a string ser recriado a qualquer momento - quando comparada às seqüências Base64 armazenadas como Unicode, a representação binária é 63% menor, tem a densidade máxima de bits de 100%, alinha melhor a memória, classifica e hashes mais rapidamente e, talvez o mais importante, elimina colisões falsas entre identificadores que diferem apenas no caso ortográfico. Esse último problema, embora extremamente improvável numericamente, não pode ser descartado quando os IDs Base64 são tratados como não diferenciam maiúsculas de minúsculas, como fazem alguns sistemas de arquivos (por exemplo , Windows , que remonta ao DOS ).
Isso é meio importante: se você estiver usando um videoId / channelId string como parte de um nome de arquivo Windows / NTFS, há uma vanishingly miniscule- mas, no entanto diferente de zero -chance de colisões de nome de arquivo devido a esses sistemas de arquivos implantando caminho e nomes de arquivos case-insensitive .
Se você estiver preocupado com esse problema remotamente possível, uma maneira de eliminá-lo matematicamente seria recodificar os números inteiros decodificados - ainda obtidos conforme descrito neste artigo - em uma base 10 (decimal) ou (uniforme- caso) representação hexadecimal, para uso em nomes de caminho ou arquivo em tais sistemas de arquivos. [nota 2.] Nessa abordagem, o videoId de 64 bits precisaria de 20 dígitos decimais [0-9]
ou 8 dígitos hexadecimais [0-9,A-F]
( vs. 11 dígitos Base64). O channelId de 128 bits exigiria no máximo 39 dígitos decimais ou 16 dígitos hexadecimais ( vs. 22 dígitos Base64).
A decodificação para binário é trivial para o caso de 64 bits , porque você pode usar um UInt64
( ulong
em C # ) para armazenar o valor binário nativo que volta.
/// <summary> Recover the unique 64-bit value from an 11-character videoID </summary>
/// <remarks>
/// The method of padding shown here (i.e. 'b64pad') is provided to demonstrate the
/// full and correct padding requirement for Base64 in general. For our cases:
///
/// videoId → 11 chars → b64pad[11 % 3] → b64pad[2] → "="
/// channelId → 22-chars → b64pad[22 % 3] → b64pad[1] → "=="
///
/// Note however that, because it returns 'ulong', this function only works for videoId
/// values, and the padding will always end up being "=". This is assumed in the revised
/// version of this code given further below, by just hard-coding the value "=".
/// </remarks>
static ulong YtEnc_to_videoId(String ytId)
{
String b64 = ytId.Replace('-', '+').Replace('_', '/') + b64pad[ytId.Length % 3];
return BitConverter.ToUInt64(Convert.FromBase64String(b64), 0);
}
static String[] b64pad = { "", "==", "=" };
No caso dos valores de 128 bits , é um pouco mais complicado, porque, a menos que seu compilador tenha uma __int128
representação, você precisará descobrir uma maneira de armazenar a coisa toda e mantê-la combinada à medida que a distribuir . Um tipo de valor simples (ou System.Numerics.Vectors.Vector<T>
que se manifesta como um registro de hardware SIMD de 128 bits, quando disponível) fará o truque no .NET (não mostrado).
[ edit: ]
Após uma reflexão mais aprofundada, uma parte do meu post original não estava no máximo completo. Para ser justo, o trecho original é mantido (você pode ignorá-lo, se desejar); imediatamente abaixo, explico o insight ausente:
[ original: ]
Você deve ter notado acima que eu escrevi que você pode recuperar " um " número inteiro. Este não seria o valor que foi originalmente codificado? Não necessariamente. E não estou aludindo à distinção assinada / não assinada que, é verdade, não pode ser verificada aqui (porque não altera nenhum fato sobre a imagem binária). São os próprios valores numéricos: sem alguns " Rosetta Stone"que nos permitiria verificar com valores absolutos conhecidos como" corretos ", o mapeamento numérico do alfabeto e também a endianidade não podem ser conhecidos positivamente, o que significa que não há garantia de que você esteja recuperando o mesmo valor que felizmente, desde que o YouTube nunca exponha publicamente os chamados valores corretos em um formato menos opaco em outro lugar, isso não pode importar.
Isso ocorre porque os valores decodificados de 64 ou 128 bits não têm nenhum uso, exceto como um token de identificação, de modo que nossos únicos requisitos para a transformação são codificação distinta (nenhum dois tokens exclusivos colidem) e reversibilidade (a decodificação recupera a identidade do token original).
Em outras palavras, tudo o que realmente importa é o disparo sem perdas da string Base64 original. Como o Base64 é sem perdas e reversível (desde que você mantenha sempre o mesmo mapeamento de alfabeto e suposição de endianidade para codificação e decodificação), ele satisfaz nossos propósitos. Seus valores numéricos podem não corresponder aos registrados no cofre principal do YouTube, mas você não poderá diferenciar.
[ Nova análise: ]
Acontece que lá são algumas pistas que podem nos contar sobre o "verdadeiro" Base64 mapeamento. Apenas certos mapeamentos preveem os caracteres de posição final que observamos, o que significa que o valor binário de apenas esses caracteres deve ter um certo número de zeros LSB. Heh.
Tomados em conjunto com a suposição provável de que os caracteres do alfabeto e do dígito são mapeados em ordem crescente, podemos basicamente confirmar que o mapeamento é o que é mostrado nas tabelas acima. A única incerteza restante sobre a qual a análise LSB é inconclusiva é a possível troca dos caracteres -
e _
( 62
/ 63
).
O texto original fez discutir essa questão LSB (ver mais abaixo), mas o que eu não realizar plenamente na época era como LSB informações age para restringir as possíveis Base64 mapeamentos.
Um último comentário sobre isso é que você pode escolher intencionalmente big-endian para a interpretação binária com a qual seu aplicativo trabalha internamente (mesmo que seja menos comum do que o little-endian atualmente e, portanto, não seja o que o YouTube 'oficialmente' faz) isto). O motivo é que este é um caso de visualizações duplas no mesmo valor, de forma que a ordem real de bytes seja visivelmente exposta na renderização Base64. É útil e menos confuso manter a ordem de classificação consistente entre o valor binário e a string Base64 legível por humanos (um pouco mais), mas o tipo dos valores binários little-endian é uma disputa não trivial da classificação ASCII / lexical desejada .
Não há uma solução simples para esse problema se você começar com valores de ID little-endian (ou seja, simplesmente reverter sua classificação não funcionará). Em vez disso, você deve planejar com antecedência e reverter os bytes de cada valor binário no momento da decodificação . Portanto, se você se preocupa com a exibição alfabética que corresponde à classificação dos valores binários, convém alterar a função mostrada acima para que ela decodifique em valores big-endian ulong
. Aqui está esse código:
// Recover the unique 64-bit value (big-endian) from an 11-character videoID
static ulong YtEnc_to_videoId(String ytId)
{
var a = Convert.FromBase64String(ytId.Replace('-', '+').Replace('_', '/') + "=");
if (BitConverter.IsLittleEndian) // true for most computers nowadays
Array.Reverse(a);
return BitConverter.ToUInt64(a, 0);
}
IDs do YouTube
ID do vídeo
Para o videoId , é um número inteiro de 8 bytes (64 bits). A aplicação da codificação Base64 a 8 bytes de dados requer 11 caracteres . No entanto, como cada caractere Base64 transmite exatamente 6 bits (ou seja, 2⁶ é igual a 64), essa alocação pode realmente suportar até 11 × 6 = 66
bits - um excedente de 2 bits nos 64 bits necessários para a carga útil. Os bits em excesso são definidos como zero, o que tem o efeito de excluir a exibição de certos caracteres na última posição da sequência codificada. Em particular, é garantido que o videoId termine sempre com um dos seguintes caracteres:
{ A, E, I, M, Q, U, Y, c, g, k, o, s, w, 0, 4, 8 }
Assim, a expressão regular com restrição máxima (RegEx) para o videoId seria a seguinte:
[0-9A-Za-z_-]{10}[048AEIMQUYcgkosw]
ID do canal ou da lista de reprodução
As cadeias channelId e playlistId são produzidas pela codificação Base64 de um número inteiro binário de 128 bits (16 bytes). Isso fornece uma cadeia de 22 caracteres que pode ser prefixada UC
para identificar o próprio canal ou UU
para identificar uma lista de reprodução completa dos vídeos que ele contém. Essas cadeias de caracteres com prefixo de 24 caracteres são usadas em URLs . Por exemplo, o seguinte mostra duas maneiras de se referir ao mesmo canal. Observe que a versão da lista de reprodução mostra o número total de vídeos no canal, [consulte a nota 3.], uma informação útil que as páginas do canal não expõem.
URL do canalhttps://www.youtube.com/channel/UC K8sQmJBp8GCxrOtXWBpyEA
URL da lista de reproduçãohttps://www.youtube.com/playlist?list=UU K8sQmJBp8GCxrOtXWBpyEA
Como foi o caso do videoId de 11 caracteres , o cálculo por Base64 prediz corretamente o comprimento da string observado de 22 caracteres . Nesse caso, a saída é capaz de codificar 22 × 6 = 132
bits, um excedente de 4 bits; esses zeros acabam restringindo m̲o̲s̲t̲ dos 64 símbolos do alfabeto na última posição, com apenas 4 restantes elegíveis. Portanto, sabemos que o último caractere em uma string channelId do YouTube deve ser um dos seguintes:
{ A, Q, g, w }
Isso nos dá a expressão regular com restrição máxima para um channelId :
[0-9A-Za-z_-]{21}[AQgw]
Como observação final, as expressões regulares mostradas acima descrevem apenas os valores de ID simples, sem os prefixos, barras, separadores, etc., que devem estar presentes nos URLs e nos outros usos diversos. Os padrões RegEx que apresentei são matematicamente mínimos possíveis, dadas as propriedades das sequências identificadoras, mas, se usadas como estão sem contexto adicional, provavelmente gerarão muitos falsos positivos, ou seja: correspondam incorretamente ao texto espúrio. Para evitar esse problema no uso real, envolva-os com o máximo possível do contexto adjacente esperado.
Notas
[1.]
Como prometido acima, aqui está um trecho da especificação Base64, que discute suas considerações ao selecionar os símbolos do alfabeto. Indivíduos que procuram entender como o processo foi concluído na seleção de caracteres com semântica de URL podem achar as explicações um tanto pouco redutoras.
3.4 Escolhendo o alfabeto
Aplicativos diferentes têm requisitos diferentes para os caracteres do alfabeto. Aqui estão alguns requisitos que determinam qual alfabeto deve ser usado:
Manipulado por seres humanos. Os caracteres "0" e "O" são facilmente confundidos, assim como "1", "l" e "I". No alfabeto base32 abaixo, onde 0 (zero) e 1 (um) não estão presentes, um decodificador pode interpretar 0 como O e 1 como I ou L, dependendo do caso. (No entanto, por padrão, não deve; consulte a seção anterior.)
Codificado em estruturas que exigem outros requisitos. Para as bases 16 e 32, isso determina o uso de letras maiúsculas ou minúsculas. Para a base 64, os caracteres não alfanuméricos (em particular, "/") podem ser problemáticos em nomes de arquivos e URLs.
Usado como identificadores. Certos caracteres, especialmente "+" e "/" no alfabeto base 64, são tratados como quebras de palavras pelas ferramentas de pesquisa / índice de texto herdadas.
Não existe um alfabeto universalmente aceito que atenda a todos os requisitos. Para um exemplo de uma variante altamente especializada, consulte IMAP [8]. Neste documento, documentamos e nomeamos alguns alfabetos usados atualmente.
[2.]
Como alternativa, para resolver o problema do uso de cadeias de caracteres de ID codificadas em Base64 como componentes "como estão" dos nomes de arquivos ou caminhos no sistema de arquivos NTFS, que não diferencia maiúsculas de minúsculas por padrão (e, portanto, tecnicamente corre o risco de conflitar com um ou mais valores de ID não relacionados), acontece que o NTFS pode ser configurado com nomes de caminhos / arquivos que diferenciam maiúsculas de minúsculas por volume. A ativação do comportamento não padrão pode corrigir o problema descrito aqui, mas raramente é recomendado, pois altera as expectativas para todos os aplicativos diferentes que inspecionam ou acessam o volume. Se você está considerando essa opção, leia e entenda isso primeiro e provavelmente mudará de ideia.
[3.]
Acredito que o número total de vídeos mostrados na página da lista de reprodução do canal leve em consideração uma exclusão de vídeos restritos de acordo com a região geográfica do cliente HTTP. Isso explica qualquer discrepância entre o número de vídeos listados para a lista de reprodução versus o canal.