Yet Another Youtube Down Loader

⌈⌋ branch:  yaydl


Check-in [6f4c3accee]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Done. (Only YouTube for now.)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 6f4c3acceee83e418449504bdc42a8561f8f997560e3b8e2512b1549633dff6e
User & Date: Cthulhux 2020-11-08 18:00:04
Context
2020-11-08
18:01
Fixed link check-in: ee258c7577 user: Cthulhux tags: trunk
18:00
Done. (Only YouTube for now.) check-in: 6f4c3accee user: Cthulhux tags: trunk
17:55
initial empty check-in check-in: 90954bc286 user: Cthulhux tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added CODE_OF_CONDUCT.md.









































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# Code of Merit

1. The project creators, lead developers, core team, constitute
the managing members of the project and have final say in every decision
of the project, technical or otherwise, including overruling previous decisions.
There are no limitations to this decisional power.

2. Contributions are an expected result of your membership on the project.
Don't expect others to do your work or help you with your work forever. 

3. All members have the same opportunities to seek any challenge they want
within the project. 

4. Authority or position in the project will be proportional
to the accrued contribution. Seniority must be earned.

5. Software is evolutive: the better implementations must supersede lesser
implementations. Technical advantage is the primary evaluation metric.

6. This is a space for technical prowess; topics outside of the project
will not be tolerated.

7. Non technical conflicts will be discussed in a separate space. Disruption
of the project will not be allowed.

8. Individual characteristics, including but not limited to,
body, sex, sexual preference, race, language, religion, nationality,
or political preferences are irrelevant in the scope of the project and
will not be taken into account concerning your value or that of your contribution
to the project.

9. Discuss or debate the idea, not the person.

10. There is no room for ambiguity: Ambiguity will be met with questioning;
further ambiguity will be met with silence. It is the responsibility
of the originator to provide requested context.

11. If something is illegal outside the scope of the project, it is illegal
in the scope of the project. This Code of Merit does not take precedence over
governing law.

12. This Code of Merit governs the technical procedures of the project not the 
activities outside of it. 

13. Participation on the project equates to agreement of this Code of Merit.

14. No objectives beyond the stated objectives of this project are relevant
to the project. Any intent to deviate the project from its original purpose
of existence will constitute grounds for remedial action which may include
expulsion from the project.

This document is the Code of Merit, version 1.0.

Added Cargo.toml.































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[package]
name = "yaydl"
version = "0.1.0"
authors = ["Cthulhux <git@tuxproject.de>"]
edition = "2018"

[dependencies]
anyhow = "1.0"
clap = "3.0.0-beta.2"
indicatif = "0.15.0"
inventory = "0.1.9"
qstring = "0.7.2"
regex = "1.4.1"
reqwest = { version = "0.10.8", features = ["blocking"] }
serde_json = "1.0"

Added LICENSE.

















































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0

1. Definitions.

    1.1. "Contributor" means each individual or entity that creates
         or contributes to the creation of Modifications.

    1.2. "Contributor Version" means the combination of the Original
         Software, prior Modifications used by a Contributor (if any),
         and the Modifications made by that particular Contributor.

    1.3. "Covered Software" means (a) the Original Software, or (b)
         Modifications, or (c) the combination of files containing
         Original Software with files containing Modifications, in
         each case including portions thereof.

    1.4. "Executable" means the Covered Software in any form other
         than Source Code.

    1.5. "Initial Developer" means the individual or entity that first
         makes Original Software available under this License.

    1.6. "Larger Work" means a work which combines Covered Software or
         portions thereof with code not governed by the terms of this
         License.

    1.7. "License" means this document.

    1.8. "Licensable" means having the right to grant, to the maximum
         extent possible, whether at the time of the initial grant or
         subsequently acquired, any and all of the rights conveyed
         herein.

    1.9. "Modifications" means the Source Code and Executable form of
         any of the following:

        A. Any file that results from an addition to, deletion from or
           modification of the contents of a file containing Original
           Software or previous Modifications;

        B. Any new file that contains any part of the Original
           Software or previous Modifications; or

        C. Any new file that is contributed or otherwise made
           available under the terms of this License.

    1.10. "Original Software" means the Source Code and Executable
          form of computer software code that is originally released
          under this License.

    1.11. "Patent Claims" means any patent claim(s), now owned or
          hereafter acquired, including without limitation, method,
          process, and apparatus claims, in any patent Licensable by
          grantor.

    1.12. "Source Code" means (a) the common form of computer software
          code in which modifications are made and (b) associated
          documentation included in or with such code.

    1.13. "You" (or "Your") means an individual or a legal entity
          exercising rights under, and complying with all of the terms
          of, this License.  For legal entities, "You" includes any
          entity which controls, is controlled by, or is under common
          control with You.  For purposes of this definition,
          "control" means (a) the power, direct or indirect, to cause
          the direction or management of such entity, whether by
          contract or otherwise, or (b) ownership of more than fifty
          percent (50%) of the outstanding shares or beneficial
          ownership of such entity.

