yt-zmp 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +201 -69
- package/bin/yt-zmp-bridge.js +2 -0
- package/{download-test.js → bin/yt-zmp.js} +5 -3
- package/index.js +25 -38
- package/package.json +44 -35
- package/src/bridge-client.js +80 -0
- package/src/bridge-server.js +126 -0
- package/{client.js → src/client.js} +8 -4
- package/{downloader.js → src/downloader.js} +51 -7
- package/{extractor.js → src/extractor.js} +123 -22
- package/{formats.js → src/formats.js} +3 -3
- package/{media.js → src/media.js} +23 -13
- package/src/proxy.js +64 -0
- package/src/utils.js +28 -0
- package/cookie-test.js +0 -36
- package/direct-test.js +0 -73
- package/test.js +0 -190
- package/utils.js +0 -40
- /package/{auth.js → src/auth.js} +0 -0
- /package/{cipher.js → src/cipher.js} +0 -0
- /package/{parser.js → src/parser.js} +0 -0
- /package/{transcoder.js → src/transcoder.js} +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Adicionado
|
|
6
|
+
- Suporte a proxy HTTP/HTTPS (`proxy` por chamada, `setDefaultProxy` e `YOUTUBE_PROXY`) em todas as requisições: página, Player API, JavaScript do player e download do stream. Usa `undici` (dependência opcional).
|
|
7
|
+
- Bridge para servidores com IP bloqueado: servidor `yt-zmp-bridge` e cliente `bridgeDownload` (`yt-zmp/bridge`).
|
|
8
|
+
- Detecção de travamento no download (`stallTimeoutMs`, padrão 30 s): o stream que para de enviar dados é abortado e a tentativa é repetida, em vez de ficar pendurado.
|
|
9
|
+
- Linha de comando `yt-zmp`.
|
|
10
|
+
- Suíte de testes offline (`npm test`).
|
|
11
|
+
|
|
12
|
+
### Corrigido
|
|
13
|
+
- O extrator descarta respostas sem formatos utilizáveis (sem `streamingData`, só SABR ou sem `url`/`signatureCipher`) e passa para o próximo cliente, em vez de parar no primeiro que devolve `streamingData` vazio.
|
|
14
|
+
- `itag`, `bitrate`, `width`, `height` e `fps` são normalizados para número, pois alguns retornos os trazem como texto e quebravam a comparação por `itag`.
|
|
15
|
+
- Mensagens de erro mais claras: status de reprodução do cliente que falhou e causa do erro de conexão no bridge.
|
|
16
|
+
|
|
17
|
+
### Alterado
|
|
18
|
+
- Código-fonte movido para `src/`; o ponto de entrada continua sendo `import YouTube from 'yt-zmp'`.
|
|
19
|
+
- Mínimo de Node.js: 18.17.
|
|
20
|
+
- O pacote publica apenas `index.js`, `src/`, `bin/`, `README.md`, `LICENSE` e `CHANGELOG.md`.
|
|
21
|
+
|
|
22
|
+
## 1.0.0
|
|
23
|
+
|
|
24
|
+
Versão inicial.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yt-zmp contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,132 +1,264 @@
|
|
|
1
|
-
#
|
|
1
|
+
# yt-zmp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Extração e download de áudio e vídeo do YouTube em Node.js, **sem yt-dlp**. Consulta os clientes internos do YouTube, decifra as URLs dos streams e baixa a mídia, com conversão para MP3 e junção de vídeo e áudio via FFmpeg.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Recursos principais:
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Cadeia de clientes (`WEB`, `MWEB`, `ANDROID`, `IOS`, `TVHTML5` e outros), usando o primeiro que devolver streams utilizáveis.
|
|
8
|
+
- Áudio (`m4a` ou `mp3`), vídeo em qualidade escolhida (com junção de áudio) e streams muxados (ex.: itag 18).
|
|
9
|
+
- **Proxy HTTP/HTTPS** por chamada, por variável de ambiente ou global.
|
|
10
|
+
- **Bridge** para servidores cujo IP o YouTube bloqueia: o download é feito em outra máquina com IP residencial.
|
|
11
|
+
- Cookies (formato Netscape) e GVS PO token opcionais.
|
|
12
|
+
- Download com detecção de travamento: se o stream parar de enviar dados, a tentativa é abortada e repetida.
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
> Projeto independente, sem relação com o YouTube ou o Google. O YouTube muda seus endpoints com frequência; se algo parar de funcionar, atualize o pacote. Use apenas em conteúdo que você tem autorização para baixar e respeite os termos de uso do serviço.
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Para baixar o vídeo, combinar vídeo e áudio com FFmpeg e entregar somente o áudio final, use `video-audio`:
|
|
16
|
+
## Instalação
|
|
14
17
|
|
|
15
18
|
```bash
|
|
16
|
-
|
|
19
|
+
npm install yt-zmp
|
|
17
20
|
```
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
- Node.js **18.17 ou superior**.
|
|
23
|
+
- **FFmpeg** no `PATH` (ou informe `ffmpegPath`) para gerar MP3, combinar vídeo com áudio ou extrair áudio de um stream muxado. O formato `m4a` direto não precisa de FFmpeg.
|
|
24
|
+
- O `undici` é instalado como dependência opcional e só é usado quando há proxy.
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
O pacote é ESM. Em projetos CommonJS, use `import()` dinâmico:
|
|
24
27
|
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
node download-test.js "https://www.youtube.com/watch?v=VIDEO_ID" audio 0 mp3
|
|
28
|
+
```js
|
|
29
|
+
const { default: YouTube } = await import('yt-zmp');
|
|
28
30
|
```
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
## Uso rápido
|
|
31
33
|
|
|
32
34
|
```js
|
|
33
|
-
|
|
35
|
+
import YouTube from 'yt-zmp';
|
|
36
|
+
|
|
37
|
+
// Áudio em MP3
|
|
38
|
+
const audio = await YouTube.downloadMedia('https://youtu.be/VIDEO_ID', {
|
|
34
39
|
kind: 'audio',
|
|
35
|
-
audioFormat: '
|
|
36
|
-
|
|
40
|
+
audioFormat: 'mp3',
|
|
41
|
+
outputDir: './downloads'
|
|
37
42
|
});
|
|
38
|
-
|
|
43
|
+
console.log(audio.path, audio.title, audio.size);
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
// Vídeo até 720p, com áudio combinado
|
|
46
|
+
const video = await YouTube.downloadMedia('https://youtu.be/VIDEO_ID', {
|
|
47
|
+
kind: 'video',
|
|
48
|
+
height: 720
|
|
49
|
+
});
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
// Só metadados e formatos, sem baixar
|
|
52
|
+
const info = await YouTube.extract('https://youtu.be/VIDEO_ID');
|
|
53
|
+
console.log(info.title, info.client.name, info.formats.length);
|
|
54
|
+
```
|
|
43
55
|
|
|
44
|
-
|
|
56
|
+
A linha de comando faz o mesmo:
|
|
45
57
|
|
|
46
58
|
```bash
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
npx yt-zmp "https://youtu.be/VIDEO_ID" audio 0 mp3
|
|
60
|
+
npx yt-zmp "https://youtu.be/VIDEO_ID" video 720
|
|
61
|
+
npx yt-zmp "https://youtu.be/VIDEO_ID" muxed
|
|
50
62
|
```
|
|
51
63
|
|
|
52
|
-
|
|
64
|
+
Uso: `yt-zmp URL [audio|video|video-audio|muxed] [altura] [m4a|mp3]`. Lê `YOUTUBE_COOKIE_FILE`, `YOUTUBE_GVS_PO_TOKEN` e `YOUTUBE_PROXY` do ambiente.
|
|
53
65
|
|
|
54
|
-
|
|
66
|
+
## API
|
|
55
67
|
|
|
56
|
-
|
|
57
|
-
YOUTUBE_COOKIE_FILE=/caminho/privado/youtube-cookies.txt \
|
|
58
|
-
npm run auth:test -- "https://www.youtube.com/watch?v=VIDEO_ID"
|
|
59
|
-
```
|
|
68
|
+
### `YouTube.downloadMedia(input, options)`
|
|
60
69
|
|
|
61
|
-
|
|
70
|
+
`input` aceita link (`watch`, `youtu.be`, `shorts`, `embed`) ou o ID de 11 caracteres. Devolve `{ path, size, id, title, kind, audioFormat, format }`.
|
|
62
71
|
|
|
63
|
-
|
|
72
|
+
| Opção | Padrão | Descrição |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `kind` | `'muxed'` | `'audio'`, `'video'` ou `'muxed'` (vídeo com áudio no mesmo stream, em geral até 360p). |
|
|
75
|
+
| `audioFormat` | `'m4a'` | Para `kind: 'audio'`: `'m4a'` ou `'mp3'`. |
|
|
76
|
+
| `mp3Quality` | `'2'` | Qualidade LAME (0 melhor, 9 pior). |
|
|
77
|
+
| `height` | — | Altura máxima para vídeo. Se não existir, usa a melhor abaixo do limite. |
|
|
78
|
+
| `itag` | — | Força um itag específico. |
|
|
79
|
+
| `mergeAudio` | `true` | Em `kind: 'video'`, baixa o melhor áudio e combina com FFmpeg. |
|
|
80
|
+
| `outputDir` | `'./downloads'` | Pasta de saída. |
|
|
81
|
+
| `outputPath` | — | Caminho final do arquivo (ignora `outputDir`). |
|
|
82
|
+
| `retries` | `3` | Tentativas; a cada falha a extração é refeita com URL nova. |
|
|
83
|
+
| `client` | — | Força um cliente (`WEB`, `MWEB`, `ANDROID`, `IOS`, `TVHTML5`...). Sem isso, tenta em cadeia. |
|
|
84
|
+
| `proxy` | — | URL do proxy para esta chamada. Veja [Proxy](#proxy). |
|
|
85
|
+
| `stallTimeoutMs` | `30000` | Aborta o stream se ficar esse tempo sem receber dados. `0` desativa. |
|
|
86
|
+
| `cookieFile` | — | Arquivo de cookies Netscape. |
|
|
87
|
+
| `poToken` | `YOUTUBE_GVS_PO_TOKEN` | GVS PO token. |
|
|
88
|
+
| `headers` | — | Cabeçalhos extras. |
|
|
89
|
+
| `ffmpegPath` | `'ffmpeg'` | Caminho do executável. |
|
|
64
90
|
|
|
65
|
-
|
|
66
|
-
npm run download:test -- "https://www.youtube.com/watch?v=VIDEO_ID" muxed 720
|
|
67
|
-
```
|
|
91
|
+
Quando o cliente só fornece um stream muxado (como o itag 18), `kind: 'audio'` baixa esse arquivo e o FFmpeg extrai o áudio, em vez de falhar por falta de áudio adaptativo.
|
|
68
92
|
|
|
69
|
-
|
|
93
|
+
### `YouTube.downloadAudioFromVideo(input, options)`
|
|
70
94
|
|
|
71
|
-
|
|
72
|
-
- `audio`: melhor stream de áudio disponível;
|
|
73
|
-
- `video`: melhor stream de vídeo disponível, que pode não conter áudio.
|
|
95
|
+
Baixa o vídeo com áudio, extrai só o áudio com FFmpeg e apaga o temporário. Aceita as mesmas opções de rede de `downloadMedia`.
|
|
74
96
|
|
|
75
|
-
|
|
97
|
+
### `YouTube.extract(input, options)`
|
|
76
98
|
|
|
77
|
-
|
|
78
|
-
|
|
99
|
+
Devolve `{ id, title, author, channelId, duration, viewCount, thumbnail, formats, expiresInSeconds, client, poTokenApplied }`. Cada item de `formats` tem `itag`, `mimeType`, `quality`, `audioQuality`, `bitrate`, `width`, `height`, `fps`, `contentLength`, `url`, `hasUrl` e `sourceClient`.
|
|
100
|
+
|
|
101
|
+
Opções: `client`, `webOnly`, `includePlayerJson` (anexa o JSON bruto do Player API em `playerJson`), `proxy`, `cookieFile`, `auth`, `poToken`, `headers` e `pageRetries`.
|
|
102
|
+
|
|
103
|
+
### Seletores de formato
|
|
104
|
+
|
|
105
|
+
`findAudio`, `findVideo`, `findMuxed`, `sortAudio`, `sortVideo` e `sortMuxed` ignoram formatos sem URL. `findAudio(formats, { mimeType: 'audio/mp4' })` prioriza AAC (itag 140), mais compatível com conversão para MP3.
|
|
106
|
+
|
|
107
|
+
### Outras funções
|
|
108
|
+
|
|
109
|
+
`extractVideoId`, `normalizeUrl`, `download`, `downloadWithRetry`, `convertToMp3`, `mergeVideoAudio`, `extractAudioFromVideo`, `loadYouTubeAuth`, `setDefaultProxy` e `bridgeDownload` também estão em `YouTube` e como exportações nomeadas.
|
|
110
|
+
|
|
111
|
+
## Clientes do YouTube
|
|
112
|
+
|
|
113
|
+
Por padrão o módulo tenta `WEB`, `MWEB`, `ANDROID`, `IOS`, `TVHTML5` e variantes embutidas, nessa ordem, e usa o primeiro que devolver ao menos um formato com URL (ou `signatureCipher`). Respostas sem formatos utilizáveis, como as que trazem só `serverAbrStreamingUrl` (SABR), são descartadas e o próximo cliente é tentado.
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
// força um cliente; não há fallback para os outros
|
|
117
|
+
await YouTube.downloadMedia(url, { kind: 'audio', client: 'ANDROID' });
|
|
118
|
+
|
|
119
|
+
// só WEB, com o JSON bruto do Player API
|
|
120
|
+
const data = await YouTube.extract(url, { webOnly: true, includePlayerJson: true });
|
|
79
121
|
```
|
|
80
122
|
|
|
81
|
-
|
|
123
|
+
Variáveis de ambiente equivalentes: `YOUTUBE_PLAYER_CLIENT=ANDROID` e `YOUTUBE_WEB_ONLY=1`.
|
|
124
|
+
|
|
125
|
+
Cada cliente recebe respostas diferentes. Os clientes web costumam exigir PO token para os streams; o `ANDROID` costuma entregar URLs diretas, às vezes só no muxed itag 18. Para obter MP3 a partir de qualquer um deles basta ter FFmpeg.
|
|
126
|
+
|
|
127
|
+
## Uso em servidor ou hospedagem
|
|
128
|
+
|
|
129
|
+
O YouTube trata IPs de datacenter com mais desconfiança do que IPs residenciais. Em hospedagens, VPS e nuvem é comum receber "Faça login para confirmar que não é um bot", `UNPLAYABLE` sem `streamingData` ou `403` no `googlevideo.com`, mesmo com o mesmo código funcionando no seu computador ou celular. Um PO token ou cookies, sozinhos, não resolvem bloqueio por IP.
|
|
130
|
+
|
|
131
|
+
As saídas, da mais automática para a menos:
|
|
132
|
+
|
|
133
|
+
1. **Proxy residencial** com sessão fixa. Veja [Proxy](#proxy).
|
|
134
|
+
2. **Bridge**: outra máquina com IP residencial faz o download e o servidor só recebe o arquivo. Veja [Bridge](#bridge).
|
|
135
|
+
3. Rodar o código em uma máquina com IP residencial.
|
|
136
|
+
|
|
137
|
+
## Proxy
|
|
138
|
+
|
|
139
|
+
Todas as requisições (página, Player API, JavaScript do player e download do stream) podem sair por um proxy HTTP/HTTPS. Requer o pacote `undici`, instalado como dependência opcional.
|
|
82
140
|
|
|
83
141
|
```js
|
|
142
|
+
// 1. por chamada
|
|
84
143
|
await YouTube.downloadMedia(url, {
|
|
85
144
|
kind: 'audio',
|
|
86
|
-
|
|
87
|
-
mp3Quality: '2'
|
|
145
|
+
proxy: 'http://usuario:senha@host:porta'
|
|
88
146
|
});
|
|
147
|
+
|
|
148
|
+
// 2. padrão global do processo
|
|
149
|
+
YouTube.setDefaultProxy('http://usuario:senha@host:porta');
|
|
150
|
+
|
|
151
|
+
// 3. variável de ambiente
|
|
152
|
+
// YOUTUBE_PROXY=http://usuario:senha@host:porta
|
|
89
153
|
```
|
|
90
154
|
|
|
91
|
-
|
|
155
|
+
Prioridade: opção `proxy` da chamada, depois `setDefaultProxy`, depois `YOUTUBE_PROXY`. Sem nenhum deles, o módulo usa o `fetch` normal e não precisa do `undici`.
|
|
156
|
+
|
|
157
|
+
**Sessão fixa é obrigatória.** A URL do stream fica presa ao IP que fez a consulta. Se o proxy trocar de IP entre a consulta e o download, o resultado é `403`. Use a opção de sessão fixa (sticky) do seu provedor, em geral um identificador no usuário ou uma porta dedicada, conforme a documentação dele. Se o IP girar no meio de uma tentativa, `retries` refaz a extração com URL nova.
|
|
158
|
+
|
|
159
|
+
Caracteres especiais na senha precisam estar codificados na URL (`encodeURIComponent`). O consumo de dados é o do vídeo: um MP4 de 360p de poucos minutos fica em torno de 10 MB; áudio gasta menos.
|
|
92
160
|
|
|
93
|
-
|
|
161
|
+
## Bridge
|
|
94
162
|
|
|
95
|
-
|
|
163
|
+
O bridge divide o trabalho em duas pontas:
|
|
96
164
|
|
|
97
|
-
|
|
165
|
+
- **Servidor** (`yt-zmp-bridge`), em uma máquina com IP que o YouTube aceita, como um PC ou celular com Termux. Faz a consulta e o download.
|
|
166
|
+
- **Cliente** (`bridgeDownload`), na hospedagem. Pede o arquivo ao servidor e salva localmente.
|
|
167
|
+
|
|
168
|
+
### Servidor
|
|
98
169
|
|
|
99
170
|
```bash
|
|
100
|
-
|
|
171
|
+
BRIDGE_TOKEN='uma-senha-longa-e-aleatoria' npx yt-zmp-bridge
|
|
101
172
|
```
|
|
102
173
|
|
|
103
|
-
|
|
174
|
+
| Variável | Padrão | Descrição |
|
|
175
|
+
| --- | --- | --- |
|
|
176
|
+
| `BRIDGE_TOKEN` | obrigatória | Mínimo de 12 caracteres. Enviada no cabeçalho `x-token`. |
|
|
177
|
+
| `PORT` | `3000` | Porta. |
|
|
178
|
+
| `BRIDGE_HOST` | `127.0.0.1` | Endereço de escuta. |
|
|
179
|
+
| `BRIDGE_MAX_PARALLEL` | `2` | Downloads simultâneos; acima disso responde `429`. |
|
|
180
|
+
| `YOUTUBE_PLAYER_CLIENT` | `ANDROID` | Cliente usado no servidor. |
|
|
181
|
+
| `BRIDGE_COOKIE_FILE` | — | Cookies Netscape opcionais. |
|
|
182
|
+
|
|
183
|
+
Para áudio, o servidor tenta o stream de áudio puro e, se falhar, cai para o muxed itag 18. Quem recebe converte para MP3 com FFmpeg.
|
|
184
|
+
|
|
185
|
+
Para ser alcançado pela hospedagem sem abrir portas, use um túnel de saída, como `cloudflared tunnel --url http://localhost:3000`. Em túneis temporários a URL muda a cada reinício, e túneis gratuitos podem cortar respostas que demoram mais de cerca de 100 s sem enviar dados.
|
|
186
|
+
|
|
187
|
+
### Cliente
|
|
104
188
|
|
|
105
189
|
```js
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
190
|
+
import { bridgeDownload } from 'yt-zmp/bridge';
|
|
191
|
+
|
|
192
|
+
const r = await bridgeDownload('https://youtu.be/VIDEO_ID', './downloads/musica', {
|
|
193
|
+
bridgeUrl: 'https://seu-tunel.exemplo.com', // ou BRIDGE_URL
|
|
194
|
+
token: process.env.BRIDGE_TOKEN, // ou BRIDGE_TOKEN
|
|
195
|
+
kind: 'audio', // 'audio' | 'video' | 'muxed'
|
|
196
|
+
audioFormat: 'm4a'
|
|
110
197
|
});
|
|
198
|
+
// r.path => ./downloads/musica.m4a (a extensão vem do servidor)
|
|
111
199
|
```
|
|
112
200
|
|
|
113
|
-
|
|
201
|
+
Os erros dizem o que aconteceu: token diferente (`401`), túnel fora do ar (`530`), túnel cortou por demora (`524`), servidor ocupado (`429`) ou sem resposta no tempo limite (`timeoutMs`, padrão 10 min).
|
|
202
|
+
|
|
203
|
+
**Segurança:** quem tiver a URL e o token consegue usar o servidor para baixar vídeos pela sua conexão. Use um token longo e aleatório e não o publique em repositório.
|
|
204
|
+
|
|
205
|
+
## Cookies
|
|
114
206
|
|
|
115
|
-
|
|
207
|
+
Para vídeos que exigem conta, aceita um arquivo de cookies Netscape. Use apenas cookies da sua própria conta e mantenha o arquivo fora do repositório.
|
|
116
208
|
|
|
117
209
|
```js
|
|
118
|
-
|
|
210
|
+
await YouTube.downloadMedia(url, { kind: 'audio', cookieFile: '/caminho/privado/cookies.txt' });
|
|
211
|
+
```
|
|
119
212
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
213
|
+
Os cookies e o `SAPISIDHASH` são enviados só ao YouTube, nunca ao `googlevideo.com`. Cookies não garantem a superação de CAPTCHA ou de bloqueio por IP. Para conferir a sessão sem baixar nada:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
YOUTUBE_COOKIE_FILE=/caminho/privado/cookies.txt npm run auth:check -- "URL"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
O comando informa quantos cookies foram lidos e se o YouTube respondeu, sem imprimir valores.
|
|
220
|
+
|
|
221
|
+
## GVS PO token
|
|
222
|
+
|
|
223
|
+
Algumas respostas exigem um GVS PO token nas URLs de mídia. O yt-zmp **não gera** esse token; se você tem um legítimo para a sessão e o cliente, informe-o e ele será anexado como `pot` às URLs:
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
await YouTube.downloadMedia(url, { kind: 'video', poToken: process.env.YOUTUBE_GVS_PO_TOKEN });
|
|
124
227
|
```
|
|
125
228
|
|
|
126
|
-
|
|
229
|
+
Sem o token, alguns formatos aparecem na extração mas dão `403` no download. Não imprima nem versione o valor.
|
|
230
|
+
|
|
231
|
+
## Erros comuns
|
|
232
|
+
|
|
233
|
+
| Sintoma | Causa provável |
|
|
234
|
+
| --- | --- |
|
|
235
|
+
| `sem formatos utilizáveis [UNPLAYABLE, sem streamingData]` | O YouTube recusou o cliente ou o IP. Tente outro `client`, um proxy residencial ou o bridge. |
|
|
236
|
+
| `403` no stream | URL presa a outro IP (proxy sem sessão fixa, extração e download em redes diferentes), PO token ausente ou URL expirada (`expiresInSeconds`). |
|
|
237
|
+
| `nenhuma URL pôde ser resolvida` | Falha ao decifrar a assinatura do player: o `cipher.js` precisa acompanhar uma mudança do YouTube. Atualize o pacote. |
|
|
238
|
+
| `Download travado: sem dados por 30s` | O stream parou de enviar. A tentativa é repetida; ajuste `stallTimeoutMs` se a rede for lenta. |
|
|
239
|
+
| `FFmpeg não encontrado` | Instale o FFmpeg ou informe `ffmpegPath`. |
|
|
240
|
+
|
|
241
|
+
Não reutilize nem salve URLs do `googlevideo.com`: são assinadas, expiram rápido e podem ficar presas ao IP da extração. O `downloadMedia` extrai uma URL nova a cada tentativa.
|
|
242
|
+
|
|
243
|
+
## Variáveis de ambiente
|
|
244
|
+
|
|
245
|
+
| Variável | Uso |
|
|
246
|
+
| --- | --- |
|
|
247
|
+
| `YOUTUBE_PROXY` | Proxy padrão. |
|
|
248
|
+
| `YOUTUBE_PLAYER_CLIENT` | Cliente forçado. |
|
|
249
|
+
| `YOUTUBE_WEB_ONLY` | `1` para usar só o cliente WEB. |
|
|
250
|
+
| `YOUTUBE_GVS_PO_TOKEN` | GVS PO token. |
|
|
251
|
+
| `YOUTUBE_COOKIE_FILE` | Cookies, lidos pela linha de comando. |
|
|
252
|
+
| `BRIDGE_URL`, `BRIDGE_TOKEN` | Padrões de `bridgeDownload`. |
|
|
253
|
+
|
|
254
|
+
## Desenvolvimento
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
npm test
|
|
258
|
+
```
|
|
127
259
|
|
|
128
|
-
|
|
260
|
+
Os testes rodam offline e cobrem links, seletores de formato, filtro de respostas, proxy, detecção de travamento no download e o cliente do bridge. Os scripts em `scripts/` fazem testes manuais contra o YouTube e não vão no pacote publicado.
|
|
129
261
|
|
|
130
|
-
##
|
|
262
|
+
## Licença
|
|
131
263
|
|
|
132
|
-
|
|
264
|
+
MIT.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import YouTube from '../index.js';
|
|
2
3
|
|
|
3
4
|
const url = process.argv[2];
|
|
4
5
|
const kind = process.argv[3] || 'muxed';
|
|
@@ -8,7 +9,7 @@ const height = process.argv[4]
|
|
|
8
9
|
const audioFormat = process.argv[5] || 'm4a';
|
|
9
10
|
|
|
10
11
|
if (!url) {
|
|
11
|
-
console.log('Uso:
|
|
12
|
+
console.log('Uso: yt-zmp "URL_DO_YOUTUBE" [audio|video|video-audio|muxed] [altura] [m4a|mp3]');
|
|
12
13
|
process.exit(1);
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -28,13 +29,14 @@ if (height !== undefined && !Number.isFinite(height)) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
try {
|
|
31
|
-
console.log('\
|
|
32
|
+
console.log('\nConsultando e baixando mídia do YouTube...\n');
|
|
32
33
|
|
|
33
34
|
const downloadOptions = {
|
|
34
35
|
height,
|
|
35
36
|
audioFormat,
|
|
36
37
|
outputDir: './downloads',
|
|
37
38
|
cookieFile: process.env.YOUTUBE_COOKIE_FILE,
|
|
39
|
+
proxy: process.env.YOUTUBE_PROXY,
|
|
38
40
|
poToken: process.env.YOUTUBE_GVS_PO_TOKEN
|
|
39
41
|
};
|
|
40
42
|
const result = kind === 'video-audio'
|
package/index.js
CHANGED
|
@@ -1,37 +1,16 @@
|
|
|
1
|
+
import { extractVideoId, normalizeUrl } from './src/parser.js';
|
|
2
|
+
import { extract } from './src/extractor.js';
|
|
3
|
+
import { download, downloadWithRetry } from './src/downloader.js';
|
|
4
|
+
import { downloadMedia, downloadAudioFromVideo } from './src/media.js';
|
|
5
|
+
import { loadYouTubeAuth } from './src/auth.js';
|
|
6
|
+
import { convertToMp3, mergeVideoAudio, extractAudioFromVideo } from './src/transcoder.js';
|
|
7
|
+
import { setDefaultProxy } from './src/proxy.js';
|
|
8
|
+
import { bridgeDownload } from './src/bridge-client.js';
|
|
1
9
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
extract
|
|
8
|
-
} from './extractor.js';
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
download,
|
|
12
|
-
downloadWithRetry
|
|
13
|
-
} from './downloader.js';
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
downloadMedia,
|
|
17
|
-
downloadAudioFromVideo
|
|
18
|
-
} from './media.js';
|
|
19
|
-
import {
|
|
20
|
-
loadYouTubeAuth
|
|
21
|
-
} from './auth.js';
|
|
22
|
-
|
|
23
|
-
import {
|
|
24
|
-
createFormat,
|
|
25
|
-
isAudio,
|
|
26
|
-
isVideo,
|
|
27
|
-
isMuxed,
|
|
28
|
-
sortAudio,
|
|
29
|
-
sortVideo,
|
|
30
|
-
sortMuxed,
|
|
31
|
-
findAudio,
|
|
32
|
-
findVideo,
|
|
33
|
-
findMuxed
|
|
34
|
-
} from './formats.js';
|
|
10
|
+
createFormat, isAudio, isVideo, isMuxed,
|
|
11
|
+
sortAudio, sortVideo, sortMuxed,
|
|
12
|
+
findAudio, findVideo, findMuxed
|
|
13
|
+
} from './src/formats.js';
|
|
35
14
|
|
|
36
15
|
export const YouTube = {
|
|
37
16
|
extractVideoId,
|
|
@@ -44,23 +23,31 @@ export const YouTube = {
|
|
|
44
23
|
loadYouTubeAuth,
|
|
45
24
|
|
|
46
25
|
createFormat,
|
|
47
|
-
|
|
48
26
|
isAudio,
|
|
49
27
|
isVideo,
|
|
50
28
|
isMuxed,
|
|
51
|
-
|
|
52
29
|
sortAudio,
|
|
53
30
|
sortVideo,
|
|
54
31
|
sortMuxed,
|
|
55
|
-
|
|
56
32
|
findAudio,
|
|
57
33
|
findVideo,
|
|
58
34
|
findMuxed,
|
|
59
35
|
|
|
60
36
|
download,
|
|
61
|
-
downloadWithRetry
|
|
37
|
+
downloadWithRetry,
|
|
38
|
+
|
|
39
|
+
convertToMp3,
|
|
40
|
+
mergeVideoAudio,
|
|
41
|
+
extractAudioFromVideo,
|
|
42
|
+
|
|
43
|
+
setDefaultProxy,
|
|
44
|
+
bridgeDownload
|
|
62
45
|
};
|
|
63
46
|
|
|
64
|
-
export {
|
|
47
|
+
export {
|
|
48
|
+
loadYouTubeAuth,
|
|
49
|
+
setDefaultProxy,
|
|
50
|
+
bridgeDownload
|
|
51
|
+
};
|
|
65
52
|
|
|
66
53
|
export default YouTube;
|
package/package.json
CHANGED
|
@@ -1,36 +1,45 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "yt-zmp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"main": "./index.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./index.js"
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
2
|
+
"name": "yt-zmp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Extração e download de áudio e vídeo do YouTube em Node.js, sem yt-dlp. Suporta proxy, cookies, PO token e um bridge para servidores com IP bloqueado.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./bridge": "./src/bridge-client.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"yt-zmp": "./bin/yt-zmp.js",
|
|
14
|
+
"yt-zmp-bridge": "./bin/yt-zmp-bridge.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"index.js",
|
|
18
|
+
"src/",
|
|
19
|
+
"bin/",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"CHANGELOG.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "node --test",
|
|
26
|
+
"auth:check": "node scripts/cookie-check.js"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.17"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"youtube",
|
|
33
|
+
"youtube-downloader",
|
|
34
|
+
"youtube-audio",
|
|
35
|
+
"youtube-mp3",
|
|
36
|
+
"downloader",
|
|
37
|
+
"extractor",
|
|
38
|
+
"proxy",
|
|
39
|
+
"whatsapp-bot"
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"optionalDependencies": {
|
|
43
|
+
"undici": "^6.21.0"
|
|
44
|
+
}
|
|
45
|
+
}
|