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: |
6f4c3acceee83e418449504bdc42a856 |
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
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(()) } |