2. License Grants.

    2.1. The Initial Developer Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, the Initial
    Developer hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Initial Developer, to use,
            reproduce, modify, display, perform, sublicense and
            distribute the Original Software (or portions thereof),
            with or without Modifications, and/or as part of a Larger
            Work; and

        (b) under Patent Claims infringed by the making, using or
            selling of Original Software, to make, have made, use,
            practice, sell, and offer for sale, and/or otherwise
            dispose of the Original Software (or portions thereof).

        (c) The licenses granted in Sections 2.1(a) and (b) are
            effective on the date Initial Developer first distributes
            or otherwise makes the Original Software available to a
            third party under the terms of this License.

        (d) Notwithstanding Section 2.1(b) above, no patent license is
            granted: (1) for code that You delete from the Original
            Software, or (2) for infringements caused by: (i) the
            modification of the Original Software, or (ii) the
            combination of the Original Software with other software
            or devices.

    2.2. Contributor Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, each
    Contributor hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Contributor to use, reproduce,
            modify, display, perform, sublicense and distribute the
            Modifications created by such Contributor (or portions
            thereof), either on an unmodified basis, with other
            Modifications, as Covered Software and/or as part of a
            Larger Work; and

        (b) under Patent Claims infringed by the making, using, or
            selling of Modifications made by that Contributor either
            alone and/or in combination with its Contributor Version
            (or portions of such combination), to make, use, sell,
            offer for sale, have made, and/or otherwise dispose of:
            (1) Modifications made by that Contributor (or portions
            thereof); and (2) the combination of Modifications made by
            that Contributor with its Contributor Version (or portions
            of such combination).

        (c) The licenses granted in Sections 2.2(a) and 2.2(b) are
            effective on the date Contributor first distributes or
            otherwise makes the Modifications available to a third
            party.

        (d) Notwithstanding Section 2.2(b) above, no patent license is
            granted: (1) for any code that Contributor has deleted
            from the Contributor Version; (2) for infringements caused
            by: (i) third party modifications of Contributor Version,
            or (ii) the combination of Modifications made by that
            Contributor with other software (except as part of the
            Contributor Version) or other devices; or (3) under Patent
            Claims infringed by Covered Software in the absence of
            Modifications made by that Contributor.

3. Distribution Obligations.

    3.1. Availability of Source Code.

    Any Covered Software that You distribute or otherwise make
    available in Executable form must also be made available in Source
    Code form and that Source Code form must be distributed only under
    the terms of this License.  You must include a copy of this
    License with every copy of the Source Code form of the Covered
    Software You distribute or otherwise make available.  You must
    inform recipients of any such Covered Software in Executable form
    as to how they can obtain such Covered Software in Source Code
    form in a reasonable manner on or through a medium customarily
    used for software exchange.

    3.2. Modifications.

    The Modifications that You create or to which You contribute are
    governed by the terms of this License.  You represent that You
    believe Your Modifications are Your original creation(s) and/or
    You have sufficient rights to grant the rights conveyed by this
    License.

    3.3. Required Notices.

    You must include a notice in each of Your Modifications that
    identifies You as the Contributor of the Modification.  You may
    not remove or alter any copyright, patent or trademark notices
    contained within the Covered Software, or any notices of licensing
    or any descriptive text giving attribution to any Contributor or
    the Initial Developer.

    3.4. Application of Additional Terms.

    You may not offer or impose any terms on any Covered Software in
    Source Code form that alters or restricts the applicable version
    of this License or the recipients' rights hereunder.  You may
    choose to offer, and to charge a fee for, warranty, support,
    indemnity or liability obligations to one or more recipients of
    Covered Software.  However, you may do so only on Your own behalf,
    and not on behalf of the Initial Developer or any Contributor.
    You must make it absolutely clear that any such warranty, support,
    indemnity or liability obligation is offered by You alone, and You
    hereby agree to indemnify the Initial Developer and every
    Contributor for any liability incurred by the Initial Developer or
    such Contributor as a result of warranty, support, indemnity or
    liability terms You offer.

    3.5. Distribution of Executable Versions.

    You may distribute the Executable form of the Covered Software
    under the terms of this License or under the terms of a license of
    Your choice, which may contain terms different from this License,
    provided that You are in compliance with the terms of this License
    and that the license for the Executable form does not attempt to
    limit or alter the recipient's rights in the Source Code form from
    the rights set forth in this License.  If You distribute the
    Covered Software in Executable form under a different license, You
    must make it absolutely clear that any terms which differ from
    this License are offered by You alone, not by the Initial
    Developer or Contributor.  You hereby agree to indemnify the
    Initial Developer and every Contributor for any liability incurred
    by the Initial Developer or such Contributor as a result of any
    such terms You offer.

    3.6. Larger Works.

    You may create a Larger Work by combining Covered Software with
    other code not governed by the terms of this License and
    distribute the Larger Work as a single product.  In such a case,
    You must make sure the requirements of this License are fulfilled
    for the Covered Software.

4. Versions of the License.

    4.1. New Versions.

    Sun Microsystems, Inc. is the initial license steward and may
    publish revised and/or new versions of this License from time to
    time.  Each version will be given a distinguishing version number.
    Except as provided in Section 4.3, no one other than the license
    steward has the right to modify this License.

    4.2. Effect of New Versions.

    You may always continue to use, distribute or otherwise make the
    Covered Software available under the terms of the version of the
    License under which You originally received the Covered Software.
    If the Initial Developer includes a notice in the Original
    Software prohibiting it from being distributed or otherwise made
    available under any subsequent version of the License, You must
    distribute and make the Covered Software available under the terms
    of the version of the License under which You originally received
    the Covered Software.  Otherwise, You may also choose to use,
    distribute or otherwise make the Covered Software available under
    the terms of any subsequent version of the License published by
    the license steward.

    4.3. Modified Versions.

    When You are an Initial Developer and You want to create a new
    license for Your Original Software, You may create and use a
    modified version of this License if You: (a) rename the license
    and remove any references to the name of the license steward
    (except to note that the license differs from this License); and
    (b) otherwise make it clear that the license contains terms which
    differ from this License.

5. DISCLAIMER OF WARRANTY.

    COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
    BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
    INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
    SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
    PURPOSE OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND
    PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU.  SHOULD ANY
    COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
    INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
    NECESSARY SERVICING, REPAIR OR CORRECTION.  THIS DISCLAIMER OF
    WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.  NO USE OF
    ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
    DISCLAIMER.

6. TERMINATION.

    6.1. This License and the rights granted hereunder will terminate
    automatically if You fail to comply with terms herein and fail to
    cure such breach within 30 days of becoming aware of the breach.
    Provisions which, by their nature, must remain in effect beyond
    the termination of this License shall survive.

    6.2. If You assert a patent infringement claim (excluding
    declaratory judgment actions) against Initial Developer or a
    Contributor (the Initial Developer or Contributor against whom You
    assert such claim is referred to as "Participant") alleging that
    the Participant Software (meaning the Contributor Version where
    the Participant is a Contributor or the Original Software where
    the Participant is the Initial Developer) directly or indirectly
    infringes any patent, then any and all rights granted directly or
    indirectly to You by such Participant, the Initial Developer (if
    the Initial Developer is not the Participant) and all Contributors
    under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
    notice from Participant terminate prospectively and automatically
    at the expiration of such 60 day notice period, unless if within
    such 60 day period You withdraw Your claim with respect to the
    Participant Software against such Participant either unilaterally
    or pursuant to a written agreement with Participant.

    6.3. In the event of termination under Sections 6.1 or 6.2 above,
    all end user licenses that have been validly granted by You or any
    distributor hereunder prior to termination (excluding licenses
    granted to You by any distributor) shall survive termination.

7. LIMITATION OF LIABILITY.

    UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
    (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
    INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
    COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
    LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
    CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
    LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
    STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
    COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
    INFORMED OF THE POSSIBILITY OF SUCH DAMAGES.  THIS LIMITATION OF
    LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
    INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
    APPLICABLE LAW PROHIBITS SUCH LIMITATION.  SOME JURISDICTIONS DO
    NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
    CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
    APPLY TO YOU.

8. U.S. GOVERNMENT END USERS.

    The Covered Software is a "commercial item," as that term is
    defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
    computer software" (as that term is defined at 48
    C.F.R. 252.227-7014(a)(1)) and "commercial computer software
    documentation" as such terms are used in 48 C.F.R. 12.212
    (Sept. 1995).  Consistent with 48 C.F.R. 12.212 and 48
    C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
    U.S. Government End Users acquire Covered Software with only those
    rights set forth herein.  This U.S. Government Rights clause is in
    lieu of, and supersedes, any other FAR, DFAR, or other clause or
    provision that addresses Government rights in computer software
    under this License.

9. MISCELLANEOUS.

    This License represents the complete agreement concerning subject
    matter hereof.  If any provision of this License is held to be
    unenforceable, such provision shall be reformed only to the extent
    necessary to make it enforceable.  This License shall be governed
    by the law of the jurisdiction specified in a notice contained
    within the Original Software (except to the extent applicable law,
    if any, provides otherwise), excluding such jurisdiction's
    conflict-of-law provisions.  Any litigation relating to this
    License shall be subject to the jurisdiction of the courts located
    in the jurisdiction and venue specified in a notice contained
    within the Original Software, with the losing party responsible
    for costs, including, without limitation, court costs and
    reasonable attorneys' fees and expenses.  The application of the
    United Nations Convention on Contracts for the International Sale
    of Goods is expressly excluded.  Any law or regulation which
    provides that the language of a contract shall be construed
    against the drafter shall not apply to this License.  You agree
    that You alone are responsible for compliance with the United
    States export administration regulations (and the export control
    laws and regulation of any other countries) when You use,
    distribute or otherwise make available any Covered Software.

10. RESPONSIBILITY FOR CLAIMS.

    As between Initial Developer and the Contributors, each party is
    responsible for claims and damages arising, directly or
    indirectly, out of its utilization of rights under this License
    and You agree to work with Initial Developer and Contributors to
    distribute such responsibility on an equitable basis.  Nothing
    herein is intended or shall be deemed to constitute any admission
    of liability.

--------------------------------------------------------------------

NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND
DISTRIBUTION LICENSE (CDDL)

For Covered Software in this distribution, this License shall
be governed by the laws of Germany (excluding conflict-of-law
provisions).

Any litigation relating to this License shall be subject to the
jurisdiction and the courts of Braunschweig, Germany, with venue
lying in Braunschweig, Germany.

Added README.md.















































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# yaydl

yet another youtube down loader

    % yaydl "https://www.youtube.com/watch?v=jNQXAC9IVRw"

## How? What? Why?

    % yaydl --help

# Features

* Can download videos.
* Can optionally keep only the audio part of them (requires `ffmpeg`).
* Comes as a single binary (once compiled) - take it everywhere on your thumbdrive, no Python cruft required.

## Non-features

The list of features is deliberately kept short:

* No output quality choice. `yaydl` assumes that your internet connection is good enough, or else you would stream, not download.
* No complex filters. This is a downloading tool.
* No image file support. Videos only.

## Missing features (patches are welcome)

* `yaydl` currently ignores video meta data (except the title) unless they are a part of the video file.
* Playlists are not supported yet.

## How to install

### From the source code

Install Rust (e.g. with [rustup](https://rustup.rs)), then:

**using Fossil:**

    % fossil clone https://code.rosaelefanten.org/yaydl yaydl.fossil
    % mkdir yaydl ; cd yaydl ; fossil open ../yaydl.fossil
    % cargo build --release

**using Git:**

    % git clone https://github.com/dertuxmalwieder/yaydl
    % cd yaydl
    % cargo build --release

### From your package manager

Nobody has provided any packages for `yaydl` yet. You can help!

## How to contribute code

1. Read and agree to the [Code of ~~Conduct~~ Merit](CODE_OF_MERIT.md).
2. Implicitly agree to the [LICENSE](LICENSE). Nobody reads those. I don't either.
3. Find out if anyone has filed a GitHub Issue or even sent a Pull Request yet. Act accordingly.
4. Send me a patch, either via e-mail (`yaydl at tuxproject dot de`), on the IRC or as a GitHub Pull Request. Note that GitHub only provides a mirror, so you'd double my work if you choose the latter. :-)

If you do that well (and regularly) enough, I'll probably grant you commit access to the upstream Fossil repository.

### Add new definitions

1. Implement `definitions::SiteDefinition` as `handlers/<YourSite>.rs`.
2. Push the new handler to the inventory: `inventory::submit! {  &YourSiteHandler as &dyn SiteDefinition }`
3. Add the new module to `handlers.rs`.
4. Optionally, add new requirements to `Cargo.toml`.
5. Send me a patch, preferably with an example. (I cannot know all sites.)

#### Minimal example that does nothing

```rust
// handlers/noop.rs

use anyhow::Result;
use crate::definitions::SiteDefinition;

struct NoopExampleHandler;
impl SiteDefinition for NoopExampleHandler {
    fn can_handle_url<'a>(&'a self, url: &'a str) -> bool {
        // Return true here, if <url> can be covered by this handler.
        // Note that yaydl will skip all other handlers then.
        true
    }
    
    fn does_video_exist<'a>(&'a self, url: &'a str) -> Result<bool> {
    	// Return true here, if the video exists.
    	false
    }
    
    fn find_video_title<'a>(&'a self, url: &'a str) -> Result<String> {
        // Return the video title from <url> here.
        Ok("".to_string())
    }
    
    fn find_video_direct_url<'a>(&'a self, url: &'a str) -> Result<String> {
    	  // Return the direct download URL of the video here.
    	  Ok("".to_string())
    }

    fn find_video_file_extension<'a>(&'a self, url: &'a str) -> Result<String> {
        // Return the designated file extension of the video here.
        Ok("mp4".to_string())
    }

    fn display_name<'a>(&'a self) -> String {
        // For cosmetics, this is the display name of this handler.
        "NoopExample"
    }
}
   
// Push the site definition to the list of known handlers:
inventory::submit! {
    &NoopExampleHandler as &dyn SiteDefinition
}
```

### Fix some bugs or add new features

1. Do so.
2. Send me a patch.

## Donations

Writing this software and keeping it available is eating some of the time which most people would spend with their friends. Naturally, I absolutely accept financial compensation.

* PayPal: [GebtmireuerGeld](https://paypal.me/gebtmireuergeld)
* Liberapay: [Cthulhux](https://liberapay.com/Cthulhux/donate)

Thank you.

## Contact

* Twitter: [@tux0r](https://twitter.com/tux0r)
* IRC: `irc.freenode.net/yaydl`
* Matrix: @Cthulhux:matrix.org

Added src/definitions.rs.

















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
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
37
38
39
40
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file LICENSE in this distribution for details.
 * A copy of the CDDL is also available via the Internet at
 * http://www.opensource.org/licenses/cddl1.txt
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the contents of the LICENSE file from this
 * distribution.
 */

// Yet Another Youtube Down Loader
// - definitions.rs file -

use anyhow::Result;

// Define the public interface for site definitions:
pub trait SiteDefinition {
    // true, if this site can handle <url>.
    fn can_handle_url<'a>(&'a self, url: &'a str) -> bool;

    // true, if the video exists.
    fn does_video_exist<'a>(&'a self, url: &'a str) -> Result<bool>;

    // returns the title of a video.
    fn find_video_title<'a>(&'a self, url: &'a str) -> Result<String>;

    // returns the download URL of a video.
    fn find_video_direct_url<'a>(&'a self, url: &'a str) -> Result<String>;

    // returns the file extension of the video (e.g. "mp4").
    fn find_video_file_extension<'a>(&'a self, url: &'a str) -> Result<String>;

    // returns the name of the site (e.g. "YouTube").
    fn display_name<'a>(&'a self) -> String;
}

Added src/ffmpeg.rs.







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
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
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file LICENSE in this distribution for details.
 * A copy of the CDDL is also available via the Internet at
 * http://www.opensource.org/licenses/cddl1.txt
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the contents of the LICENSE file from this
 * distribution.
 */

// Yet Another Youtube Down Loader
// - ffmpeg.rs file -

use std::path::Path;
use std::process::Command;

// It makes very little sense to link ffmpeg statically with yaydl.
// Just use the system's one (or inform the user if there isn't one).

pub fn to_audio(inputfile: &Path, outputfile: &Path) {
    Command::new("ffmpeg")
        .arg("-i")
        .arg(inputfile)
        .arg("-vn") // Skip the video streams.
        .arg("-loglevel")
        .arg("quiet") // Shut the fuck up.
        .arg(outputfile)
        .output()
        .expect("Could not find ffmpeg - no processing is done.");
}

Added src/handlers.rs.











































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file LICENSE in this distribution for details.
 * A copy of the CDDL is also available via the Internet at
 * http://www.opensource.org/licenses/cddl1.txt
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the contents of the LICENSE file from this
 * distribution.
 */

// Yet Another Youtube Down Loader
// - handlers.rs file -

mod youtube;

// Add your own modules here.

Added src/handlers/youtube.rs.





































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file LICENSE in this distribution for details.
 * A copy of the CDDL is also available via the Internet at
 * http://www.opensource.org/licenses/cddl1.txt
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the contents of the LICENSE file from this
 * distribution.
 */

// Yet Another Youtube Down Loader
// - YouTube handler -

use crate::definitions::SiteDefinition;

use anyhow::Result;
use qstring::QString;
use regex::Regex;
use serde_json::{json, Value};

static mut VIDEO_INFO: String = String::new();
static mut VIDEO_MIME: String = String::new();

unsafe fn get_video_info(id: &str) -> Result<Value> {
    if VIDEO_INFO.is_empty() {
        // We need to fetch the video information first.
        let video_url = format!(
            "https://www.youtube.com/get_video_info?video_id={}&el=embedded&ps=default",
            id
        );
        let res = reqwest::blocking::get(video_url.as_str())?;
        let body = res.text()?;

        // Try to find the encoded JSON object in the response.
        let qs = QString::from(body.as_str());
        let json = qs.get("player_response").unwrap_or("");

        VIDEO_INFO = json.replace("+", " ");
    }

    // Return it:
    let v: Value = serde_json::from_str(&VIDEO_INFO)?;
    Ok(v)
}

// Implement the site definition:
struct YouTubeHandler;
impl SiteDefinition for YouTubeHandler {
    fn can_handle_url<'a>(&'a self, url: &'a str) -> bool {
        Regex::new(r"(?:www\.)?youtu(?:be.com|be)/")
            .unwrap()
            .is_match(url)
    }

    fn find_video_title<'a>(&'a self, url: &'a str) -> Result<String> {
        let id_regex = Regex::new(r"(?:v=|.be/)(.*$)").unwrap();
        let id = id_regex.captures(url).unwrap().get(1).unwrap().as_str();
        unsafe {
            let video_info = get_video_info(id)?;
            let video_info_title = video_info["videoDetails"]["title"].as_str().unwrap_or("");

            Ok(String::from(video_info_title))
        }
    }

    fn find_video_direct_url<'a>(&'a self, url: &'a str) -> Result<String> {
        let id_regex = Regex::new(r"(?:v=|.be/)(.*$)").unwrap();
        let id = id_regex.captures(url).unwrap().get(1).unwrap().as_str();
        unsafe {
            let video_info = get_video_info(id)?;
            let video_info_itags = match video_info["streamingData"]["formats"].as_array() {
                None => return Ok("".to_string()),
                Some(itags) => itags,
            };
            let video_info_itags_adaptive =
                match video_info["streamingData"]["adaptiveFormats"].as_array() {
                    None => return Ok("".to_string()),
                    Some(itags) => itags,
                };

            let mut url_to_choose = "";

            // Finding the least horrible combination of video and audio:
            let vq1 = "tiny";
            let vq2 = "small";
            let vq3 = "medium";
            let vq4 = "large";
            let vq5 = "hd720";
            let vq6 = "hd1080";
            let mut last_vq = "".to_string();

            let aq1 = "AUDIO_QUALITY_LOW";
            let aq2 = "AUDIO_QUALITY_MEDIUM";
            let aq3 = "AUDIO_QUALITY_HIGH";
            let mut last_aq = "".to_string();

            for itag in video_info_itags.iter().chain(video_info_itags_adaptive) {
                // The highest quality wins.
                let this_aq = itag["audioQuality"].as_str().unwrap_or("");
                let this_vq = itag["quality"].as_str().unwrap_or("");

                let is_better_audio = (last_aq == aq1 && (this_aq == aq2 || this_aq == aq3))
                    || (last_aq == aq2 && this_aq == aq3);
                let is_same_or_better_audio = (last_aq == this_aq) || is_better_audio;

                let is_better_video = (last_vq == vq1
                    && (this_vq == vq2
                        || this_vq == vq3
                        || this_vq == vq4
                        || this_vq == vq5
                        || this_vq == vq6))
                    || (last_vq == vq2
                        && (this_vq == vq3 || this_vq == vq4 || this_vq == vq5 || this_vq == vq6))
                    || (last_vq == vq3 && (this_vq == vq4 || this_vq == vq5 || this_vq == vq6))
                    || (last_vq == vq4 && (this_vq == vq5 || this_vq == vq6))
                    || (last_vq == vq5 && this_vq == vq6);
                let is_same_or_better_video = (last_vq == this_vq) || is_better_video;

                let is_better_quality = is_better_audio && is_same_or_better_video
                    || is_better_video && is_same_or_better_audio;

                if itag["mimeType"].to_string().contains("video/")
                    && itag["quality"] != json!(null)
                    && itag["audioQuality"] != json!(null)
                    && (last_vq == "" || last_aq == "" || is_better_quality)
                {
                    VIDEO_MIME = itag["mimeType"].to_string();
                    url_to_choose = itag["url"].as_str().unwrap();
                    last_vq = String::from(this_vq);
                    last_aq = String::from(this_aq);
                }
            }

            if url_to_choose.is_empty() {
                Err(anyhow::Error::msg(
                    "Could not find a working itag - aborting.".to_string(),
                ))
            } else {
                Ok(url_to_choose.to_string())
            }
        }
    }

    fn does_video_exist<'a>(&'a self, url: &'a str) -> Result<bool> {
        let id_regex = Regex::new(r"(?:v=|.be/)(.*$)").unwrap();
        let id = id_regex.captures(url).unwrap().get(1).unwrap().as_str();
        unsafe {
            let video_info = get_video_info(id)?;
            let video_info_has_details = video_info["videoDetails"] != json!(null);
            Ok(video_info_has_details)
        }
    }

    fn display_name<'a>(&'a self) -> String {
        "YouTube".to_string()
    }

    fn find_video_file_extension<'a>(&'a self, _url: &'a str) -> Result<String> {
        // By this point, we have already filled VIDEO_MIME. Let's just use that.
        unsafe {
            let mut ext = "mp4";
            if VIDEO_MIME.contains("video/webm") {
                ext = "webm";
            }
            Ok(ext.to_string())
        }
    }
}

// Push the site definition to the list of known handlers:
inventory::submit! {
    &YouTubeHandler as &dyn SiteDefinition
}

Added src/main.rs.





























































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file LICENSE in this distribution for details.
 * A copy of the CDDL is also available via the Internet at
 * http://www.opensource.org/licenses/cddl1.txt
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the contents of the LICENSE file from this
 * distribution.
 */

// Yet Another Youtube Down Loader
// - main.rs file -

use anyhow::Result;
use clap::{App, Arg};
use indicatif::{ProgressBar, ProgressStyle};
use reqwest::{blocking::Client, header, Url};
use std::{
    fs,
    io::{self, copy, Read},
    path::{Path, PathBuf},
};

mod definitions;
mod ffmpeg;
mod handlers;

struct DownloadProgress<R> {
    inner: R,
    progress_bar: ProgressBar,
}

impl<R: Read> Read for DownloadProgress<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner.read(buf).map(|n| {
            self.progress_bar.inc(n as u64);
            n
        })
    }
}

fn download(url: &str, filename: &str) -> Result<()> {
    let url = Url::parse(url)?;
    let client = Client::new();

    // Find the video size:
    let total_size = {
        let resp = client.head(url.as_str()).send()?;
        if resp.status().is_success() {
            resp.headers()
                .get(header::CONTENT_LENGTH)
                .and_then(|ct_len| ct_len.to_str().ok())
                .and_then(|ct_len| ct_len.parse().ok())
                .unwrap_or(0)
        } else {
            return Err(anyhow::Error::msg(format!(
                "Couldn't download URL: {}. Error: {:?}",
                url,
                resp.status(),
            ))
            .into());
        }
    };

    let mut request = client.get(url.as_str());

    // Display a progress bar:
    let pb = ProgressBar::new(total_size);
    pb.set_style(ProgressStyle::default_bar()
                 .template("{spinner:.green} [{elapsed_precise}] [{bar:40.green/blue}] {bytes}/{total_bytes} ({eta})")
                 .progress_chars("#>-"));

    let file = Path::new(filename);

    if file.exists() {
        // Continue the file:
        let size = file.metadata()?.len() - 1;
        request = request.header(header::RANGE, format!("bytes={}-", size));
        pb.inc(size);
    }

    let mut source = DownloadProgress {
        progress_bar: pb,
        inner: request.send()?,
    };

    let mut dest = fs::OpenOptions::new()
        .create(true)
        .append(true)
        .open(&file)?;

    let _ = copy(&mut source, &mut dest)?;

    Ok(())
}

fn main() -> Result<()> {
    // Argument parsing:
    let args = App::new("yaydl")
        .version("0.1.0")
        .about("Yet Another Youtube Down Loader")
        .arg(Arg::new("onlyaudio")
             .about("Only keeps the audio stream")
             .short('x')
             .long("only-audio"))
        .arg(Arg::new("verbose")
             .about("Talks more while the URL is processed")
             .short('v')
             .long("verbose"))
        .arg(Arg::new("audioformat")
             .about("Sets the target audio format (only if --only-audio is used).\nSpecify the file extension here (defaults to \"mp3\").")
             .short('f')
             .long("audio-format")
             .takes_value(true)
             .value_name("AUDIO"))
        .arg(Arg::new("outputfile")
             .about("Sets the output file name")
             .short('o')
             .long("output")
             .takes_value(true)
             .value_name("OUTPUTFILE"))
        .arg(Arg::new("URL")
             .about("Sets the input URL to use")
             .required(true)
             .index(1))
        .get_matches();

    if let Some(in_url) = args.value_of("URL") {
        inventory::collect!(&'static dyn definitions::SiteDefinition);
        let mut site_def_found = false;

        for handler in inventory::iter::<&dyn definitions::SiteDefinition> {
            // "15:15 And he found a pair of eyes, scanning the directories for files."
            // https://kingjamesprogramming.tumblr.com/post/123368869357/1515-and-he-found-a-pair-of-eyes-scanning-the
            // ------------------------------------

            // Find a known handler for <in_url>:
            if !handler.can_handle_url(in_url) {
                continue;
            }

            // This one is it.
            site_def_found = true;
            println!("Fetching from {}.", handler.display_name());

            let video_exists = handler.does_video_exist(in_url)?;
            if !video_exists {
                println!("The video could not be found. Invalid link?");
            } else {
                if args.is_present("verbose") {
                    println!("The requested video was found. Processing...");
                }

                let vt = handler.find_video_title(in_url)?;

                // Usually, we already find errors here.
                if vt.is_empty() {
                    println!("The video title could not be extracted. Invalid link?");
                } else {
                    if args.is_present("verbose") {
                        println!("Title: {}", vt);
                    }

                    let ext = handler.find_video_file_extension(in_url)?;
                    let url = handler.find_video_direct_url(in_url)?;

                    // Now let's download it:
                    let mut targetfile = format!(
                        "{}.{}",
                        vt.replace(&['\"', ':', '\'', '\\', '/'][..], "-"),
                        ext
                    );

                    if let Some(in_targetfile) = args.value_of("outputfile") {
                        targetfile = in_targetfile.to_string();
                    }

                    if args.is_present("verbose") {
                        println!("Starting the download.");
                    }

                    download(&url, &targetfile)?;

                    if args.is_present("onlyaudio") {
                        if args.is_present("verbose") {
                            println!("Post-processing.");
                        }

                        // Convert the video to an audio file.
                        let mut outputext = "mp3";
                        if let Some(in_outputext) = args.value_of("audioformat") {
                            outputext = &in_outputext;
                        }

                        let inpath = Path::new(&targetfile);
                        let mut outpathbuf = PathBuf::from(&targetfile);
                        outpathbuf.set_extension(outputext);
                        let outpath = &outpathbuf.as_path();

                        ffmpeg::to_audio(inpath, outpath);

                        // Get rid of the evidence.
                        fs::remove_file(&targetfile)?;

                        // Success!
                        println!(
                            "\"{}\" successfully downloaded.",
                            outpathbuf
                                .into_os_string()
                                .into_string()
                                .unwrap_or(targetfile.to_string())
                        );
                    } else {
                        // ... just success!
                        println!("\"{}\" successfully downloaded.", &targetfile);
                    }
                }

                // Stop looking for other handlers:
                break;
            }
        }

        if !site_def_found {
            println!(
                "yaydl could not find a site definition that would satisfy {}. Exiting.",
                in_url
            );
        }
    }

    Ok(())
}