├── .assets ├── logo.png ├── logo_discord.png └── ytfzf.gif ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── help.md └── workflows │ └── full-check.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── addons ├── extensions │ ├── auto-play │ ├── auto-thumb │ ├── bsm │ ├── buttons │ ├── comments │ ├── gui │ ├── ipc │ ├── notify-log │ ├── playlists │ ├── smart-thumb-download │ └── subscription-manager ├── interfaces │ ├── gui │ ├── kitty │ ├── sixel │ ├── sixel-menu │ │ ├── kittyatize-item │ │ ├── menu.py │ │ └── sixelize-item │ └── sxiv ├── scrapers │ ├── ani │ ├── ani-category │ ├── ani-gogohd-link │ ├── ard │ ├── ddg │ ├── feed-url │ ├── invidious-popular │ ├── osu │ ├── pictures │ ├── recommended │ ├── scrape_list │ ├── video-info │ ├── yt-music │ ├── yt-music-playlist │ └── yt-music-utils │ │ └── convert-ascii-escape.pl ├── sort-names │ ├── alpha │ └── alpha-rev ├── thumbnail-viewers │ ├── display-viewer │ ├── foot │ └── w3m └── url-handlers │ ├── atp │ ├── mpvq │ ├── rfp │ └── vlc-player ├── credits ├── bsgalvan.md ├── euro20179.md ├── gardockt.md ├── jac-zac.md ├── mathisto.md ├── mudskipper875.md ├── pystardust.md ├── qoheniac.md └── simonhughxyz.md ├── docs ├── conf.sh ├── man │ ├── ytfzf.1 │ └── ytfzf.5 └── subscriptions └── ytfzf /.assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pystardust/ytfzf/8637bc3929a34be1e21afcf89ddd9d84fc421645/.assets/logo.png -------------------------------------------------------------------------------- /.assets/logo_discord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pystardust/ytfzf/8637bc3929a34be1e21afcf89ddd9d84fc421645/.assets/logo_discord.png -------------------------------------------------------------------------------- /.assets/ytfzf.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pystardust/ytfzf/8637bc3929a34be1e21afcf89ddd9d84fc421645/.assets/ytfzf.gif -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]: " 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Describe the bug 11 | 12 | ### To Reproduce 13 | 14 | ### Expected behavior 15 | 16 | ### Screenshots 17 | 18 | ### Information 19 | - OS: 20 | - Terminal: 21 | - Ytfzf version: 22 | - Output of `readlink $(which sh)`: 23 | - (if is a thumbnail issue) run `ytfzf --thumbnail-log=log.txt` and post the file: 24 | 25 | 26 | ### Additional context 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature request]: " 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Background 11 | 12 | [ ] I have checked ytfzf(1) and ytfzf(5) or the wiki before creating this. 13 | 14 | ### Problem 15 | Why do we need this feature? 16 | 17 | ### Feature 18 | Description of your proposed feature. 19 | 20 | ### Alternative solutions to problem? 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Help 3 | about: For when you need help 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Before Making This Post 11 | * [ ] I have taken a look at both man pages, `ytfzf(1)` and `ytfzf(5)` 12 | 13 | ### What do you need help with? 14 | -------------------------------------------------------------------------------- /.github/workflows/full-check.yml: -------------------------------------------------------------------------------- 1 | name: full-check 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ubuntu: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2.4.0 10 | - name: install_dependencies 11 | run: | 12 | sudo apt install curl jq 13 | - name: build 14 | run: | 15 | sudo make install 16 | sudo make uninstall 17 | - name: build_addons 18 | run: | 19 | sudo make addons 20 | sudo make uninstall 21 | - name: build_docs 22 | run: | 23 | sudo make docs 24 | sudo make uninstall 25 | - name: run_program_version 26 | run: | 27 | make install 28 | ytfzf --version 29 | - name: run_program_help 30 | run: | 31 | ytfzf -h 32 | - name: run_program_odysee 33 | run: | 34 | ytfzf -rcO ytfzf 35 | - name: run_program_youtube 36 | run: | 37 | ytfzf -rcY ytfzf 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | * use the development branch 4 | * Keep the script POSIX compliant as defined [here](https://pubs.opengroup.org/onlinepubs/9699919799/) 5 | * Exceptions may be made if there is a backup POSIX function defined, for example, the `shuf` utility is used, and there is a backup `shuf` function defined if the user does not have `shuf` installed. 6 | * If something is not POSIX it may only be implemented if it is available on many systems (Linux, and BSD), and it's impractical to implement in POSIX shell. 7 | * For example: `date -d` is used. 8 | 9 | * Feel free to give yourself a file in the `credits` folder, and add yourself to the table in the README :) 10 | 11 | 12 | # Shells 13 | 14 | A non-comprehensive list of shells to test the script with 15 | 16 | * bash 17 | * dash 18 | * ksh 19 | * bsd-ksh (on the aur this is the package oksh) 20 | * busybox sh 21 | * any other posix shells 22 | * zsh* 23 | 24 | *Technically zsh does not have to work because it's not posix compliant, but zsh should work at least some what. 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROG=ytfzf 2 | PREFIX=/usr/local 3 | BINDIR=${PREFIX}/bin 4 | DOCDIR=${PREFIX}/share/doc/ytfzf 5 | MANDIR=${PREFIX}/share/man 6 | LICENSEDIR=${PREFIX}/share/licenses/ytfzf 7 | 8 | YTFZF_SYSTEM_ADDON_DIR=${PREFIX}/share/ytfzf/addons 9 | 10 | .DEFAULT_GOAL := default 11 | 12 | all: 13 | 14 | default: install doc 15 | 16 | doc: 17 | mkdir -p ${DESTDIR}${MANDIR}/man1 18 | mkdir -p ${DESTDIR}${MANDIR}/man5 19 | mkdir -p ${DESTDIR}${DOCDIR} 20 | mkdir -p ${DESTDIR}${LICENSEDIR} 21 | chmod 644 docs/man/ytfzf.1 docs/man/ytfzf.5 docs/conf.sh LICENSE 22 | cp docs/man/ytfzf.1 ${DESTDIR}${MANDIR}/man1 23 | cp docs/man/ytfzf.5 ${DESTDIR}${MANDIR}/man5 24 | cp docs/conf.sh ${DESTDIR}${DOCDIR} 25 | cp LICENSE ${DESTDIR}${LICENSEDIR} 26 | 27 | install: 28 | chmod 755 ${PROG} 29 | cp ${PROG} ${PROG}.bak 30 | sed 's_$${YTFZF\_SYSTEM\_ADDON\_DIR:=/usr/local/share/ytfzf/addons}_$${YTFZF\_SYSTEM\_ADDON\_DIR:=${YTFZF_SYSTEM_ADDON_DIR}}_' < ${PROG} > ${PROG}.bak 31 | mkdir -p ${DESTDIR}${BINDIR} 32 | cp ${PROG}.bak ${DESTDIR}${BINDIR}/${PROG} 33 | rm ${PROG}.bak 34 | 35 | addons: 36 | chmod 755 addons/*/* 37 | mkdir -p ${DESTDIR}${YTFZF_SYSTEM_ADDON_DIR} 38 | cp -r addons/interfaces ${DESTDIR}${YTFZF_SYSTEM_ADDON_DIR} 39 | cp -r addons/scrapers ${DESTDIR}${YTFZF_SYSTEM_ADDON_DIR} 40 | cp -r addons/sort-names ${DESTDIR}${YTFZF_SYSTEM_ADDON_DIR} 41 | cp -r addons/thumbnail-viewers ${DESTDIR}${YTFZF_SYSTEM_ADDON_DIR} 42 | cp -r addons/url-handlers ${DESTDIR}${YTFZF_SYSTEM_ADDON_DIR} 43 | cp -r addons/extensions ${DESTDIR}${YTFZF_SYSTEM_ADDON_DIR} 44 | 45 | uninstall: 46 | rm -f ${DESTDIR}${MANDIR}/man1/ytfzf.1 47 | rm -f ${DESTDIR}${MANDIR}/man5/ytfzf.5 48 | rm -rf ${DESTDIR}${DOCDIR} 49 | rm -rf ${DESTDIR}${LICENSEDIR} 50 | rm -f ${DESTDIR}${BINDIR}/${PROG} 51 | rm -rf ${DESTDIR}${YTFZF_SYSTEM_ADDON_DIR} 52 | 53 | #legacy install locations on linux 54 | uninstall-old: 55 | rm -f /usr/bin/ytfzf 56 | rm -f /usr/share/man/man1/ytfzf.1* 57 | rm -f /usr/share/man/man5/ytfzf.5* 58 | 59 | .PHONY: all default install uninstall doc addons uninstall-old 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NOTICE 2 | 3 | This project is no longer actively maintained, it should still work for the foreseeable future 4 | 5 | --- 6 | 7 |

8 | 9 |
10 |
11 | 12 | 13 | Maintainer: Euro20179 14 | 15 | 16 | Discord 17 | Discord 18 |
19 |
20 | A POSIX script that helps you find Youtube videos (without API) and opens/downloads them using mpv/youtube-dl 21 |


22 |

23 | 24 |

25 | This is a little showcase 26 |

27 |

28 | 29 |

30 | 31 | --- 32 | 33 | # Table Of Contents 34 | 35 | - [`Dependencies`](#Dependencies) 36 | - [`Install`](#Install) 37 | - [`Features`](#Features) 38 | - [`Examples`](#Examples) 39 | - [`Configuration`](#Configuration) 40 | - [`Bugs`](#Bugs) 41 | - [`Contributing`](#Contributing) 42 | - [`Credits`](#Credits) 43 | 44 | --- 45 | 46 | # Dependencies 47 | 48 | There are only 2 required dependencies, however the rest require some configuration before you can replace them. 49 | 50 | ## Required dependencies 51 | 52 | - [`jq`](https://github.com/stedolan/jq) 53 | - [`curl`](https://github.com/curl/curl) 54 | 55 | ## Recommended dependencies 56 | 57 | - [`mpv`](https://github.com/mpv-player/mpv) (the default video and audio player) 58 | - [`fzf`](https://github.com/junegunn/fzf) (the default menu selection screen) 59 | 60 | ## Optional dependencies 61 | 62 | - [`yt-dlp`](https://github.com/yt-dlp/yt-dlp) (for downloading) 63 | - [`dmenu`](https://tools.suckless.org/dmenu/) (only if using the -D option) 64 | - [`ueberzugpp`](https://github.com/jstkdng/ueberzugpp) 65 | - needed for the following thumbnail viewers: 66 | - `kitty`, `iterm2`, `sixel`, and `ueberzug` 67 | - the original [`ueberzug`](https://github.com/seebye/ueberzug) or any fork may be used if you only want to use the `ueberzug` viewer. 68 | 69 | ### Thumbnail Viewers 70 | 71 | - **To use a thumbnail viewer include `-T ` in the command when running ytfzf** 72 | 73 | | Program | Wayland Support | 74 | | :--------------------------------------------------------------------- | :--------------------- | 75 | | [`kitty`](https://github.com/kovidgoyal/kitty) (requires `ueberzugpp`) | ✅ | 76 | | `iterm2` (requires `ueberzugpp`) | ✅ | 77 | | `sixel` (requires `ueberzugpp`) | ✅ | 78 | | `sway` (requires `ueberzugpp`) | ✅ (only on sway) | 79 | | `wayland` (requires `ueberzugpp`) | ✅ | 80 | | [`chafa`](https://github.com/hpjansson/chafa) | ✅ | 81 | | [`catimg`](https://github.com/posva/catimg) | ✅ | 82 | | [`imv`](https://git.sr.ht/~exec64/imv) | ✅ | 83 | | [`mpv`](https://github.com/mpv-player/mpv) | ✅ | 84 | | [`swayimg`](https://github.com/artemsen/swayimg) | only on `sway` | 85 | | [`swayimg`](https://github.com/artemsen/swayimg) (-T swayimg-hyprland) | only on `hyprland` | 86 | 87 | # Install 88 | 89 | 90 | Repo status 91 | 92 | 93 | **if on `linux` and installed using make on version `2.0` or prior, run `sudo make uninstall-old` first** 94 | 95 | 1. Install the dependencies listed [above](#Dependencies) 96 | 2. Run the following commands 97 | 98 | ```sh 99 | git clone https://github.com/pystardust/ytfzf 100 | cd ytfzf 101 | sudo make install doc 102 | ``` 103 | 104 | - If you wish to not install documentation (highly unrecommended) run `sudo make install` instead. 105 | 106 | - If you wish to install addons, run `sudo make addons` 107 | 108 | - `YTFZF_SYSTEM_ADDONS_DIR` will point to `/usr/local/share/ytfzf/addons` even if you set `PREFIX` to something else 109 | - If you use a different prefix, it would be smart to export `YTFZF_SYSTEM_ADDONS_DIR` to `$PREFIX/share/ytfzf/addons` in a shell startup file. 110 | 111 | - You may also install `ytfzf` through your package manager, as listed on the side. 112 | 113 | ## Addons 114 | 115 | Addons are extra features that will not be as supported as everything built into `ytfzf` itself. 116 | 117 | Addons are located in `addons`, copy any addon to `~/.config/ytfzf/{addon-type}/{addon}`, and give it execute permissions. 118 | 119 | You may also just copy the entire addon folder type, eg: `cp -r addons/thumbnail-viewers ~/.config/ytfzf/` 120 | 121 | ### Usage 122 | 123 | To use a scraper addon run `ytfzf -c ...` 124 | 125 | To use a thumbnail-viewer addon run `ytfzf -T ...` 126 | 127 | To use a interface addon run `ytfzf -i ...` 128 | 129 | To use a url-handler addon run `ytfzf -u ...` 130 | 131 | To use a sort-name addon run `ytfzf --sort-name= ...` 132 | 133 | To use an extension addon run `ytfzf -e ...` 134 | 135 | --- 136 | 137 | # Features 138 | 139 | - Subscriptions 140 | - Thumbnails 141 | - Watch history 142 | - Search History 143 | - Downloading 144 | - Queueing multiple videos 145 | - Custom menus, and scrapers 146 | - Addon support 147 | 148 | --- 149 | 150 | # Examples 151 | 152 | > Search with thumbnails 153 | 154 | ```sh 155 | ytfzf -t 156 | ``` 157 | 158 | > Use `dmenu` as the menu instead of `fzf` 159 | 160 | ```sh 161 | ytfzf -D 162 | ``` 163 | 164 | > Print the link of the selected video instead of playing it 165 | 166 | ```sh 167 | ytfzf -L 168 | ``` 169 | 170 | > Search Odysee instead of youtube 171 | 172 | ```sh 173 | ytfzf -cO 174 | ``` 175 | 176 | > Use the chafa thumbnail viewer, pass --vo=sixel, and --quiet to mpv, scrape odysee with the search _odysee search_, youtube with the search: _youtube search_, and also scrape subscriptions 177 | 178 | ```sh 179 | ytfzf -t -T chafa --url-handler-opts='--vo=sixel --quiet' -cO,Y,SI --multi-search odysee search,youtube search 180 | ``` 181 | 182 | --- 183 | 184 | # Configuration 185 | 186 | Everything that is an option can also be configured in `~/.config/ytfzf/conf.sh`. 187 | 188 | In addition, the video player and other things may be changed here 189 | 190 | Here is a [sample configuration](docs/conf.sh) (please dont use it) 191 | 192 | For more information, see `ytfzf(5)` which should be installed, if it's not see [the wiki](https://github.com/pystardust/ytfzf/wiki). 193 | 194 | --- 195 | 196 | # Bugs 197 | 198 | - _dwm with swallow patch: Images don't render with ueberzug when looped (ie, option `-l`)_ 199 | - _if thumbnails are not working `.Xauthority` might be causing it. Try deleting it and relogging into your computer._ 200 | - When fzf is not set to 100% height, thumbnails may appear in the wrong position 201 | 202 | # Contributing 203 | 204 | Feel free to contribute, and add your name to the credits, please use the development branch. For more information see [contributing](CONTRIBUTING.md) 205 | 206 | # Credits 207 | 208 | | User | Contributions | Donate | 209 | | :------------ | :---------------------------------------- | :----- | 210 | | Pystardust | [contributions](credits/pystardust.md) | | 211 | | Euro20179 | [contributions](credits/euro20179.md) | | 212 | | Simonhughxyz | [contributions](credits/simonhughxyz.md) | | 213 | | Jac-Zac | [contributions](credits/jac-zac.md) | | 214 | | Mudskipper875 | [contributions](credits/mudskipper875.md) | | 215 | | Gardockt | [contributions](credits/gardockt.md) | | 216 | | qoheniac | [contributions](credits/qoheniac.md) | | 217 | | mathisto | [contributions](credits/mathisto.md) | | 218 | -------------------------------------------------------------------------------- /addons/extensions/auto-play: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | auto_play_handler () { 4 | _url="$1" 5 | stop_code=4 6 | $user_url_handler "$@" 7 | if [ $? -eq $stop_code ]; then return 0; fi 8 | case "$_url" in 9 | */*) id="${_url##*=}" ;; 10 | *) id="$_url" ;; 11 | esac 12 | #if we dont set the url handler back, it just uses multimedia_handler 13 | tab="$(printf '\t')" 14 | while :; do 15 | link=$(url_handler="$user_url_handler" ytfzf --ext=auto-play --auto-play-opts="$auto_play_opts" -cR $auto_play_opts -L --search-again=0 --loop=0 "$id" | tr '\n' '\t') 16 | id="${link%%$tab*}" 17 | id="${id##*=}" 18 | set -f 19 | $user_url_handler $link 20 | if [ $? -eq $stop_code ]; then break; fi 21 | done 22 | } 23 | 24 | on_opt_parse_auto_play_help () { 25 | print_info " 26 | Usage: ytfzf --ext=auto-play 27 | Options: 28 | --auto-play-opts: opts to use for auto play, default: -a 29 | " 30 | exit 0 31 | } 32 | 33 | on_opt_parse_auto_play_opts () { 34 | auto_play_opts="$1" 35 | return 1 36 | } 37 | 38 | on_post_set_vars_auto_play () { 39 | : "${auto_play_opts=-a}" 40 | user_url_handler=$url_handler 41 | url_handler=auto_play_handler 42 | } 43 | 44 | print_help_auto_play () { 45 | printf "%s\n" "After a video ends, auto play the next video recommended by youtube 46 | Options: 47 | --auto-play-opts: options to give to ytfzf when this extention uses it to find recommnded videos and auto play the next. default: -a 48 | hint: use this option with no value to disable auto play, and instead just show a list of recommended videos 49 | --auto-play-help: a worse version of this help menu" 50 | } 51 | -------------------------------------------------------------------------------- /addons/extensions/auto-thumb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | if [ "$XDG_SESSION_TYPE" = "X11" ]; then 5 | command_exists "ueberzug" && thumbnail_viewer=ueberzug 6 | elif command_exists "ueberzugpp"; then 7 | case "$TERM" in 8 | xterm-kitty) thumbnail_viewer=kitty ;; 9 | foot) thumbnail_viewer=sixel ;; 10 | wezterm|iterm2) thumbnail_viewer=iterm2 ;; 11 | esac 12 | else 13 | case "${XDG_SESSION_TYPE}" in 14 | wayland) 15 | if command_exists "swayimg"; then 16 | case "${XDG_CURRENT_DESKTOP}" in 17 | Hyprland) thumbnail_viewer=swayimg-hyprland ;; 18 | sway) thumbnail_viewer=swayimg ;; 19 | esac 20 | else 21 | thumbnail_viewer=chafa 22 | fi 23 | ;; 24 | X11|tty) 25 | thumbnail_viewer=chafa ;; 26 | *) 27 | print_warning "XDG_SESSION_TYPE is not set, defaulting to chafa\n" 28 | thumbnail_viewer=chafa ;; 29 | esac 30 | fi 31 | 32 | 33 | print_info "Thumbnail viewer: $thumbnail_viewer\n" 34 | -------------------------------------------------------------------------------- /addons/extensions/bsm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [ -z "$BASH_VERSION" ] && { 4 | die 3 "It appears you are not running bash\nMake sure /bin/sh points to the bash shell\n" 5 | } 6 | 7 | run_interface () { 8 | local _interface="interface_${interface:-text}" 9 | ${_interface//-/_} "$ytfzf_video_json_file" "$ytfzf_selected_urls" 10 | } 11 | 12 | add_commas () { 13 | read -r num 14 | printf "%'d" "$num" 15 | unset num 16 | } 17 | 18 | get_key_value () { 19 | local sep="${3:- }" 20 | local value="${1##*"${sep}""${2}"=}" 21 | printf -v KEY_VALUE "%s" "${value%%"${sep}"*}" 22 | printf "%s" "$KEY_VALUE" 23 | unset value 24 | [ "$KEY_VALUE" ] 25 | return "$?" 26 | } 27 | 28 | title_str () { 29 | printf "%s\n" "${1^}" 30 | } 31 | 32 | search_prompt_menu () { 33 | #shellcheck disable=SC2154 34 | parse_search_hist_file < "$search_hist_file" > "$search_hist_file.bash" 35 | HISTFILE="$search_hist_file.bash" 36 | printf "Search\n" > /dev/stderr 37 | history -r 38 | read -p "> " -er _search 39 | _search="$(history -p "$_search")" 40 | history -s "$_search" 41 | history -a 42 | printf "\033[1A\033[K\r%s\n" "> $_search" > /dev/stderr 43 | rm "$HISTFILE" 44 | } 45 | 46 | generic_wrapper () { 47 | local _base_name="$1" 48 | shift 49 | fn_name="${_base_name}${interface:+_${interface//-/_}}" 50 | if command_exists "$fn_name"; then 51 | print_debug "[INTERFACE]: Running menu function: $fn_name\n" 52 | $fn_name "$@" 53 | else 54 | print_debug "[INTERFACE]: Menu function $fn_name did not exist, falling back to ${_base_name}_ext\n" 55 | "${_base_name}_ext" "$@" 56 | fi 57 | } 58 | 59 | detach_cmd () { 60 | "$@" & disown 61 | } 62 | -------------------------------------------------------------------------------- /addons/extensions/buttons: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | #shellcheck disable=SC2154 4 | #shellcheck disable=SC2034 5 | 6 | BUTTONS_old_url_handler=$url_handler 7 | 8 | bool_to_word () { 9 | case "$1" in 10 | 1) echo "ON" ;; 11 | 0) echo "OFF" ;; 12 | esac 13 | } 14 | 15 | post_scrape_buttons () { 16 | [ "$enable_back_button" -eq 0 ] && return 0 17 | data="$(cat "$ytfzf_video_json_file")" 18 | [ "$is_audio_only" -eq 1 ] && toggle_audio_id=OFF || toggle_audio_id=ON 19 | [ "$url_handler" = "downloader" ] && toggle_download_id=OFF || toggle_download_id=ON 20 | [ "$is_detach" -eq "1" ] && toggle_detach_id=OFF || toggle_detach_id=ON 21 | [ "$show_thumbnails" -eq 1 ] && toggle_thumbnails_id=OFF || toggle_thumbnails_id=ON 22 | echo \ 23 | '[{"ID": "DOWNLOAD-'"$toggle_download_id"'", "date": "now", "title": "['"SET DOWNLOAD $toggle_download_id"']", "url": "download", "action": "set-download"},' \ 24 | '{"ID": "AUDIO-'"$toggle_audio_id"'", "date": "now", "title": "[SET AUDIO ONLY '"$toggle_audio_id"']", "url": "audio", "action": "set-audio"},' \ 25 | '{"ID": "DETACH-'"$toggle_detach_id"'", "date": "now", "title": "[SET DETACH '"$toggle_detach_id"']", "url": "detach", "action": "set-detach"},' \ 26 | '{"ID": "THUMBNAILS-'"$toggle_thumbnails_id"'", "date": "now", "title": "[SET THUMBNAILS '"$toggle_thumbnails_id"']", "url": "thumbnails", "action": "set-thumbnails"}]' \ 27 | "$data" > "$ytfzf_video_json_file" 28 | } 29 | 30 | handle_custom_action_set_download () { 31 | if ! [ "$url_handler" = "downloader" ]; then 32 | data="$(jq '[.[]|select(.ID=="DOWNLOAD-ON") = { 33 | "ID": "DOWNLOAD-OFF", 34 | "date": "now", 35 | "title": "[SET DOWNLOAD OFF]", 36 | "url": "download", 37 | "action": "set-download" 38 | }]' < "$ytfzf_video_json_file")" 39 | BUTTONS_old_url_handler=$url_handler 40 | url_handler=downloader 41 | else 42 | data="$(jq '[.[]|select(.ID=="DOWNLOAD-OFF") = { 43 | "ID": "DOWNLOAD-ON", 44 | "date": "now", 45 | "title": "[SET DOWNLOAD ON]", 46 | "url": "download", 47 | "action": "set-download" 48 | }]' < "$ytfzf_video_json_file")" 49 | url_handler=$BUTTONS_old_url_handler 50 | fi 51 | echo "$data" > "$ytfzf_video_json_file" 52 | return 1 #continue 53 | } 54 | 55 | handle_custom_action_set_audio () { 56 | old_state="$(bool_to_word $is_audio_only)" 57 | is_audio_only=$((is_audio_only^1)) 58 | new_state="$(bool_to_word $is_audio_only)" 59 | data="$(jq '[.[]|select(.ID=="AUDIO-'"$new_state"'") = { 60 | "ID": "AUDIO-'"$old_state"'", 61 | "date": "now", 62 | "title": "[SET AUDIO ONLY '"$old_state"']", 63 | "url": "audio", 64 | "action": "set-audio" 65 | }]' < "$ytfzf_video_json_file")" 66 | echo "$data" > "$ytfzf_video_json_file" 67 | return 1 #continue 68 | } 69 | 70 | handle_custom_action_set_detach () { 71 | old_state="$(bool_to_word $is_detach)" 72 | is_detach=$((is_detach^1)) 73 | new_state="$(bool_to_word $is_detach)" 74 | data="$(jq '[.[]|select(.ID=="DETACH-'"$new_state"'") = { 75 | "ID": "DETACH-'"$old_state"'", 76 | "date": "now", 77 | "title": "[SET DETACH '"$old_state"']", 78 | "url": "detach", 79 | "action": "set-detach" 80 | }]' < "$ytfzf_video_json_file")" 81 | echo "$data" > "$ytfzf_video_json_file" 82 | return 1 #continue 83 | } 84 | 85 | handle_custom_action_set_thumbnails () { 86 | old_state="$(bool_to_word $show_thumbnails)" 87 | show_thumbnails=$((show_thumbnails^1)) 88 | new_state="$(bool_to_word $show_thumbnails)" 89 | data="$(jq '[.[]|select(.ID=="THUMBNAILS-'"$new_state"'") = { 90 | "ID": "THUMBNAILS-'"$old_state"'", 91 | "date": "now", 92 | "title": "[SET THUMBNAILS '"$old_state"']", 93 | "url": "thumbnails", 94 | "action": "set-thumbnails" 95 | }]' < "$ytfzf_video_json_file")" 96 | echo "$data" > "$ytfzf_video_json_file" 97 | return 1 #continue 98 | } 99 | 100 | print_help_buttons () { 101 | printf "%s\n" "Adds buttons that toggle audio_only, detach, etc..." "use --disable-back or set enable_back_button to 0 to disable this extension" 102 | } 103 | -------------------------------------------------------------------------------- /addons/extensions/comments: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | get_search_from_EXT_comments (){ 4 | _search="${selected_url}" 5 | } 6 | 7 | set +f 8 | instances=$(printf "%s\n" "${YTFZF_TEMP_DIR}"/*) 9 | set -f 10 | 11 | instance_count=$(echo "$instances" | wc -l) 12 | 13 | if [ "$instance_count" -gt 1 ]; then 14 | printf "%s\n" "More than 1 ytfzf instance is running, please select one" 15 | echo "$instances" | sed 's/.*\///' | nl 16 | read -r num 17 | 18 | instance="$(echo "$instances" | sed -n "${num}p")" 19 | else 20 | instance="$instances" 21 | fi 22 | 23 | read -r selected_url < "${instance}/ids" 24 | 25 | [ -z "$selected_url" ] && { 26 | printf "%s\n" "This instance is not playing a video" 27 | } 28 | 29 | scrape="comments" 30 | search_source="EXT_comments" 31 | -------------------------------------------------------------------------------- /addons/extensions/gui: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | command_exists "gtkdialog" || die 3 "gtkdialog (https://github.com/puppylinux-woof-CE/gtkdialog) is required for the gui extension\n" 4 | 5 | tab_space="$(printf '\t')" 6 | 7 | load_interface gui || die 2 "The gui interface addon could not be found\n" 8 | 9 | print_help_gui () { 10 | printf "%s\n" "Use a gui instead of tui as a menu" 11 | } 12 | -------------------------------------------------------------------------------- /addons/extensions/ipc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | #shellcheck disable=SC2154 4 | #shellcheck disable=SC2034 5 | 6 | 7 | on_post_set_vars_ipc () { 8 | old_interface=$interface 9 | interface=ipc 10 | } 11 | 12 | search_prompt_menu_ipc () { 13 | interface=$old_interface search_prompt_menu_wrapper 14 | } 15 | 16 | quick_menu_ipc (){ 17 | interface=$old_interface quick_menu_wrapper 18 | } 19 | 20 | open_ipc () { 21 | ! [ -p "$ipc_file" ] && mkfifo "$ipc_file" 22 | } 23 | 24 | close_ipc () { 25 | kill "$(cat "$session_temp_dir/ipc__handle_ipc_id")" 26 | [ -p "$ipc_file" ] && rm -f "$ipc_file".* 27 | } 28 | 29 | on_opt_parse_ipci () { 30 | ipc_interface=$optarg 31 | } 32 | 33 | handle_ipc () { 34 | while :; do 35 | while read -r line; do 36 | if printf "%s" "$line" | grep -q '[!@#\$%\^&\*(\-\[\];:"'"'"',\./]'; then 37 | echo "INVALID COMMAND: \"$line\" CONTAINS WEIRD CHARS" > "${ipc_file}.out" 38 | break 39 | fi 40 | case "$line" in 41 | (get*) 42 | eval "echo \$${line#get }" > "${ipc_file}.${line#get }" ;; 43 | *) 44 | echo "INVALID COMMAND: \"$line\"" > "${ipc_file}.out" ;; 45 | esac 46 | done < "$ipc_file" 47 | done 48 | } 49 | 50 | interface_ipc () { 51 | [ "$old_interface" = "ipc" ] && die 1 "You are doing something weird\n" 52 | export ipc_file="$session_cache_dir/ipc" 53 | open_ipc 54 | handle_ipc & 55 | echo "$!" > "${session_temp_dir}/ipc__handle_ipc_id" 56 | $(printf "%s" "interface_$old_interface" | sed 's/-/_/g' | sed 's/^interface_$/interface_text/') "$@" 57 | close_ipc 58 | } 59 | 60 | print_help_ipc() { 61 | printf "%s\n" "Opens an ipc file to write to be able to read variables 62 | eg: 63 | echo 'get is_audio_only' > \$session_cache_dir/ipc 64 | cat \$session_cache_dir/ipc.is_audio_only" 65 | } 66 | -------------------------------------------------------------------------------- /addons/extensions/notify-log: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | print_info () { 4 | [ $log_level -ge 2 ] && notify-send -c ytfzf-info "$1" 5 | } 6 | print_warning () { 7 | [ $log_level -ge 1 ] && notify-send -c ytfzf-warning "$1" 8 | } 9 | print_error () { 10 | [ $log_level -ge 0 ] && notify-send -c ytfzf-error "$1" 11 | } 12 | 13 | print_help_notify_log () { 14 | printf "%s\n" "Use notify-send instead of printf for logging" 15 | } 16 | -------------------------------------------------------------------------------- /addons/extensions/playlists: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | : "${PLAYLISTS_YTFZF_DIR:="${YTFZF_CONFIG_DIR:-"$HOME/.config/ytfzf"}"/playlists}" 4 | 5 | print_help_playlists () { 6 | printf "%s" "This extension is intended to always be loaded in your config file, ie: add this line: 7 | 8 | load_extension playlists 9 | 10 | Usage (if loaded): 11 | --playlist= Plays the playlist named . 12 | --list-playlists Lists all playlists and exists. 13 | --list-titles= Lists the titles of all items in . 14 | --create-playlist= Create a playlist called in \$PLAYLISTS_YTFZF_DIR 15 | --del-playlist= Remove the playlist from \$PLAYLISTS_YTFZF_DIR 16 | --atp= Adds the selected result to the playlist 17 | --rfp= Opens playlist , and any selected items will be removed." 18 | } 19 | 20 | 21 | on_opt_parse_playlist () { 22 | scrape="playlist" 23 | ! [ -f "${PLAYLISTS_YTFZF_DIR}/$1" ] && die 1 "playlist: \"$1\" does not exist\n" 24 | initial_search="${PLAYLISTS_YTFZF_DIR}/$1" 25 | return 1 26 | } 27 | 28 | on_opt_parse_create_playlist () { 29 | _playlist_name=$1 30 | _playlist_full_path="${PLAYLISTS_YTFZF_DIR}/${_playlist_name}" 31 | [ -f "$_playlist_full_path" ] && die 1 "playlist: \"$_playlist_name\" already exists\n" 32 | touch "$_playlist_full_path" 33 | print_info "\"$_playlist_name\" created\n" 34 | exit 0 35 | } 36 | 37 | on_opt_parse_del_playlist () { 38 | _playlist_name="$1" 39 | _playlist_full_path="${PLAYLISTS_YTFZF_DIR}/${_playlist_name}" 40 | ! [ -f "$_playlist_full_path" ] && die 1 "playlist: \"$_playlist_name\" does not exist\n" 41 | printf "%s [Y/n]: " "Are you sure you want to remove the playlist: \"$_playlist_name\"" >&2 42 | read -r confirm 43 | case "$confirm" in 44 | [Nn]|[Nn][Oo]) die 1 "Playlist not removed\n" 45 | esac 46 | rm "$_playlist_full_path" 47 | print_info "\"$_playlist_name\" removed\n" 48 | exit 0 49 | } 50 | 51 | on_opt_parse_list_playlists () { 52 | ls "${PLAYLISTS_YTFZF_DIR}" 53 | exit 0 54 | } 55 | 56 | on_opt_parse_atp () { 57 | _playlists_playlist_name=$1 58 | load_url_handler atp 59 | return 1 60 | } 61 | 62 | on_opt_parse_rfp () { 63 | scrape="playlist" 64 | ! [ -f "${PLAYLISTS_YTFZF_DIR}/$1" ] && die 1 "playlist: \"$1\" does not exist\n" 65 | initial_search="${PLAYLISTS_YTFZF_DIR}/$1" 66 | _playlists_playlist_name="$1" 67 | _playlist_full_path="${PLAYLISTS_YTFZF_DIR}/${1}" 68 | load_url_handler rfp 69 | return 1 70 | } 71 | 72 | on_opt_parse_list_titles () { 73 | _playlist_name="$1" 74 | _playlist_full_path="${PLAYLISTS_YTFZF_DIR}/${_playlist_name}" 75 | jq -s -r '.[]|.[]|.title' < "$_playlist_full_path" 76 | exit 0 77 | } 78 | 79 | ext_on_search_playlists () { 80 | [ -z "$_playlists_playlist_name" ] && return 0 81 | export _PLAYLIST_NAME=${PLAYLISTS_YTFZF_DIR}/${_playlists_playlist_name} 82 | export YTFZF_VIDEO_JSON_FILE="${ytfzf_video_json_file}" 83 | } 84 | -------------------------------------------------------------------------------- /addons/extensions/smart-thumb-download: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | download_thumbnails () { 4 | [ "$skip_thumb_download" -eq 1 ] && { print_info "Skipping thumbnail download\n"; return 0; } 5 | [ "$async_thumbnails" -eq 0 ] && print_info 'Fetching thumbnails...\n' 6 | curl_config_file="${session_temp_dir}/curl_config" 7 | [ -z "$*" ] && return 0 8 | : > "$curl_config_file" 9 | for line in "$@"; do 10 | _url="${line%%;*}" _id="${line##*;}" 11 | [ -f "${YTFZF_CUSTOM_THUMBNAILS_DIR}/${_id}.jpg" ] && continue 12 | printf "url=\"%s\"\noutput=\"$YTFZF_CUSTOM_THUMBNAILS_DIR/%s.jpg\"\n" "${_url}" "${_id}" 13 | done >> "$curl_config_file" 14 | ! [ -s "$curl_config_file" ] && return 0 15 | [ "$async_thumbnails" -eq 1 ] && use_silent="s" 16 | curl -${use_silent}fLZ -K "$curl_config_file" 17 | [ $? -eq 2 ] && curl -${use_silent}fL -K "$curl_config_file" 18 | } 19 | -------------------------------------------------------------------------------- /addons/extensions/subscription-manager: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | print_help_subscription_manager () { 4 | cat <> " "Type a comment for this channel" 14 | read -r comment 15 | printf "%s #%s\n" "$real_link" "$comment" >> "${YTFZF_SUBSCRIPTIONS_FILE}" 16 | exit 0 17 | } 18 | 19 | on_opt_parse_remove_sub () { 20 | lines_to_remove=$(quick_menu_wrapper "Remove a channel " < "$YTFZF_SUBSCRIPTIONS_FILE") 21 | _tmp_subfile="${YTFZF_SUBSCRIPTIONS_FILE}.tmp" 22 | while read -r subline; do 23 | case "$lines_to_remove" in 24 | *"$subline"*) : ;; 25 | *) printf "%s\n" "$subline" >> "$_tmp_subfile" ;; 26 | esac 27 | done < "$YTFZF_SUBSCRIPTIONS_FILE" 28 | mv "$_tmp_subfile" "$YTFZF_SUBSCRIPTIONS_FILE" 29 | exit 0 30 | } 31 | -------------------------------------------------------------------------------- /addons/interfaces/gui: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | : "${YTFZF_GUI_CSS:=$YTFZF_CONFIG_DIR/interface-gui.css}" 4 | 5 | display_text_gui () { 6 | MAIN_DIALOG=" 7 | 8 | 9 | 500 10 | 500 11 | $* 12 | 13 | " gtkdialog 14 | } 15 | 16 | info_wait_prompt_gui () { 17 | : 18 | } 19 | 20 | gui_dialog () { 21 | css_file="${session_temp_dir}/gtk.css" 22 | if [ -f "$YTFZF_GUI_CSS" ]; then 23 | css_file="$YTFZF_GUI_CSS" 24 | else 25 | : > "$css_file" 26 | fi 27 | export MAIN_DIALOG="" 28 | urls="$(jq -r '.url')" 29 | while read -r url; do 30 | _correct_json="$(jq -r --arg url "$url" '[.[]|select(.url==$url)]|unique_by(.ID)[0]' < "$video_json_file")" 31 | id="$(_get_video_json_attr "ID")" 32 | title="$(_get_video_json_attr "title")" 33 | channel="$(_get_video_json_attr "channel")" 34 | views="$(_get_video_json_attr "views" | add_commas)" 35 | date="$(_get_video_json_attr "date")" 36 | scraper="$(_get_video_json_attr "scraper")" 37 | duration="$(_get_video_json_attr "duration")" 38 | description="$(_get_video_json_attr "description" | sed 's/\\n/\n/g')" 39 | 40 | 41 | unset IFS 42 | 43 | for path in "${YTFZF_CUSTOM_THUMBNAILS_DIR}/$id.jpg" "${thumb_dir}/${id}.jpg" "${YTFZF_CUSTOM_THUMBNAILS_DIR}/YTFZF:DEFAULT.jpg"; do 44 | thumb_path="$path" 45 | [ -f "${thumb_path}" ] && break 46 | done 47 | 48 | export MAIN_DIALOG="$MAIN_DIALOG 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | 400 69 | $thumb_path 70 | 71 | " 72 | done <<-EOF 73 | $urls 74 | EOF 75 | 76 | export MAIN_DIALOG="$MAIN_DIALOG" 77 | if gtkdialog -h 2> /dev/null | grep -q -- '--styles'; then 78 | MAIN_DIALOG=$MAIN_DIALOG gtkdialog --styles "$css_file" 79 | else 80 | 81 | MAIN_DIALOG=$MAIN_DIALOG gtkdialog 82 | fi 83 | } 84 | 85 | interface_gui () { 86 | video_json_file="$1" 87 | selected_id_file="$2" 88 | command_exists "gtkdialog" || die 3 "gtkdialog (https://github.com/puppylinux-woof-CE/gtkdialog) is required for the gui interface\n" 89 | create_sorted_video_data | gui_dialog | sed -n -e 's/EXIT="\(.*\)"/\1/p' -e 's/[[:space:]]*//g' > "$selected_id_file" 90 | while read -r line; do 91 | [ "$line" = "abort" ] && exit 0 92 | done < "$selected_id_file" 93 | } 94 | 95 | print_help_gui (){ 96 | print_info "YTFZF_GUI_CSS is a file for custom css for the window\nselectors:\n#TitleText\n#ViewsText\n#DateText\n#DurationText\n#UrlButton\n#Thumbnail\n" 97 | exit 0 98 | } 99 | 100 | search_prompt_menu_gui () { 101 | vars=$(MAIN_DIALOG=" 102 | 103 | 104 | 105 | Search... 106 | SEARCH 107 | 108 | 111 | 112 | " gtkdialog) 113 | 114 | while read -r line; do 115 | case "$line" in 116 | *SEARCH*) 117 | _search=$(printf "%s" "$line" | tr -d '"') 118 | _search="${_search#*=}" ;; 119 | EXIT*) break ;; 120 | esac 121 | done <<-EOF 122 | $vars 123 | EOF 124 | } 125 | -------------------------------------------------------------------------------- /addons/interfaces/kitty: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | interface_kitty () { 4 | for path in "${YTFZF_CUSTOM_INTERFACES_DIR}/sixel-menu" "${YTFZF_SYSTEM_ADDON_DIR}/interfaces/sixel-menu"; do 5 | const_SIXEL_UTIL_DIR="$YTFZF_CUSTOM_INTERFACES_DIR/sixel-menu" 6 | [ -d "$const_SIXEL_UTIL_DIR" ] && break 7 | false 8 | done || die 3 'The sixel-menu folder is required for this interface\n' 9 | 10 | video_json_file=$1 11 | selected_id_file=$2 12 | 13 | export selected_id_file="$selected_id_file" 14 | export video_json_file="$video_json_file" 15 | export YTFZF_CUSTOM_THUMBNAILS_DIR="${YTFZF_CUSTOM_THUMBNAILS_DIR}" 16 | export session_cache_dir="$session_cache_dir" 17 | export session_temp_dir="$session_temp_dir" 18 | 19 | _tmp_selection="${session_temp_dir}/menu-selection" 20 | 21 | jq -c -r 'select(.!=[])|.[]' < "$video_json_file" | 22 | sort_video_data_fn | jq -r '[.title, .ID, .url]|join("\t|")' | column -t -s "$tab_space" > "$session_temp_dir/sixel-lines" 23 | "$const_SIXEL_UTIL_DIR"/menu.py "${session_temp_dir}/sixel-lines" "${const_SIXEL_UTIL_DIR}/kittyatize-item" "${session_cache_dir}" "{}" | trim_url > "$selected_id_file" 24 | 25 | } 26 | -------------------------------------------------------------------------------- /addons/interfaces/sixel: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | interface_sixel () { 4 | for path in "${YTFZF_CUSTOM_INTERFACES_DIR}/sixel-menu" "${YTFZF_SYSTEM_ADDON_DIR}/interfaces/sixel-menu"; do 5 | const_SIXEL_UTIL_DIR="$YTFZF_CUSTOM_INTERFACES_DIR/sixel-menu" 6 | [ -d "$const_SIXEL_UTIL_DIR" ] && break 7 | false 8 | done || die 3 'The sixel-menu folder is required for this interface\n' 9 | 10 | video_json_file=$1 11 | selected_id_file=$2 12 | 13 | export selected_id_file="$selected_id_file" 14 | export video_json_file="$video_json_file" 15 | export YTFZF_CUSTOM_THUMBNAILS_DIR="${YTFZF_CUSTOM_THUMBNAILS_DIR}" 16 | export session_cache_dir="$session_cache_dir" 17 | export session_temp_dir="$session_temp_dir" 18 | 19 | _tmp_selection="${session_temp_dir}/menu-selection" 20 | 21 | jq -c -r 'select(.!=[])|.[]' < "$video_json_file" | 22 | sort_video_data_fn | jq -r '[.title, .ID, .url]|join("\t|")' | column -t -s "$tab_space" > "$session_temp_dir/sixel-lines" 23 | "$const_SIXEL_UTIL_DIR"/menu.py "${session_temp_dir}/sixel-lines" "${const_SIXEL_UTIL_DIR}/sixelize-item" "${session_cache_dir}" "{}" | trim_url > "$selected_id_file" 24 | 25 | } 26 | -------------------------------------------------------------------------------- /addons/interfaces/sixel-menu/kittyatize-item: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | session_cache_dir="$1" 4 | item="$@" 5 | 6 | : "${SIXELIZE_ITEM_CONFIG:="${XDG_CONFIG_HOME:-$HOME/.config}"/ytfzf/sixel-menu-conf.sh}" 7 | [ -f "${SIXELIZE_ITEM_CONFIG}" ] && . "${SIXELIZE_ITEM_CONFIG}" 8 | 9 | thumb_dir="${session_cache_dir}/thumbnails" 10 | 11 | id="$(printf "%s" "$item" | awk '{print $(NF-1)}')" 12 | id="${id#"|"}" 13 | 14 | for path in "${YTFZF_CUSTOM_THUMBNAILS_DIR}/$id.jpg" "${thumb_dir}/${id}.jpg" "${YTFZF_CUSTOM_THUMBNAILS_DIR}/YTFZF:DEFAULT.jpg"; do 15 | thumb_path="$path" 16 | [ -f "${path}" ] && break 17 | done 18 | /usr/bin/kitty icat --clear 19 | /usr/bin/kitty icat --place "30x30@50x0" "$thumb_path" 20 | #img2sixel -w 300 "$thumb_path" | tee "${session_sixel_cache}/${id}.sixel" 21 | -------------------------------------------------------------------------------- /addons/interfaces/sixel-menu/menu.py: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | 3 | import os 4 | import sys 5 | import tty 6 | import termios 7 | import shutil 8 | import json 9 | import sys 10 | import subprocess 11 | 12 | from enum import Enum, auto 13 | 14 | KEYS = { 15 | b'\x1b[A': "ARROW_UP", 16 | b'\x1b[B': "ARROW_DOWN", 17 | b'\x1b[D': "ARROW_LEFT", 18 | b'\x1b[C': "ARROW_RIGHT" 19 | } 20 | 21 | 22 | class Key: 23 | @staticmethod 24 | def toString(b: bytes): 25 | keyrepr = KEYS.get(b) 26 | if not keyrepr: 27 | return str(b, "utf-8") 28 | return keyrepr 29 | 30 | def __init__(self, name: str, bytes: bytes): 31 | self.type = type 32 | self.name = name 33 | self.bytes = bytes 34 | 35 | def __eq__(self, name: str): 36 | return self.name == name 37 | 38 | def __repr__(self): 39 | return f'{self.name}:{self.bytes}' 40 | 41 | 42 | fd = sys.stdin.fileno() 43 | 44 | old_settings = termios.tcgetattr(fd) 45 | 46 | tty.setraw(sys.stdin.fileno()) 47 | 48 | 49 | class KeyType(Enum): 50 | ESCAPE = auto() 51 | ARROW = auto() 52 | FKEY = auto() 53 | # home/end/insert/page(up/down) 54 | ASCII = auto() 55 | REGULAR = auto() 56 | 57 | 58 | def getInput(): 59 | seq = bytes() 60 | escapeSequence = False 61 | while len(seq) == 0 or escapeSequence: 62 | cur_char = bytes(sys.stdin.read(1), "utf-8") 63 | seq += cur_char 64 | if seq[-1] == 27: 65 | escapeSequence = True 66 | elif (escapeSequence and seq[-1] != 91) and \ 67 | (seq[-1] > 57 or seq[-1] < 48) and \ 68 | seq[-1] != 79 and seq[-1] != 59: 69 | escapeSequence = False 70 | return Key(Key.toString(seq), seq) 71 | 72 | 73 | class Lines(list): 74 | renderCmd = "" 75 | 76 | def __init__(self, *data): 77 | super().__init__(data) 78 | self.selectedLine = 0 79 | self.outputCache = {} 80 | 81 | def renderPreview(self, line): 82 | sys.stderr.write("\033[s") 83 | sys.stderr.write(f'\033[0;{TERMCOLUMNS // 2 + 1}H') 84 | if self.outputCache.get(self.selectedLine): 85 | sys.stderr.write(self.outputCache[self.selectedLine]) 86 | else: 87 | cmd = list(map(lambda x: x.replace( 88 | "{}", line), self.renderCmd)) 89 | o = subprocess.check_output(cmd).decode("utf-8") 90 | self.outputCache[self.selectedLine] = o 91 | sys.stderr.write(o + "\n") 92 | sys.stderr.write("\033[u") 93 | 94 | def render(self): 95 | start = max(0, self.selectedLine - TERMLINES // 2) 96 | end = min(len(self) + 1, self.selectedLine + TERMLINES // 2) 97 | i = start 98 | for line in self[start:end]: 99 | displayLine = line 100 | if len(line) > TERMCOLUMNS // 2: 101 | displayLine = line[0:TERMCOLUMNS // 2] 102 | if i == self.selectedLine: 103 | if self.renderCmd: 104 | self.renderPreview(line) 105 | sys.stderr.write('\033[31m') 106 | sys.stderr.write(displayLine + "\033[0m\n\r") 107 | i += 1 108 | 109 | def appendJSONString(self, string): 110 | super().append(json.loads(string)) 111 | 112 | def down(self): 113 | if self.selectedLine < len(self) - 1: 114 | self.selectedLine += 1 115 | else: 116 | self.selectedLine = 0 117 | 118 | def up(self): 119 | if self.selectedLine > 0: 120 | self.selectedLine -= 1 121 | else: 122 | self.selectedLine = len(self) - 1 123 | 124 | 125 | LINES = Lines() 126 | 127 | if not sys.argv[1]: 128 | sys.stderr.write("No file to read\n") 129 | exit(1) 130 | 131 | with open(sys.argv[1], "r") as f: 132 | for line in f.read().split("\n"): 133 | if not line: 134 | continue 135 | try: 136 | LINES.append(line.strip()) 137 | except Exception as err: 138 | print(err) 139 | 140 | TERMCOLUMNS, TERMLINES = shutil.get_terminal_size([80, 24]) 141 | shouldPrint = True 142 | 143 | if len(sys.argv) > 2: 144 | LINES.renderCmd = sys.argv[2:] 145 | else: 146 | LINES.renderCmd = ["img2sixel", "-w", "100", "{}"] 147 | 148 | 149 | #TOOD: add :command, such as :copy to copy the current line 150 | while True: 151 | for f in sys.stderr, sys.stdout: 152 | f.write("\033[2J\033[0;0H") 153 | 154 | LINES.render() 155 | 156 | sys.stderr.write(f'\033[0;{TERMCOLUMNS // 2}H') 157 | k = getInput() 158 | 159 | if k == "q": 160 | shouldPrint = False 161 | break 162 | elif k == "ARROW_DOWN" or k == "j": 163 | LINES.down() 164 | elif k == "ARROW_UP" or k == "k": 165 | LINES.up() 166 | elif k == '\r': 167 | break 168 | 169 | 170 | termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) 171 | 172 | os.system("clear") 173 | 174 | if shouldPrint: 175 | for fd in sys.stderr, sys.stdout: 176 | fd.write("\033[2J\033[0;0H") 177 | print(LINES[LINES.selectedLine]) 178 | -------------------------------------------------------------------------------- /addons/interfaces/sixel-menu/sixelize-item: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | session_cache_dir="$1" 4 | item="$@" 5 | 6 | : "${SIXELIZE_ITEM_CONFIG:="${XDG_CONFIG_HOME:-$HOME/.config}"/ytfzf/sixel-menu-conf.sh}" 7 | [ -f "${SIXELIZE_ITEM_CONFIG}" ] && . "${SIXELIZE_ITEM_CONFIG}" 8 | 9 | thumb_dir="${session_cache_dir}/thumbnails" 10 | session_sixel_cache="${session_cache_dir}/sixel" 11 | ! [ -d "$session_sixel_cache" ] && mkdir -p "$session_sixel_cache" 12 | 13 | id="$(printf "%s" "$item" | awk '{print $(NF-1)}')" 14 | id="${id#"|"}" 15 | 16 | if [ -f "$session_sixel_cache/${id}.sixel" ]; then 17 | cat "${session_sixel_cache}/${id}.sixel" 18 | 19 | else 20 | for path in "${YTFZF_CUSTOM_THUMBNAILS_DIR}/$id.jpg" "${thumb_dir}/${id}.jpg" "${YTFZF_CUSTOM_THUMBNAILS_DIR}/YTFZF:DEFAULT.jpg"; do 21 | thumb_path="$path" 22 | [ -f "${path}" ] && break 23 | done 24 | img2sixel -w 300 "$thumb_path" | tee "${session_sixel_cache}/${id}.sixel" 25 | fi 26 | -------------------------------------------------------------------------------- /addons/interfaces/sxiv: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | interface_sxiv () { 5 | command_exists "sxiv" || die 3 "sxiv is required for this menu\n" 6 | video_json_file=$1 7 | selected_id_file=$2 8 | curl_config_file="${session_temp_dir}/curl_config" 9 | jq -r '.[] | select(.thumbs!=null)|" 10 | url = \"\(.thumbs)\" 11 | output = \"'"$thumb_dir"'/\(.title)-\(.ID).jpg\""' < "$video_json_file" > "$curl_config_file" 12 | curl -Z -K "$curl_config_file" 13 | ids="$(sxiv -to "$thumb_dir")" 14 | while read -r id; do 15 | id="${id##*-}" 16 | id="${id%.*}" 17 | jq -r '.[]|select(.ID=="'"${id}"'").url' < "$video_json_file" >> "$selected_id_file" 18 | done <<-EOF 19 | $ids 20 | EOF 21 | } 22 | -------------------------------------------------------------------------------- /addons/scrapers/ani: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | _ani_get_categories () { 4 | #stolen from pystardust/ani-cli 5 | sed -n 's_^[[:space:]]*_\1_p' "$2" 11 | } 12 | 13 | scrape_ani () { 14 | search="$1" 15 | print_info "If you like this, you should check out https://github.com/pystardust/ani-cli, as it is dedicated for anime!\nPlus that team does all the hard work\n" 16 | [ "$search" = ":help" ] && print_info "Search gogoanime for an anime\nYou can use the --pages-start, and --pages options to control which episodes to scrape" && return 100 17 | output_json_file="$2" 18 | search="$(printf "%s" "$search" | tr '[[:space:]]' '-')" 19 | #their url could move a lot 20 | #stolen from pystardust/ani-cli 21 | base_url=$(curl -s -L -o /dev/null -w "%{url_effective}\n" https://gogoanime.cm) 22 | _tmp_html="${session_temp_dir}/ani.html" 23 | _get_request "$base_url/search.html" -G -d "keyword=$search" > "$_tmp_html" 24 | _tmp_categories_file=${session_temp_dir}/ani-categories.list 25 | _tmp_thumbnails_file="${session_temp_dir}/ani-thumbnails.list" 26 | _tmp_json_var="[]" 27 | _ani_get_categories "$_tmp_html" | uniq | tee "$_tmp_categories_file" | { 28 | while read -r category; do 29 | thumbnail="$(_ani_get_thumbnails "$category" "$_tmp_html")" 30 | _tmp_json_var=$(printf "%s" "$_tmp_json_var" | jq --arg thumbnail "$thumbnail" --arg title "$category" '. + [{"title": $title, "ID": $title, "url": "'"$base_url/category/"'\($title)", "thumbs": $thumbnail, "action": "scrape type=ani-category search=\($title)"}]') 31 | done 32 | echo "$_tmp_json_var" >> "$output_json_file" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /addons/scrapers/ani-category: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | 4 | _supports_echo_e () { 5 | _test_against="$EOT" 6 | #if this is true, echo printed the correct char 7 | [ "$(echo -e "\x3")" = "$_test_against" ] 8 | } 9 | 10 | scrape_ani_category () { 11 | _supports_echo_e || die 1 "Your shell does not support echo -e\n" 12 | search="$1" 13 | output_json_file="$2" 14 | base_url='https://animixplay.to' 15 | #This pipeline is mostly a copy paste from https://github.com/pystardust/ani-cli 16 | #this pipeline does the following 17 | #1: send request to the url with the search 18 | #2: does some webscraping to get a list of links to gogohd.net 19 | #3: lastly uses sed to prepend https: to the front of each link 20 | #4: writes the whole thing to ${session_cache_dir}/ani-category-episodes.list 21 | _get_request "$base_url/v1/${search}" | sed -n "s_.*epslistplace.*>\(.*\)_\1_p" | tr ',' '\n' | sed -e '/extra/d' -e '/PV/d' | sed -n 's_".*":"\(.*\)".*_\1_p' | sed 's/\(.*\)/https:\1/' > "${session_cache_dir}/ani-category-episodes.list" 22 | json="[" 23 | while read -r url; do 24 | id="$(printf "%s" "$url" | sed -n 's/.*title=\([^&]\+\).*/\1/p')" 25 | #this replaces all percents with \\x so that printf can convert them to real values 26 | title="$(echo -e "$(printf "%s" "$id" | sed 'y/+/-/; s/%/\\x/g')")" 27 | json=''"${json}"'{"ID": "'"$title"'", "title": "'"$title"'", "url": "'"$url"'", "scraper": "ani-category", "action": "scrape type=ani-gogohd-link search='"$url"'"},' 28 | done < "${session_cache_dir}/ani-category-episodes.list" 29 | json="${json%,}]" 30 | printf "%s\n" "$json" >> "$output_json_file" 31 | } 32 | -------------------------------------------------------------------------------- /addons/scrapers/ani-gogohd-link: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | 4 | 5 | _provider_number_to_name () { 6 | case "$1" in 7 | 1) name="Animixplay" ;; 8 | 2) name="Xstreamcdn" ;; 9 | *) name="Gogoanime" ;; 10 | esac 11 | echo "$name" 12 | } 13 | 14 | _ani_gogohd_generate_link () { 15 | id="$1" 16 | provider="$2" 17 | full_gogo_resp="$3" 18 | case "$provider" in 19 | 1) 20 | refr="https://animixplay.to" 21 | [ -z "$id" ] && return 0 22 | command_exists "base64" || { pring_warning "base64 is not installed\n" && return 0; } 23 | enc_id="$(printf "%s" "$id" | base64)" 24 | ani_id="$(printf "%sLTXs3GrU8we9O%s", "$id" "$enc_id" | base64)" 25 | result_links="$(_get_request "$refr/api/cW9${ani_id}" -I | sed -nE 's_[L|l]ocation: https?://[^#]*#([^#]*).*_\1_p' | base64 -d)" 26 | _generate_json () { 27 | echo '[{"ID": "'"${title}-ani-gogohd"'", "title": "'"${title}"'", "scraper": "ani-gogohd-link", "url": "'"$result_links"'"}]' 28 | } ;; 29 | 2) 30 | fb_id=$(printf "%s" "$full_gogo_resp" | sed -n "s_.*fembed.*/v/__p") 31 | refr="https://fembed-hd.com/v/$fb_id" 32 | [ -z "$fb_id" ] && return 0 33 | result_links="$(curl -a "$useragent" -s -x post "https://fembed-hd.com/api/source/$fb_id" -h "x-requested-with:xmlhttprequest" | 34 | sed -e 's/\\//g' -e 's/.*data"://' | tr "}" "\n" | sed -n 's/.*file":"\(.*\)","label":"\(.*\)","type.*/\2>\1/p')" 35 | 36 | _generate_json () { 37 | json="[" 38 | while read -r quality_and_url; do 39 | quality="${quality_and_url%%">"*}" 40 | url="${quality_and_url#*">"}" 41 | json=''"${json}"'{"id":"'"$id"'", "url": "'"$url"'", "title": "'"${title} (${quality})"'", "scraper": "ani-gogohd-link"},' 42 | done <<-eof 43 | $result_links 44 | eof 45 | json="${json%,}]" 46 | echo "$json" 47 | } ;; 48 | *) 49 | command_exists "openssl" || die 3 "openssl is necessary for gogoanime scraping\n" 50 | refr="https://gogohd.net/" 51 | [ -z "$id" ] && return 0 52 | secret_key=$(printf "%s" "$full_gogo_resp" | sed -n '2p' | tr -d "\n" | od -A n -t x1 | tr -d " |\n") 53 | iv=$(printf "%s" "$resp" | sed -n '3p' | tr -d "\n" | od -A n -t x1 | tr -d " |\n") 54 | second_key=$(printf "%s" "$resp" | sed -n '4p' | tr -d "\n" | od -A n -t x1 | tr -d " |\n") 55 | token=$(printf "%s" "$resp" | head -1 | base64 -d | openssl enc -d -aes256 -K "$secret_key" -iv "$iv" | sed -n 's/.*&\(token.*\)/\1/p') 56 | ajax=$(printf '%s' "$id" | openssl enc -e -aes256 -K "$secret_key" -iv "$iv" -a) 57 | data=$(curl -A "$useragent" -sL -H "X-Requested-With:XMLHttpRequest" "${gogohd_url}encrypt-ajax.php?id=${ajax}&alias=${id}&${token}" | sed -e 's/{"data":"//' -e 's/"}/\n/' -e 's/\\//g') 58 | result_links="$(printf '%s' "$data" | base64 -d 2>/dev/null | openssl enc -d -aes256 -K "$second_key" -iv "$iv" 2>/dev/null | 59 | tr -d \\\\ | sed -n "s_.*file\":\"\([^\"]*\)\".*source.*_\1_p")" 60 | _generate_json () { 61 | echo '[{"ID": "'"${title}-ani-gogohd"'", "title": "'"${title}"'", "scraper": "ani-gogohd-link", "url": "'"$result_links"'"}]' 62 | } ;; 63 | 64 | esac 65 | [ -n "$result_links" ] && _generate_json 66 | } 67 | 68 | _supports_echo_e () { 69 | _test_against="$EOT" 70 | #if this is true, echo printed the correct char 71 | [ "$(echo -e "\x3")" = "$_test_against" ] 72 | } 73 | 74 | 75 | scrape_ani_gogohd_link () { 76 | _supports_echo_e || die 1 "Your shell does not support echo -e\n" 77 | link="$1" 78 | output_json_file="$2" 79 | id="$(printf "%s" "$link" | sed -n 's/.*title=\([^&]\+\).*/\1/p')" 80 | #this replaces all percents with \\x so that printf can convert them to real values 81 | title="$(echo -e "$(printf "%s" "$id" | sed 'y/+/-/; s/%/\\x/g')")" 82 | id="$(printf "%s" "$link" | sed -n 's/.*id=\([^&]*\).*/\1/p')" 83 | gogohd_url="https://gogohd.net/" 84 | resp=$(_get_request "${gogohd_url}streaming.php?id=$id" -l | 85 | sed -n 's/.*class="container-\(.*\)">/\1/p ; 86 | s/.*class="wrapper container-\(.*\)">/\1/p ; 87 | s/.*class=".*videocontent-\(.*\)">/\1/p ; 88 | s/.*data-value="\(.*\)">.*/\1/p ; 89 | s/.*data-status="1".*data-video="\(.*\)">.*/\1/p') 90 | 91 | provider=1 92 | i=0 93 | while [ "$i" -lt 3 ] && [ -z "$result_links" ]; do 94 | print_info "Trying provider: $(_provider_number_to_name $provider)\n" 95 | data="$(_ani_gogohd_generate_link "$id" "$provider" "$resp")" 96 | [ -n "$data" ] && break 97 | provider=$((provider % 3 + 1)) 98 | i=$((i+1)) 99 | done 100 | printf "%s\n" "$data" >> "$output_json_file" 101 | } 102 | -------------------------------------------------------------------------------- /addons/scrapers/ard: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | _ard_get_thumbnail_width () { 4 | case "$1" in 5 | maxres) echo "4096" ;; # 4K 6 | maxresdefault) _ard_get_thumbnail_width "maxres" ;; 7 | high) echo "1920" ;; # Full HD 8 | default) _ard_get_thumbnail_width "high" ;; 9 | medium) echo "960" ;; # 540p 10 | sddefault) _ard_get_thumbnail_width "medium" ;; 11 | *) _ard_get_thumbnail_width "default" ;; 12 | esac 13 | } 14 | 15 | _ard_correct_ytdl_pref () { 16 | ytdl_pref="best" 17 | [ $__is_fzf_preview -eq 0 ] && print_warning "ytdl_pref $1 ist not supported for ARD, fallback to best\n" 18 | } 19 | 20 | scrape_ard () { 21 | search=$(echo "$1" | sed 's/ /+/g') 22 | [ "$search" = ":help" ] && print_info "Search https://www.ardmediathek.de/\n" && return 100 23 | output_json_file="$2" 24 | search_url="https://api.ardmediathek.de/search-system/mediathek/ard/search/vods?query=$search&pageNumber=0&pageSize=$((20 * "${pages_to_scrape:-1}"))" 25 | _tmp_json="${session_temp_dir}/ard_search.json" 26 | _thumbnail_width=$(_ard_get_thumbnail_width "$thumbnail_quality") 27 | curl "$search_url" > "$_tmp_json" 28 | jq ' 29 | def pad_left(n; num): 30 | num | tostring | 31 | if (n > length) then ((n - length) * "0") + (.) else . end 32 | ; 33 | [.teasers[]| 34 | { 35 | scraper: "ard", 36 | ID: .id, 37 | url: "https://www.ardmediathek.de/video/\(.id)", 38 | title: .longTitle, 39 | channel: .show.title, 40 | thumbs: (.images.aspect16x9.src | gsub("{width}"; "'"$_thumbnail_width"'")), 41 | duration: "\(.duration / 60 | floor):\(pad_left(2; .duration % 60))", 42 | date: "\(.broadcastedOn | fromdateiso8601 | strftime("%Y-%m-%d"))", 43 | description: .show.synopsis, 44 | }]' < "$_tmp_json" >> "$output_json_file" 45 | } 46 | -------------------------------------------------------------------------------- /addons/scrapers/ddg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | scrape_ddg () { 4 | case "$url_handler" in 5 | video_player|audio_player|downloader|multimedia_player) die 1 "It appears your url_handler is not a webbrowser" ;; 6 | esac 7 | search=$1 8 | [ "$search" = ":help" ] && print_info "search ddg" && return 100 9 | command_exists "ddgr" || die 3 "ddgr is used to scrape duckduckgo" 10 | output_json_file="$2" 11 | 12 | _tmp_json="${session_temp_dir}/ddg.json" 13 | 14 | ddgr --json "$search" | jq '[.[]|{scraper: "ddg", url: .url, title: .title, ID: 3, channel: "", duration: "", views: "", description: .description}]' >> "$output_json_file" 15 | } 16 | -------------------------------------------------------------------------------- /addons/scrapers/feed-url: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | handle_link () { 4 | link=$1 5 | domain="${link#https://}" 6 | domain="${domain%%/*}" 7 | case "$domain" in 8 | #invidious list found here: https://docs.invidious.io/instances 9 | *youtube*|*invidious*|vid.puffyan.us|yewtu.be|inv.riverside.rocks|yt.artemislena.eu|tube.cthd.icu) 10 | link=$(_get_real_channel_link "$1") 11 | final="https://www.youtube.com/feeds/videos.xml?channel_id=${link##*/}" 12 | ;; 13 | *reddit*) final="$link/.rss" ;; 14 | esac 15 | } 16 | 17 | scrape_feed_url () { 18 | IFS=" " 19 | if [ "$1" = ":help" ]; then 20 | printf "%s\n" "Get the rss feed for something 21 | supported searches: 22 | youtube/ 23 | 24 | r/subreddit 25 | 26 | example searches: 27 | youtube/pewdiepie 28 | r/linux" 29 | return 100 30 | fi 31 | for link in $1; do 32 | case "$link" in 33 | r/*) final="https://www.reddit.com/r/${link#r/}/.rss" ;; 34 | youtube/*) 35 | link="https://www.youtube.com/user/${link#youtube/}" 36 | handle_link "$link" 37 | ;; 38 | *) 39 | handle_link "$link" ;; 40 | esac 41 | #i honestly don't even know how it's possible that non-printable characters end up here 42 | final="$(printf "%s" "$final" | sed 's/[^[:print:]]//g')" 43 | printf "%s\n" "$final" 44 | _get_request "$final" -L --head --silent -f > /dev/null|| print_warning "Warning: $final, does not appear to be a real url\n" 45 | done 46 | exit 0 47 | } 48 | -------------------------------------------------------------------------------- /addons/scrapers/invidious-popular: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | __invidious_search_json_videos () { 4 | jq ' 5 | def pad_left(n; num): 6 | num | tostring | 7 | if (n > length) then ((n - length) * "0") + (.) else . end 8 | ; 9 | [ .[] | select(.type=="shortVideo") | 10 | { 11 | scraper: "invidious_popular", 12 | ID: .videoId, 13 | url: "'"${yt_video_link_domain}"'/watch?v=\(.videoId)", 14 | title: .title, 15 | channel: .author, 16 | thumbs: "'"${invidious_instance}"'/vi/\(.videoId)/'"$thumbnail_quality"'.jpg", 17 | duration: "\(.lengthSeconds / 60 | floor):\(pad_left(2; .lengthSeconds % 60))", 18 | views: "\(.viewCount)", 19 | date: .publishedText, 20 | description: .description 21 | } 22 | ]' 23 | } 24 | 25 | scrape_invidious_popular(){ 26 | search="$1" 27 | [ "$search" = ":help" ] && print_info "Scrapes what is currently popular on $invidious_instance\n" && return 100 28 | output_json_file="$2" 29 | 30 | echo "scraping invidious popular" 31 | while [ "${i:=1}" -le "$pages_to_scrape" ]; do 32 | _tmp_json="${session_temp_dir}/invidious-popular-$i.json" 33 | 34 | [ "$invidious_instance" = "http://localhost:3000" ] && invidious_instance="https://ytprivate.com" 35 | _get_request "$invidious_instance/api/v1/popular" \ 36 | -G --data-urlencode "page=$1" --compressed > "$_tmp_json" 37 | 38 | _get_invidious_thumb_quality_name 39 | 40 | { 41 | _invidious_search_json_live < "$_tmp_json"| jq '[.[]|.scraper="invidious_popular"]' 42 | __invidious_search_json_videos "invidious_popular" < "$_tmp_json" 43 | _invidious_search_json_channel < "$_tmp_json" | jq '[ .[]|.scraper="invidious_popular" ]' 44 | _invidious_search_json_playlist < "$_tmp_json" | jq '[ .[]|.scraper="invidious_popular" ]' 45 | } >> "$output_json_file" 46 | i=$((i+1)) 47 | done 48 | unset i output_json_file 49 | } 50 | -------------------------------------------------------------------------------- /addons/scrapers/osu: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | scrape_osu(){ 3 | [ "$1" = ":help" ] && print_info "gets the newest beatmaps, and opens the link in $BROWSER\n" && return 100 4 | url_handler="$BROWSER" 5 | output_json_file="$2" 6 | curl -Li \ 7 | -H "Accept: application/json, text/javascript, */*; q=0.01" \ 8 | -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0" \ 9 | https://osu.ppy.sh/beatmapsets | grep '{"beatmapsets"' > "${session_temp_dir}/osu.json" 10 | 11 | jq '[.beatmapsets[]|{"ID": "osu-\(.id)", "channel": .creator, "thumbs": .covers.cover, "title": .title_unicode, "scraper": "osu", "views": "\(.play_count)", "url": "https://osu.ppy.sh/beatmapsets/\(.id)", "bpm": "\(.bpm)", "diffs": "\(.beatmaps|length)"}]' < "${session_temp_dir}/osu.json" > "$output_json_file" 12 | } 13 | 14 | on_startup_osu () { 15 | scrape_search_exclude="$scrape_search_exclude osu " 16 | } 17 | 18 | thumbnail_video_info_text_osu () { 19 | bpm="$(_get_video_json_attr "bpm")" 20 | diffs="$(_get_video_json_attr "diffs")" 21 | [ "$views" -eq "$views" ] 2>/dev/null && views="$(printf "%s" "$views" | add_commas)" 22 | [ -n "$title" ] && printf "\n ${c_cyan}%s" "$title" 23 | [ -n "$channel" ] && printf "\n ${c_blue}Channel: ${c_green}%s" "$channel" 24 | [ -n "$bpm" ] && printf "\n ${c_blue}BPM: ${c_red}%s" "$bpm" 25 | [ -n "$diffs" ] && printf "\n ${c_blue}Diffs: ${c_red}%s" "$diffs" 26 | [ -n "$views" ] && printf "\n ${c_blue}Views: ${c_magenta}%s" "$views" 27 | } 28 | -------------------------------------------------------------------------------- /addons/scrapers/pictures: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | on_startup_pictures () { 4 | scrape_search_exclude="${scrape_search_exclude} pictures " 5 | } 6 | 7 | thumbnail_video_info_text_pictures () { 8 | size="$(_get_video_json_attr "size")" 9 | creation="$(_get_video_json_attr "creation")" 10 | 11 | [ "$title" ] && printf "\n ${c_cyan}%s (INODE: %s)${c_clear}" "$title" "$id" 12 | [ "$creation" ] && printf "\n ${c_blue}Created: %s${c_clear}" "$creation" 13 | 14 | { 15 | ffprobe_wh="$(ffprobe -of json -show_frames "${title}" 2>/dev/null | jq -r '.[][0] | "\(.width) \(.height)"')" 16 | width="${ffprobe_wh% *}" 17 | height="${ffprobe_wh#* }" 18 | printf "\n ${c_yellow}%sx%s${c_clear}" "${width}" "${height}" 19 | } & 20 | } 21 | 22 | scrape_pictures () { 23 | search="${1:-"${XDG_PICTURES_HOME:-"$HOME/Pictures"}"}" 24 | [ "$search" = ":help" ] && printf "%s\n" "Display your pictures folder, or folder if a search is gven" "use :w as the search to search possible wallpaper folders" "set YTFZF_PICTURES_WALLPAPER_PATH to different paths if you do not like the default" && return 100 25 | output_json_file="${2}" 26 | : "${YTFZF_PICTURES_WALLPAPER_PATH:="/usr/share/wallpapers:/usr/share/backgrounds:$HOME/.local/share/wallpapers:$HOME/.local/share/backgrounds"}" 27 | case "$search" in 28 | :wall|:wallpapers|:w|:wallpaper) 29 | prepare_for_set_args ":" 30 | for path in $YTFZF_PICTURES_WALLPAPER_PATH; do 31 | [ -d "$path" ] && find "${path}" -type f -printf '{"ID": "%i", "size": "%s", "title": "%p", "thumbs": "file://%p", "url": "file://%p", "creation": "%Bc", "scraper": "pictures"}\n' | jq '. as $data | $data + {"url": "\($data.url|gsub(" ";"%20"))", "thumbs": "\($data.url|gsub(" ";"%20"))"}' | jq -s >> "$output_json_file" 32 | done 33 | end_of_set_args 34 | return 0 35 | esac 36 | 37 | find "${search}" -type f -printf '{"ID": "%i", "size": "%s", "title": "%p", "thumbs": "file://%p", "url": "file://%p", "creation": "%Bc", "scraper": "pictures"}\n' | jq '. as $data | $data + {"url": "\($data.url|gsub(" ";"%20"))", "thumbs": "\($data.url|gsub(" ";"%20"))"}' | jq -s >> "$output_json_file" 38 | } 39 | -------------------------------------------------------------------------------- /addons/scrapers/recommended: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # needs search_query as $* 3 | ## Scrape data and store video information in videos_data ( and thumbnails ) 4 | 5 | on_startup_recommended () { 6 | scrape_search_exclude="$scrape_search_exclude recommended " 7 | } 8 | 9 | scrape_recommended () { 10 | search=$1 11 | [ "$search" = ":help" ] && print_info "scrapers your recommended list based on your tracker cookie\n" && return 100 12 | output_json_file=$2 13 | _tmp_html="${session_temp_dir}/yt-search.html" 14 | _tmp_json="${session_temp_dir}/yt-search.json" 15 | _cookie_file="$HOME/.config/ytfzf/recommended-cookie" 16 | 17 | printf "%s\n" "Scraping Youtube..." 18 | 19 | [ -f "$_cookie_file" ] || { print_info "It seems you do not have a cookie file, follow the instructions below to get this scraper to work: 20 | 1. open youtube 21 | 2. sign in 22 | 3. press ctrl + shift + i (open inspect element) 23 | 4. go to network tab 24 | 5. refresh the page 25 | 6. click the get request for either the file '/' or 'https://www.youtube.com/api/stats/watchtime' 26 | 7. find request headers 27 | 8. copy the Cookie header 28 | 9. put in $_cookie_file 29 | 10. add 'custom_scrape_search_exclude=\"recommended\"' to your config file (technically optional) 30 | 11. run 'ytfzf -c recommended' 31 | "; exit; } 32 | 33 | read -r cookie < "$_cookie_file" 34 | cookie="${cookie#Cookie: }" 35 | curl -s -f -b "$_cookie_file" "https://www.youtube.com" \ 36 | -H "User-Agent: $4" \ 37 | -H 'Accept-Language: en-US,en;q=0.9' \ 38 | -H "Cookie: $cookie" \ 39 | --compressed > "$_tmp_html" || exit "$?" 40 | sed -n '/var *ytInitialData/,$p' < "$_tmp_html" | 41 | tr -d '\n' | 42 | sed -E ' s_^.*var ytInitialData ?=__ ; s_;.*__ ;' > "$_tmp_json" 43 | 44 | #gets a list of videos 45 | { 46 | jq '[ .contents| 47 | ..|.videoRenderer? | 48 | select(. !=null) | 49 | { 50 | scraper: "youtube_search", 51 | url: "'"${yt_video_link_domain}"'/watch?v=\(.videoId)", 52 | title: .title.runs[0].text, 53 | channel: .longBylineText.runs[0].text, 54 | duration:.lengthText.simpleText, 55 | views: .shortViewCountText.simpleText, 56 | date: .publishedTimeText.simpleText, 57 | description: .detailedMetadataSnippets[0].snippetText.runs[0].text, 58 | ID: .videoId, 59 | thumbs: .thumbnail.thumbnails[0].url 60 | } 61 | ]' < "$_tmp_json" 62 | 63 | jq '[ .contents| 64 | ..|.playlistRenderer? | 65 | select(. !=null) | 66 | { 67 | url: "'"${yt_video_link_domain}"'/playlist?list=\(.videoId)", 68 | title: "[Playlist] \(.title.simpleText)", 69 | channel: .longBylineText.runs[0].text, 70 | duration: "\(.videoCount) videos", 71 | views: "playlist", 72 | date: "playlist", 73 | ID: .playlistId, 74 | thumbs: .thumbnails[0].thumbnails[0].url, 75 | action: "scrape type=invidious-playlist search='"${yt_video_link_domain}"'/playlist?list=\(.playlistId)" 76 | } 77 | ]' <"$_tmp_json" 78 | } >> "$output_json_file" 79 | } 80 | -------------------------------------------------------------------------------- /addons/scrapers/scrape_list: -------------------------------------------------------------------------------- 1 | YTFZF_SCRAPELIST_FILE="$HOME/.config/ytfzf/scrapelist" 2 | 3 | scrape_scrape_list () { 4 | f=${1:-YTFZF_SCRAPELIST_FILE} 5 | [ "$f" = ":help" ] && print_info "Search should be a scrapelist file\nthis is an example scrapelist file:\nY youtube search\nO odysee search\n" && return 100 6 | output_json_file=$2 7 | ! [ -f "${f}" ] && die 2 "scrape list file ($f) doesn't exist\n" 8 | 9 | _start_series_of_threads 10 | i=0 11 | while IFS=' ' read -r _scr_type s; do 12 | { 13 | ytfzf_video_json_file="${session_temp_dir}/SL-$i.json.final" scrape_website "$_scr_type" "$s" 14 | handle_scrape_error "$?" 15 | } & 16 | _thread_started "$!" 17 | i=$((i+1)) 18 | done <<- EOF 19 | $(sed \ 20 | -e "s/#.*//" \ 21 | -e "/^[[:space:]]*$/d" \ 22 | "$f" ) 23 | EOF 24 | wait 25 | _concatinate_json_file "${session_temp_dir}/SL-" "$i" "$output_json_file" 26 | } 27 | -------------------------------------------------------------------------------- /addons/scrapers/video-info: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | #this scraper uses return youtube dislike's api: https://returnyoutubedislike.com/ 4 | 5 | scrape_video_info () { 6 | set -f 7 | unset IFS 8 | videos="$(printf "%s\n" $1)" 9 | output_json_file=$2 10 | printf "Not sure what 'clearly attributed means', but i don't like cutting people short\nThis scraper uses return youtube dislike's api: https://returnyoutubedislike.com/\n" >&2 11 | _get_invidious_thumb_quality_name 12 | while read -r line; do 13 | { 14 | id="${line##*=}" 15 | print_info "Scraping video info for $id\n" 16 | _tmp_json="${session_temp_dir}/video-info-ratings-$id.json" 17 | _get_request "https://returnyoutubedislikeapi.com/votes?videoid=$id" > "$_tmp_json" || exit "$?" 18 | rating_json="$(jq '{"likes": "\(.likes)", "dislikes": "\(.dislikes)"}' < "$_tmp_json")" 19 | _get_request "${invidious_instance}/api/v1/videos/$id" | 20 | jq ' 21 | def pad_left(n; num): 22 | num | tostring | 23 | if (n > length) then ((n - length) * "0") + (.) else . end 24 | ; 25 | { 26 | "scraper": "video_info", 27 | "title": .title, 28 | "ID": .videoId, 29 | "genre": .genre, 30 | "author": .author, 31 | "url": "'"$line"'", 32 | "duration": "\(.lengthSeconds / 60 | floor):\(pad_left(2; .lengthSeconds % 60))", 33 | "description": .description, 34 | "thumbs": "'"${invidious_instance}"'/vi/\(.videoId)/'"$thumbnail_quality"'.jpg" 35 | }' | jq '[ . + '"$rating_json"' ]' >> "$_tmp_json.final" 36 | } & 37 | count=$((count+1)) 38 | #youtube dislikes only allows 100 pings per minute 39 | [ $count -ge 99 ] && break 40 | done <<-EOF 41 | $videos 42 | EOF 43 | wait 44 | set +f 45 | cat "${session_temp_dir}/video-info-ratings"*".final" >> "$output_json_file" 46 | } 47 | 48 | thumbnail_video_info_text_video_info () { 49 | IFS=';' read -r genre likes dislikes <<-EOF 50 | $(jq -r --arg url "$url" '.[]|select(.url == $url )| .genre + ";" + .likes + ";" + .dislikes' < "$video_json_file") 51 | EOF 52 | printf "${c_cyan}%s${c_reset} (%s)\n" "$title" "$id" 53 | printf "${c_yellow}%s${c_reset}\n" "$genre" 54 | printf "${c_green}%s${c_reset}/${c_red}%s${c_reset}\n" "$(printf "%s" "$likes" | add_commas)" "$(printf "%s" "$dislikes" | add_commas)" 55 | } 56 | -------------------------------------------------------------------------------- /addons/scrapers/yt-music: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | _yt_music_get_playlist_json () { 4 | jq '..|.musicResponsiveListItemRenderer?|select(.!=null)|select(..|.musicResponsiveListItemFlexColumnRenderer?.text.runs|length == 5) 5 | | { 6 | title: .flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text, 7 | duration: .flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[-1].text, 8 | url: "https://music.youtube.com/playlist?list=\(.menu.menuRenderer.items[0].menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId)", 9 | thumbs: .thumbnail.musicThumbnailRenderer.thumbnail.thumbnails[-1].url, 10 | action: "scrape type=yt-music-playlist search=https://music.youtube.com/playlist?list=\(.menu.menuRenderer.items[0].menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId)", 11 | ID: .menu.menuRenderer.items[0].menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId }' | jq '[inputs]' 12 | } 13 | 14 | _yt_music_get_song_json () { 15 | jq '..|.musicResponsiveListItemRenderer?|select(.!=null)|select(..|.musicResponsiveListItemFlexColumnRenderer?.text.runs|length == 7) 16 | | { 17 | title: .flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text, 18 | channel: .flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[2].text, 19 | views: .flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[4].text, 20 | duration: .flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[-1].text, 21 | url: "https://music.youtube.com/watch?v=\(.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint.watchEndpoint.videoId)", 22 | ID: .flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint.watchEndpoint.videoId, 23 | thumbs: .thumbnail.musicThumbnailRenderer.thumbnail.thumbnails[-1].url}' | jq '[inputs]' 24 | } 25 | 26 | scrape_yt_music () { 27 | search="$1" 28 | output_json_file="$2" 29 | _tmp_html="${session_temp_dir}/yt-music.html" 30 | _tmp_json="${session_temp_dir}/yt-music.json" 31 | url="https://music.youtube.com/search" 32 | _get_request "$url" -G --data-urlencode "q=$search" > "$_tmp_html" 33 | 34 | if [ -f $YTFZF_CUSTOM_SCRAPERS_DIR/yt-music-utils/convert-ascii-escape.pl ]; then 35 | utils_path=$YTFZF_CUSTOM_SCRAPERS_DIR/yt-music-utils/convert-ascii-escape.pl 36 | elif [ -f "$YTFZF_SYSTEM_ADDON_DIR"/scrapers/yt-music-utils/convert-ascii-escape.pl ]; then 37 | utils_path="$YTFZF_SYSTEM_ADDON_DIR"/scrapers/yt-music-utils/convert-ascii-escape.pl 38 | else 39 | print_error "The convert-ascii-escape.pl file could not be found\n" 40 | exit 1 41 | fi 42 | 43 | 44 | sed -n "s/.*data: '\([^']*\)'.*/\1/p" < "$_tmp_html" | "$utils_path" > "$_tmp_json" 45 | { 46 | _yt_music_get_playlist_json < "$_tmp_json" 47 | _yt_music_get_song_json < "$_tmp_json" 48 | } > "$output_json_file" 49 | } 50 | -------------------------------------------------------------------------------- /addons/scrapers/yt-music-playlist: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _yt_music_get_playlist_json () { 3 | jq '..|.musicResponsiveListItemRenderer?|select(.!=null)|select(..|.musicResponsiveListItemFlexColumnRenderer?.text.runs|length == 5) 4 | | { 5 | title: .flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text, 6 | duration: .flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[-1].text, 7 | url: "https://music.youtube.com/playlist?list=\(.menu.menuRenderer.items[0].menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId)", 8 | thumbs: .thumbnail.musicThumbnailRenderer.thumbnail.thumbnails[-1].url, 9 | action: "scrape type=yt-music-playlist search=https://music.youtube.com/playlist?list=\(.menu.menuRenderer.items[0].menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId)", 10 | ID: .menu.menuRenderer.items[0].menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId }' | jq '[inputs]' 11 | } 12 | 13 | _yt_music_get_song_json () { 14 | jq '..|.musicResponsiveListItemRenderer?|select(.!=null) 15 | | { 16 | title: .flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text, 17 | channel: .flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[-1].text, 18 | duration: .fixedColumns[0].musicResponsiveListItemFixedColumnRenderer.text.runs[0].text, 19 | url: "https://music.youtube.com/watch?v=\(.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint.watchEndpoint.videoId)", 20 | ID: .flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].navigationEndpoint.watchEndpoint.videoId, 21 | thumbs: .thumbnail.musicThumbnailRenderer.thumbnail.thumbnails[-1].url}' | jq '[inputs]' 22 | } 23 | 24 | scrape_yt_music_playlist () { 25 | search="$1" 26 | output_json_file="$2" 27 | _tmp_html="${session_temp_dir}/yt-music-playlist.html" 28 | _tmp_json="${session_temp_dir}/yt-music-playlist.json" 29 | _get_request "$search" > "$_tmp_html" 30 | 31 | if [ -f $YTFZF_CUSTOM_SCRAPERS_DIR/yt-music-utils/convert-ascii-escape.pl ]; then 32 | utils_path=$YTFZF_CUSTOM_SCRAPERS_DIR/yt-music-utils/convert-ascii-escape.pl 33 | elif [ -f "$YTFZF_SYSTEM_ADDON_DIR"/scrapers/yt-music-utils/convert-ascii-escape.pl ]; then 34 | utils_path="$YTFZF_SYSTEM_ADDON_DIR"/scrapers/yt-music-utils/convert-ascii-escape.pl 35 | else 36 | print_error "The convert-ascii-escape.pl file could not be found\n" 37 | exit 1 38 | fi 39 | 40 | sed -n "s/.*data: '\([^']*\)'.*/\1/p" < "$_tmp_html" | "$utils_path" > "$_tmp_json" 41 | { 42 | #_yt_music_get_playlist_json < "$_tmp_json" 43 | _yt_music_get_song_json < "$_tmp_json" 44 | } > "$output_json_file" 45 | } 46 | -------------------------------------------------------------------------------- /addons/scrapers/yt-music-utils/convert-ascii-escape.pl: -------------------------------------------------------------------------------- 1 | #!/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | while(<>){ 7 | s/\\x([0-9a-fA-F]{2})/chr(hex($1))/eg; 8 | print; 9 | } 10 | -------------------------------------------------------------------------------- /addons/sort-names/alpha: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | get_sort_by () { 3 | _video_json_line="$1" 4 | title="${_video_json_line##*'"title":"'}" 5 | title="${title%%\"*}" 6 | printf "%s" "$title" 7 | } 8 | data_sort_fn () { 9 | sort 10 | } 11 | -------------------------------------------------------------------------------- /addons/sort-names/alpha-rev: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | get_sort_by () { 3 | _video_json_line="$1" 4 | title="${_video_json_line##*'"title":"'}" 5 | title="${title%%\"*}" 6 | printf "%s" "$title" 7 | } 8 | data_sort_fn () { 9 | sort -r 10 | } 11 | -------------------------------------------------------------------------------- /addons/thumbnail-viewers/display-viewer: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | die () { 4 | printf "\033[31m$2\033[0m" 5 | exit "$1" 6 | } 7 | command_exists () { 8 | command -v "$1" > /dev/null 2>&1 9 | } 10 | 11 | 12 | case "$1" in 13 | start) command_exists "display" || die 3 "\nimagemagick is not installed" ;; 14 | stop) : ;; 15 | no-img) killall display ;; 16 | view) 17 | killall display 18 | display "$2" ;; 19 | esac 20 | -------------------------------------------------------------------------------- /addons/thumbnail-viewers/foot: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | die () { 4 | printf "\033[31m$2\033[0m" 5 | exit "$1" 6 | } 7 | command_exists () { 8 | command -v "$1" > /dev/null 2>&1 9 | } 10 | 11 | thumb_path="$2" 12 | foodpid="${TMPDIR:-/tmp}/ytfzf-foot.pid" 13 | case "$1" in 14 | start) 15 | for dep in img2sixel foot; do 16 | command_exists "$dep" || die 3 "\n$dep is not installed" 17 | done 18 | export const_FOOT_PID_FILE="" ;; 19 | stop) kill "$(cat "${foodpid}")" > /dev/null 2>&1 ;; 20 | no-img) : ;; 21 | view) 22 | kill $(cat "${foodpid}") > /dev/null 2>&1 23 | foot --app-id='ytfzf-foot' -H -e img2sixel "$thumb_path" > /dev/null 2>&1 & 24 | printf "%s\n" "$!" > "${foodpid}" 25 | ;; 26 | esac 27 | -------------------------------------------------------------------------------- /addons/thumbnail-viewers/w3m: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | w3mimgdisplay_path="${w3mimgdisplay_path:-/usr/lib/w3m/w3mimgdisplay}" 4 | 5 | action=$1 path=$2 width=$5 6 | 7 | die () { 8 | printf "\033[31m$2\033[0m" 9 | exit "$1" 10 | } 11 | command_exists () { 12 | command -v "$1" > /dev/null 2>&1 13 | } 14 | 15 | 16 | view () { 17 | while :; do 18 | printf "%b\n%s;\n" "0;1;10;130;$((width*5));$((width*3));;;;;$1" 3 | "$w3mimgdisplay_path" 19 | done 20 | } 21 | 22 | case "$action" in 23 | start) 24 | command_exists "w3m" || die 3 "w3m must be installed\n" 25 | [ -f "$w3mimgdisplay_path" ] || die 3 "$w3mimgdisplay_path does not exist\n" 26 | ;; 27 | view) view "$path" ;; 28 | esac 29 | -------------------------------------------------------------------------------- /addons/url-handlers/atp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | read -r _ytdl_pref _is_audio_only _is_detach _video_pref _audio_pref url_handler_opts _ 4 | 5 | ytfzf_video_json_file="${YTFZF_VIDEO_JSON_FILE}" 6 | 7 | playlist_name="${_PLAYLIST_NAME}" 8 | 9 | for url in "$@"; do 10 | jq -r --arg url "$url" '.[]|select(.url==$url)' < "$ytfzf_video_json_file" 11 | done | jq -s '.' >> "$playlist_name" 12 | 13 | printf "%s\n" "$@" "has been added to $playlist_name" 14 | -------------------------------------------------------------------------------- /addons/url-handlers/mpvq: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # in detach mode (e. g. via `--detach`) urls are being 3 | # appended to mpv playlist instead of being played immediately 4 | # 5 | # usage: 6 | # append --url-handler=mpvq to ytfzf args 7 | mpv_socket=${TMPDIR:-/tmp}/${USER}-ytfzf-mpv-socket 8 | 9 | die () { 10 | printf "\033[31m$2\033[0m" 11 | exit "$1" 12 | } 13 | command_exists () { 14 | command -v "$1" > /dev/null 2>&1 15 | } 16 | 17 | 18 | 19 | for dep in lsof mpv socat; do 20 | command_exists "$dep" || die 3 "$dep is not installed and is required for the mpvq url handler\n" 21 | done 22 | 23 | IFS="$(printf '\t')" 24 | read -r ytdl_pref is_audio_only is_detach video_pref audio_pref url_handler_opts _ 25 | unset IFS 26 | 27 | case "$is_detach" in 28 | 0*) /usr/bin/mpv $fs "$@" 29 | ;; 30 | 1*) lsof "${mpv_socket}" >/dev/null 2>&1 31 | if [ $? -ne 0 ] 32 | then 33 | /usr/bin/mpv $fs --input-ipc-server=${mpv_socket} --idle=yes "$@" >/dev/null 2>&1 & 34 | else 35 | echo "loadfile $@ append-play" | socat - "${mpv_socket}" 36 | fi 37 | ;; 38 | esac 39 | -------------------------------------------------------------------------------- /addons/url-handlers/rfp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | read -r _ytdl_pref _is_audio_only _is_detach _video_pref _audio_pref url_handler_opts _ 4 | 5 | ytfzf_video_json_file="${YTFZF_VIDEO_JSON_FILE}" 6 | 7 | playlist_name="${_PLAYLIST_NAME}" 8 | 9 | old_data="$(cat "$playlist_name")" 10 | 11 | for url in "$@"; do 12 | jq --arg url "$url" '[ .[] | select(.url != $url) ]' < "$playlist_name" 16 | 17 | 18 | printf "%s\n" "$@" "has been removed from $playlist_name" 19 | -------------------------------------------------------------------------------- /addons/url-handlers/vlc-player: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | IFS="$(printf '\t')" 4 | read -r ytdl_pref is_audio_only is_detach video_pref audio_pref url_handler_opts _ 5 | 6 | die () { 7 | printf "\033[31m$2\033[0m" 8 | exit "$1" 9 | } 10 | command_exists () { 11 | command -v "$1" > /dev/null 2>&1 12 | } 13 | 14 | command_exists "vlc" || die 3 "vlc must be installed for the vlc url handler\n" 15 | 16 | unset IFS 17 | 18 | set -f 19 | case "$is_detach" in 20 | 0) vlc $url_handler_opts "$@" ;; 21 | 1) setsid -f vlc $url_handler_opts "$@" ;; 22 | esac 23 | -------------------------------------------------------------------------------- /credits/bsgalvan.md: -------------------------------------------------------------------------------- 1 | # bsgalvan 2 | 3 | * sensible `invidious_instance` selection 4 | -------------------------------------------------------------------------------- /credits/euro20179.md: -------------------------------------------------------------------------------- 1 | # Euro20179 2 | 3 | * lead maintainer 4 | * subscriptions 5 | * conf.sh 6 | * addons 7 | * playlists 8 | -------------------------------------------------------------------------------- /credits/gardockt.md: -------------------------------------------------------------------------------- 1 | # Gardockt 2 | 3 | * Various bugfixes 4 | -------------------------------------------------------------------------------- /credits/jac-zac.md: -------------------------------------------------------------------------------- 1 | # Jac-Zac 2 | 3 | * The logo 4 | 5 | * Makefile 6 | -------------------------------------------------------------------------------- /credits/mathisto.md: -------------------------------------------------------------------------------- 1 | # mathisto 2 | 3 | * I refactored the `title_str` function to not be awful. -------------------------------------------------------------------------------- /credits/mudskipper875.md: -------------------------------------------------------------------------------- 1 | # Mudskipper875 2 | 3 | * `--ytdl-path` 4 | -------------------------------------------------------------------------------- /credits/pystardust.md: -------------------------------------------------------------------------------- 1 | # Pystardust 2 | 3 | * Author of the script 4 | * thumbnails 5 | * download 6 | * external menu 7 | * audio only 8 | -------------------------------------------------------------------------------- /credits/qoheniac.md: -------------------------------------------------------------------------------- 1 | # Qoheniac 2 | 3 | * Helped significantly with the `swayimg` thumbnail viewer 4 | -------------------------------------------------------------------------------- /credits/simonhughxyz.md: -------------------------------------------------------------------------------- 1 | # Simonhughxyz 2 | 3 | * fzf shortcuts 4 | * search history 5 | -------------------------------------------------------------------------------- /docs/conf.sh: -------------------------------------------------------------------------------- 1 | # This is a sample config file, refer to ytfzf(5) for more information 2 | 3 | # In the previous version of ytfzf this file had all the examples, with all defaults set, 4 | # this has been changed because it made it impossible for us to change default values that were broken or causing bugs, 5 | # as everyone used the default configuration file. 6 | # we are now going to only have this sample config file, and the ytfzf(5) manual, which has explanation of every variable and function that can be set. 7 | 8 | #a sample config below: 9 | 10 | #Variables {{{ 11 | #ytdl_pref="248+bestaudio/best" 12 | ##scrape 1 video link per channel instead of the default 2 13 | #sub_link_count=1 14 | #show_thumbnails=1 15 | ##}}} 16 | # 17 | ##Functions {{{ 18 | #external_menu () { 19 | # #use rofi instead of dmenu 20 | # rofi -dmenu -width 1500 -p "$1" 21 | #} 22 | 23 | #use vlc instead of mpv 24 | #video_player () { 25 | # #check if detach is enabled 26 | # case "$is_detach" in 27 | # #disabled 28 | # 0) vlc "$@" ;; 29 | # #enabled 30 | # 1) setsid -f vlc "$@" > /dev/null 2>&1 ;; 31 | # esac 32 | #} 33 | 34 | #on_opt_parse_c () { 35 | # arg="$1" 36 | # case "$arg" in 37 | # #when scraping subscriptions enable -l 38 | # #-cSI or -cS 39 | # SI|S) is_loop=1 ;; 40 | # esac 41 | #} 42 | #}}} 43 | -------------------------------------------------------------------------------- /docs/man/ytfzf.1: -------------------------------------------------------------------------------- 1 | .TH YTFZF 1 "2021 September" "ytfzf 2.0" 2 | 3 | .SH NAME 4 | ytfzf \- search and play videos 5 | 6 | .SH SYNOPSIS 7 | .SY ytfzf 8 | .RI [ options ] 9 | .RI [ search\-query ] 10 | 11 | .SY ytfzf 12 | .RI [ options ] 13 | .RI \- 14 | 15 | .SH DESCRIPTION 16 | .PP 17 | .B ytfzf 18 | is a POSIX script that helps you find videos 19 | from Youtube, Peertube or Odysee 20 | (without API) 21 | and opens/downloads them using mpv/youtube\-dl. 22 | 23 | .SH SEARCH OPERATORS 24 | 25 | .PP 26 | Search operators are special search queries. 27 | .PP 28 | Standard search operators include: 29 | .RS 30 | .TP 31 | .IR :help 32 | Prints a possibly brief description of how to use the scraper. 33 | .RE 34 | .PP 35 | Addon scrapers may have more or less search operators 36 | 37 | .SH SHORTCUTS 38 | 39 | .PP 40 | These shortcuts will apply in any menu that supports it. 41 | .br 42 | The only menu that currently supports it is fzf. 43 | .br 44 | Shortcuts starting with alt, will exit the menu, shortcuts starting with ctrl will not. 45 | .br 46 | To change any of the defaults set the corresponding variable in your configuration file. 47 | 48 | .TP 49 | .BR download 50 | alt-d (download_shortcut) 51 | 52 | .TP 53 | .BR "watch video" 54 | alt-v (video_shortcut) 55 | 56 | .TP 57 | .BR "audio only" 58 | alt-m (audio_shortcut) 59 | 60 | .TP 61 | .BR "detatch" 62 | alt-e (detach_shortcut) 63 | 64 | .TP 65 | .BR "print link" 66 | alt-l (print_link_shortcut) 67 | 68 | .TP 69 | .BR "show formats" 70 | alt-f (show_formats_shortcut) 71 | 72 | .TP 73 | .BR "show all info" 74 | alt-i (info_shortcut) 75 | 76 | .TP 77 | .BR "new search" 78 | alt-s (search_again_shortcut) 79 | 80 | .TP 81 | .BR "scrape next page" 82 | ctrl-p (next_page_action_shortcut) 83 | 84 | .SH OPTIONS 85 | 86 | .PP 87 | Information 88 | .RS 89 | .TP 90 | .BR \-h ", " \-\-help 91 | Show help text. 92 | .TP 93 | .BR \-\-version 94 | Show ytfzf's version. 95 | .RE 96 | 97 | .PP 98 | How to play the selected videos. 99 | .RS 100 | .TP 101 | .BR \-d ", " \-\-download 102 | Download the selected videos. 103 | This can also be set in the config file with 104 | .BR is_download . 105 | .TP 106 | .BR \-m ", " \-\-audio\-only 107 | Play audio only. 108 | This can also be set in the config file with 109 | .BR is_audio_only . 110 | .TP 111 | .BR \-f ", " \-\-formats 112 | Show available formats before proceeding. 113 | .TP 114 | .BR \-\-format\-selection=\fIscreen\fR 115 | The format selection screen type to use. 116 | .PP 117 | .RS 118 | Types: 119 | .RS 120 | .TP 121 | .IR normal 122 | .TP 123 | .IR simple 124 | .RE 125 | .RE 126 | .RS 127 | This can also be set in the config file with 128 | .BR format_selection_screen . 129 | .RE 130 | .TP 131 | .BR \-\-format\-sort=\fIsort\fR 132 | The \-\-format\-sort to use in ytdl. 133 | This can also be set in the config file with 134 | .BR format_selection_sort . 135 | .TP 136 | .BR \-\-video\-pref=pref 137 | Set the ytdl video format to pref. 138 | This can also be set in the config file with 139 | .BR video_pref . 140 | .TP 141 | .BR \-\-audio\-pref=pref 142 | Set the ytdl audio format to pref. 143 | This can also be set in the config file with 144 | .BR audio_pref . 145 | .TP 146 | .BR \-\-ytdl\-pref=pref 147 | Set the ytdl format to pref. 148 | This can also be set in the config file with 149 | .BR ytdl_pref . 150 | .TP 151 | .BR \-\-detach 152 | Detach the video player from the terminal. 153 | This can also be set in the config file with 154 | .BR is_detach . 155 | .TP 156 | .BR \-u ", " \-\-url\-handler=handler 157 | The function/program to use as a url handler. 158 | This can also be set in the config file by adding 159 | .BR load_url_handler " " . 160 | .TP 161 | .BI \-I " option" 162 | Instead of playing the selected videos, 163 | get information about them in the selected format. 164 | The available options are: 165 | .RS 166 | .TP 167 | .IR L | l | link 168 | Prints the URL of the selected videos. 169 | .TP 170 | .IR VJ | vj | video\-json 171 | Prints the json of the selected videos. 172 | .TP 173 | .IR J | j | json 174 | Prints the json of all the videos in the search results. 175 | .TP 176 | .IR F | f | format 177 | Prints the video format being used. 178 | .TP 179 | .IR R | r | raw 180 | Prints the data of the selected videos, as appears in the menu. 181 | .RE 182 | .TP 183 | .B \-L 184 | Alias for \-I L 185 | .TP 186 | .BR \-\-info\-action=\Iaction 187 | The action to do when \-\-info\-wait is 1. 188 | .BR info_wait_action . 189 | .TP 190 | .BR \-\-info\-wait=\fInumber 191 | Whether or not to wait after printing info requested with \-I or \-L. 192 | This can also be set in the config file with 193 | .BR info_wait . 194 | .RE 195 | .TP 196 | .BR \-\-url\-handler\-opts=opts 197 | Opts to pass to the url handler, by default this will pass extra opts to mpv. 198 | This can also be set in the config file with 199 | .BR url_handler_opts . 200 | .RE 201 | 202 | .PP 203 | Menu options 204 | .RS 205 | .TP 206 | .BR \-l ", " \-\-loop 207 | Reopen the menu when the video stops playing. 208 | This can also be set in the config file with 209 | .BR is_loop . 210 | .TP 211 | .BR \-s ", " \-\-search\-again 212 | After closing fzf make another search. 213 | This can also be set in the config file with 214 | .BR search_again . 215 | .TP 216 | .BR \-t ", " \-\-show\-thumbnails 217 | Show thumbnails. 218 | Doesn't work with \fB\-D\fR or \fB\-H\fR. 219 | This can also be set in the config file with 220 | .BR show_thumbnails . 221 | .TP 222 | .BR \-\-async\-thumbnails 223 | Whether or not to download thumbnails asynchronously. 224 | .br 225 | Downloading asynchronously will open the menu before all thumbnails are downloaded. 226 | This can also be set in the config file with 227 | .BR async_thumbnails . 228 | .TP 229 | .BR \-\-skip\-thumb\-download 230 | Whether or not to skip the thumbnail download. 231 | This is used if you want to not have thumbnails, or use custom thumbnails in $YTFZF_CUSTOM_THUMBNAILS_DIR. 232 | .br 233 | For more info see CUSTOM THUMBNAILS in ytfzf(5) 234 | .br 235 | This can also be set in the config file with 236 | .BR skip_thumb_download . 237 | .TP 238 | .BR \-\-notify-playing 239 | Show notifications when a video is about to be played, and other notifications relating to playing videos. 240 | This can also be set in the config file with 241 | .BR notify_playing . 242 | .TP 243 | .BR \-\-preview\-side=side 244 | The preview side in fzf. 245 | .br 246 | Options: 247 | .RS 248 | .TP 249 | .IR left 250 | .TP 251 | .IR right 252 | .TP 253 | .IR up 254 | .TP 255 | .IR down 256 | .RE 257 | This can also be changed in the config file with 258 | .BR fzf_preview_side . 259 | .TP 260 | .BR \-T ", " \-\-thumb\-viewer=program 261 | Use program for displaying thumbnails. 262 | .br 263 | Built-in programs: 264 | .RS 265 | .TP 266 | .IR chafa, chafa-16, chafa-tty 267 | chafa, chafa with 16 colors, chafa with 4 colors. 268 | .TP 269 | .IR catimg, catimg-256 270 | catimg, catimg with 256 colors. 271 | .TP 272 | .IR imv 273 | Good with tiling window managers. 274 | .TP 275 | .IR mpv 276 | Similar to imv. 277 | .TP 278 | .IR swayimg 279 | Only works on the sway wayland compositor. 280 | .TP 281 | .IR swayimg-hyprland 282 | Only works on the hyprland compositor. 283 | Uses swayimg to render an image. 284 | .TP 285 | .IR 286 | Additional viewers can be put into $YTFZF_THUMBNAIL_VIEWERS_DIR. 287 | .RE 288 | This can also be changed in the config file by adding 289 | .br 290 | .BR load_thumbnail_viewer " " . 291 | .TP 292 | .BR \-D ", " \-\-external\-menu 293 | Use an external menu instead of fzf. 294 | The default is \fIdmenu\fR. 295 | This can also be set in the config file with 296 | .BR interface="ext" . 297 | .TP 298 | .BR \-i ", " \-\-interface=interface 299 | Use a custom interface script, which would be in $YTFZF_CUSTOM_INTERFACES_DIR. 300 | This can also be set in the config file by adding 301 | .BR load_interface " " . 302 | .TP 303 | .BR \-\-sort 304 | Sorts videos (after scraping) by upload date. 305 | This can also be set in the config file by adding 306 | .BR is_sort . 307 | .TP 308 | .BR \-\-sort\-name=name 309 | Calls a function set in $YTFZF_CONFIG_FILE. Or sources a script in $YTFZF_SORT_NAMES_DIR (if it exists). 310 | See SORT NAMES in ytfzf(5) for more information. 311 | .TP 312 | .BR \-\-fancy\-subs 313 | Whether or not to have a separator between each subscription. 314 | When this option is used it automatically disables \-\-sort as it will mess up this option. 315 | .br 316 | This can also be set in the config file with 317 | .BR fancy_subs . 318 | .TP 319 | .BR \-\-disable\-back 320 | Whether or not to disable the back button in submenus. 321 | .br 322 | This can also be set in the config file with 323 | .BR enable_back_button . 324 | .BR \-\-disable\-actions 325 | Whether or not to disable actions such as submenus and the back button. 326 | .br 327 | This can also be set in the config file with 328 | .BR enable_actions . 329 | .TP 330 | .BR \-\-disable\-submenus 331 | Whether or not to disable submenus. 332 | .br 333 | Submenus are the menus that happen after a playlist or channel (currently only supported by youtube/invidious) is selected 334 | .br 335 | This can also be set in the config file with 336 | .BR enable_submenus . 337 | .TP 338 | .BR \-\-keep\-vars 339 | Whether or not options passed into ytfzf also get passed into submenus. 340 | This can also be set in the config file with 341 | .BR keep_vars . 342 | .TP 343 | .BR \-\-submenu\-opts=opts 344 | The opts to use in the submenu. 345 | .br 346 | This can also be set in the config file with 347 | .BR submenu_opts . 348 | .TP 349 | .BR \-\-submenu\-scraping\-opts=opts 350 | .B DEPRECATED "(use \-\-submenu\-opts instead)" 351 | Does the same thing as \-\-submenu\-opts. 352 | .br 353 | This can also be set in the config file with 354 | .BR submenu_scraping_opts . 355 | .RE 356 | 357 | .PP 358 | Auto selecting 359 | .RS 360 | .TP 361 | .BR \-a ", " \-\-auto\-select 362 | Auto\-play the first result. 363 | .TP 364 | .BR \-A ", " \-\-select\-all 365 | Select all results. 366 | .TP 367 | .BR \-r ", " \-\-random\-select 368 | Auto\-play a random result. 369 | .TP 370 | .BR \-S " \fIsed address\fR" ", " "\-\-select=\fIsed address\fR" 371 | Auto\-play a specific video. 372 | .PP 373 | .RS 374 | Examples: 375 | .RS 376 | .TP 377 | .IR 2 378 | Select the second video 379 | .TP 380 | .IR $ 381 | Select the last video 382 | .TP 383 | .IR /^h/ 384 | Select all videos starting with h 385 | .RE 386 | .RE 387 | 388 | .TP 389 | .BR \-n " \fInumber\fR" ", " \-\-link\-count=\fInumber 390 | The \fInumber\fR of videos to select with \fB\-a\fR or \fB\-r\fR. 391 | .RE 392 | 393 | 394 | .PP 395 | Scrapers 396 | .RS 397 | .TP 398 | .BI \-c " scrapers" ", " "\-\-scrape=scrapers" 399 | Set which scraper to use. 400 | Multiple scrapers can be separated by comma (,). 401 | The currently supported builtin scrapers are: 402 | .RS 403 | .TP 404 | .IR youtube | Y 405 | Scrapes invidious' api with a search query 406 | .TP 407 | .IR youtube-channel 408 | Scrapes a youtube channel with youtube 409 | .TP 410 | .IR invidious-channel 411 | Scrapes a youtube channel with $invidious_instance 412 | .br 413 | When this scrape is active the search query is the link to a channel. 414 | .TP 415 | .IR video-recommended | R 416 | Scrapes recommended videos from an invidious video link 417 | .TP 418 | .IR youtube-playlist | invidious-playlist 419 | Scrapes a youtube playlist 420 | .br 421 | When this scrape is active the search query is the link to a playlist. 422 | .TP 423 | .IR youtube\-trending | T 424 | Scrapes invidious' api to get youtube trending. 425 | .br 426 | When this scrape is active the search query is the tab of trending to scrape. 427 | .TP 428 | .IR from\-cache 429 | Scrapes a previous scrape that happened using \-\-keep\-cache from the saved cache. 430 | .TP 431 | .IR M | multi 432 | Uses ytfzf recursively to scrape multiple things with multiple different options 433 | .br 434 | See \fIytfzf -c M :help\fR for more info 435 | .br 436 | Tabs: 437 | .RS 438 | .TP 439 | .IR gaming 440 | .TP 441 | .IR music 442 | .TP 443 | .IR movies 444 | .RE 445 | .TP 446 | .IR youtube\-subscriptions | S | SI 447 | .I SI 448 | Scrapes invidious for channels instead of youtube. Scraping youtube may result in rate limiting. 449 | .TP 450 | .IR scrape\-list | SL 451 | uses your $YTFZF_SCRAPELIST_FILE as scrape and search input. 452 | See "scrape lists" ytfzf(5) for more information. 453 | .TP 454 | .IR peertube | P 455 | .TP 456 | .IR odysee | lbry | O 457 | .TP 458 | .IR history | H 459 | (Only if $enable_hist is enabled) 460 | .TP 461 | .IR url | U 462 | Opens the url in the video player and exits 463 | .TP 464 | .IR u 465 | Treats the url as an item, and will open fzf, or the external menu. 466 | .TP 467 | .IR comments 468 | Scrapes the comments of a video link from youtube 469 | .RE 470 | .TP 471 | .BR \-H ", " \-\-history 472 | Alias for \-c H. 473 | .br 474 | Scrapes history file. 475 | .TP 476 | .BI "\-\-scrape+=scrapers" 477 | Same as \-c, but keeps the default scrape as well. 478 | .TP 479 | .BI \-\-scraper-=scrapers 480 | Removes scraper from list of scrapers to use 481 | .TP 482 | .BR \-\-multi\-search 483 | Whether or not to use multi search. 484 | .br 485 | To use multi search, separate each search with a comma, this works well when using multiple scrapers. 486 | .br 487 | This can also be set in the config file with 488 | .BR multi_search . 489 | .TP 490 | .B \-\-force\-youtube 491 | When using the \fIyoutube\fR scraper, 492 | convert the invidious links to youtube links before playing/downloading. 493 | .TP 494 | .B \-\-force\-invidious 495 | When using the \fIyoutube\fR scraper, 496 | use whatever invidious instance was chosen instead of converting to youtube links. 497 | .RE 498 | 499 | .PP 500 | Scraper Options 501 | .RS 502 | .PP 503 | Currently, \-\-video\-duration, \-\-type, \-\-thumbnail\-quality, and \-\-features only applies to the scrape: youtube/Y 504 | .TP 505 | .BI "\-\-pages=amount" 506 | Amount of pages to scrape on youtube/invidious, and the comments scraper. 507 | This can also be set in the config file with 508 | .BR pages_to_scrape . 509 | .TP 510 | .BI "\-\-pages-start=page" 511 | The page to start on. 512 | This can also be set in the config file with 513 | .BR pages_start . 514 | .TP 515 | .BI "\-\-max\-threads=amount" 516 | Amount of threads that can be used to scrape youtube search, playlists, and channels. 517 | (this does not apply to the subscription scraper). 518 | .br 519 | This can also be set in the config file with 520 | .BR max_thread_count . 521 | .TP 522 | .BI "\-\-single\-threaded" 523 | Set the max_thread_count to 1, this has the same effect as making everything single threaded. 524 | (this does not apply to the subscription scraper). 525 | .br 526 | This can also bet set in the config file with 527 | .BR max_thread_count=1 . 528 | .TP 529 | .BI "\-\-odysee\-video\-count=amount" 530 | Amount of videos to scrape on odysee. 531 | This can also be set in the config file with 532 | .BR odysee_video_search_count . 533 | .TP 534 | .BR "\-\-nsfw" 535 | Whether or not to search for nsfw videos. 536 | .br 537 | Only works with odysee/O 538 | This can also be set in the config file with 539 | .BR nsfw . 540 | .TP 541 | .BI "\-\-sort\-by=sort" 542 | Works with youtube/Y and odysee/O. 543 | .br 544 | To use a different sort for each scrape, use comma (,) to separate the sorts. 545 | .br 546 | As apposed to \-\-sort, this happens during the search, not after. 547 | Results should sort by: 548 | .RS 549 | .TP 550 | .IR relevance 551 | .TP 552 | .IR rating " (youtube only)" 553 | .TP 554 | .IR upload_date 555 | .TP 556 | .IR oldest_first " (odysee only)" 557 | .TP 558 | .IR view_count " (youtube only)" 559 | .RE 560 | .TP 561 | .BI "\-\-upload\-date=time\-frame" 562 | Works with youtube/Y and odysee/O 563 | .br 564 | To use a different sort for each scrape, use comma (,) to separate the dates. 565 | .br 566 | Search for videos within the last: 567 | .RS 568 | .TP 569 | .IR hour 570 | .TP 571 | .IR today 572 | .TP 573 | .IR week 574 | .TP 575 | .IR month 576 | .TP 577 | .IR year 578 | .RE 579 | .TP 580 | .BI "\-\-video\-duration=duration" 581 | Whether or not to search for long or short videos. 582 | Possible options: 583 | .RS 584 | .TP 585 | .IR short 586 | .TP 587 | .IR long 588 | .RE 589 | .TP 590 | .BI "\-\-type=type" 591 | The type of results to get. 592 | .RS 593 | .TP 594 | .IR video 595 | .TP 596 | .IR playlist 597 | .TP 598 | .IR channel 599 | .TP 600 | .IR all 601 | .RE 602 | .TP 603 | .BI \-\-thumbnail\-quality= quality 604 | Select the quality of the thumbnails. 605 | Available options: 606 | .RS 607 | .TP 608 | .IR maxres 609 | .TP 610 | .IR maxresdefault 611 | .TP 612 | .IR sddefault 613 | .TP 614 | .IR high " (default)" 615 | .TP 616 | .IR medium 617 | .TP 618 | .IR default 619 | .TP 620 | .IR start 621 | The first frame of the video (low quality) 622 | .TP 623 | .IR middle 624 | The middle frame of the video (low quality) 625 | .TP 626 | .IR end 627 | The end frame of the video (low quality) 628 | .RE 629 | .TP 630 | .BI "\-\-features=features" 631 | The features to have on a video (comma separated). 632 | .RS 633 | .TP 634 | .IR hd 635 | .TP 636 | .IR subtitles 637 | .TP 638 | .IR creative_commons 639 | .TP 640 | .IR 3d 641 | .TP 642 | .IR live 643 | .TP 644 | .IR 4k 645 | .TP 646 | .IR 360 647 | .TP 648 | .IR location 649 | .TP 650 | .IR hdr 651 | .RE 652 | .TP 653 | .BI "\-\-region" 654 | The region (country code) to search. 655 | .br 656 | Supported by the scrapes youtube/Y and youtube-trending/T 657 | .RE 658 | 659 | .PP 660 | Miscellaneous 661 | .RS 662 | .TP 663 | .BI "\-\-ii=instance", "\-\-invidious\-instance=instance" 664 | Use a different invidious instance. 665 | .TP 666 | .BI "\-\-rii", "\-\-refresh\-inv-instance" 667 | If this flag is provided, refresh instance cache with healthy instances using Invidious API 668 | .TP 669 | .BI "\-\-available\-inv\-instances" 670 | Prints the invidious instances that may be used and exits. 671 | .TP 672 | .BI "\-\-channel\-link=link" 673 | Converts channel links from 'https://youtube.com/c/name' to 'https://youtube.com/channel/id' 674 | .TP 675 | .BR \-q 676 | Use search history instead of a search. 677 | This can also be set in the config file with 678 | .BR search_source=hist . 679 | .TP 680 | .BR \-\-search\-source 681 | The source to use for the search query. Valid values include: 682 | .RS 683 | .TP 684 | .RB args 685 | Use commandline arguments as the search (default) 686 | .TP 687 | .RB prompt 688 | Ask for a search via a prompt 689 | .TP 690 | .RB hist 691 | Use search history. 692 | .TP 693 | .RB next 694 | Used internally to use the next search in the list when \fBmulti_search\fR is enabled. 695 | .TP 696 | .RB fn-args 697 | Used internally to use the function arguments passed to the function as the source. 698 | .TP 699 | .RB 700 | A custom search source may be used if a function called get_search_from_ exists. 701 | The function must set the _search variable to a search. 702 | .RE 703 | .TP 704 | .BR \-x ", " \-\-history\-clear= 705 | Clear search and watch history (if \-x or \-\-history\-clear is used) 706 | .br 707 | To specify either search or watch history use \-\-history\-clear= 708 | .TP 709 | .BR \-\-keep\-cache 710 | Whether or not to keep cache after 711 | .I ytfzf 712 | exists. 713 | This can also be set in the config file with 714 | .BR keep_cache . 715 | .TP 716 | .BI \-\-ytdl\-opts= option 717 | Pass command\-line options to youtube\-dl when downloading. 718 | .EX 719 | .RB "example: " \-\-ytdl\-opts= "\fI\"\-o ~/Videos/%(title)s.%(ext)s\"" 720 | .EE 721 | .TP 722 | .BI \-\-ytdl\-path= path 723 | Specify the path to youtube\-dl or a fork of youtube\-dl for downloading. 724 | .br 725 | This can also be set in the config file with 726 | .BR ytdl_path . 727 | .TP 728 | .BI \-e ", " \-\-ext=extension 729 | Load an extension. 730 | .br 731 | You may also add 732 | .I "load_extension extension" 733 | to your config file. 734 | .TP 735 | .BI \-\-list\-addons 736 | Lists all addons and exits. 737 | .TP 738 | .BI \-\-thumbnail\-log 739 | Sets the file to log thumbnail debug info to. 740 | This can also be set in the config file with 741 | .BR thumbnail_debug_log . 742 | .RE 743 | 744 | .SH CONFIGURATION 745 | The default behaviour of \fBytfzf\fR can be changed by modifying the config file. 746 | See \fBytfzf\fR(5) for more information. 747 | 748 | .SH ADDONS 749 | .PP 750 | There are a few types of addons, 751 | .br 752 | .B interfaces (\-i, \-\-interface) 753 | .br 754 | .B scrapers (\-c, \-\-scrape) 755 | .br 756 | .B sort-names (\-\-sort\-name) 757 | .br 758 | .B thumbnail-viewers (\-T, \-\-thumb\-viewer) 759 | .br 760 | .B url-handlers (\-u, \-\-url_handler) 761 | .br 762 | .B extensions (\-e, \-\-ext) 763 | 764 | .PP 765 | To install an addon, place the addon in 766 | .I $YTFZF_SYSTEM_ADDON_DIR//addon-name 767 | .PP 768 | To use an addon, use one of the options listed above, or add 769 | .br 770 | .B load_interface 771 | .br 772 | .B scrape= 773 | .br 774 | .B load_sort_name 775 | .br 776 | .B load_thumbnail_viewer 777 | .br 778 | .B load_url_handler 779 | .br 780 | .B load_extension 781 | .br 782 | In your configuration file 783 | 784 | 785 | .SH EXIT CODES 786 | .TP 787 | .B 0 788 | Success 789 | .TP 790 | .B 1 791 | General error 792 | .TP 793 | .B 2 794 | Invalid \-\-option, option value, or configuration error. 795 | .TP 796 | .B 3 797 | Missing dependency 798 | .TP 799 | .B 5 800 | Empty search 801 | 802 | .SH ENVIRONMENT 803 | .TP 804 | .B $YTFZF_CONFIG_DIR 805 | The directory to store config files. 806 | The default is 807 | .I "$XDG_CONFIG_HOME/ytfzf (or ~/.config/ytfzf)" 808 | .TP 809 | .B $YTFZF_CONFIG_FILE 810 | The configuration file to use. 811 | The default is 812 | .I $YTFZF_CONFIG_DIR/conf.sh 813 | .TP 814 | .B $YTFZF_SUBSCRIPTIONS_FILE 815 | The subscriptions file to use. 816 | The default is 817 | .I $YTFZF_CONFIG_DIR/subscriptions 818 | .TP 819 | .B $YTFZF_SCRAPELIST_FILE 820 | The scrapelist file to use. 821 | The default is 822 | .I $YTFZF_CONFIG_DIR/scrapelist 823 | .TP 824 | .B $YTFZF_THUMBNAIL_VIEWERS_DIR 825 | The directory to keep additional thumbnail viewers. 826 | The default is 827 | .I $YTFZF_CONFIG_DIR/thumbnail-viewers 828 | .TP 829 | .B $YTFZF_CUSTOM_SCRAPERS_DIR 830 | The directory to store custom scraper scripts in 831 | The default is 832 | .I $YTFZF_CONFIG_DIR/scrapers 833 | .TP 834 | .B $YTFZF_CUSTOM_INTERFACES_DIR 835 | The directory to store custom interface scripts in 836 | the default is 837 | .I $YTFZF_CONFIG_DIR/interfaces 838 | .TP 839 | .B $YTFZF_SORT_NAMES_DIR 840 | The directory to store custom sort-name scripts in 841 | the default is 842 | .I $YTFZF_CONFIG_DIR/sort-names 843 | .TP 844 | .B $YTFZF_URL_HANDLERS_DIR 845 | The directory to store custom url handlers in 846 | the default is 847 | .I $YTFZF_CONFIG_DIR/url-handlers 848 | .TP 849 | .B $YTFZF_CUSTOM_THUMBNAILS_DIR 850 | The directory to store custom thumbnails 851 | the default is 852 | .I $YTFZF_CONFIG_DIR/thumbnails 853 | .TP 854 | .B $YTFZF_EXTENSIONS_DIR 855 | The directory to store extensions 856 | the default is 857 | .I $YTFZF_CONFIG_DIR/extensions 858 | .TP 859 | .B $YTFZF_SYSTEM_ADDON_DIR 860 | The directory to store system installed addons. 861 | The default may vary depending on how you installed ytfzf. 862 | .TP 863 | .B $YTFZF_CHECK_VARS_EXISTS 864 | Whether or not to check if variables are set in the environment before setting default values. 865 | The default is 866 | .I 1 867 | .TP 868 | .B $YTFZF_TEMP_DIR 869 | The temporary directory 870 | The default is is 871 | .I $TMPDIR/ytfzf-$user-id. 872 | .br 873 | If $TMPDIR is not defined it will use /tmp 874 | 875 | .SH FILES 876 | .TP 877 | .I ~/.config/ytfzf/conf.sh 878 | The configuration file. If submenu-conf.sh does not exist, this will also be used as the config in submenus 879 | .TP 880 | .I ~/.config/ytfzf/submenu-conf.sh 881 | The submenu configuration file 882 | .TP 883 | .I ~/.config/ytfzf/subscriptions 884 | The subscriptions file. 885 | 886 | .SH CACHE 887 | .PP 888 | Each instance of ytfzf has its own directory in $YTFZF_TEMP_DIR, if $keep_cache is enabled, it uses $cache_dir instead. 889 | .br 890 | The structure of each instance folder looks like this: (<> represents a placeholder, ? means optional) 891 | .br 892 | If an addon scraper is used, it may use undocumented files. 893 | .RS 894 | .EX 895 | $cache_dir 896 | | \-\- watch_hist 897 | | \-\- \- 898 | | | \-\- created-at 899 | | | \-\- searches.list 900 | | | \-\- post-scrape 901 | | | \-\- -? 902 | | | \-\- thumbnails? 903 | | | \-\- environment 904 | | | \-\- tmp 905 | | | | \-\- curl_config 906 | | | | \-\- .html 907 | | | | \-\- .json 908 | | | | \-\- .json.final? 909 | | | | \-\- menu_keypress 910 | | | | \-\- all-ids.list 911 | | | | \-\- downloaded-ids.list 912 | | | \-\- ids 913 | | | \-\- videos_json 914 | .EE 915 | .RE 916 | .PP 917 | An explanation of each directory/file: 918 | .RS 919 | .TP 920 | .IR created-at 921 | Contains the unix timestamp when the folder was created 922 | .TP 923 | .IR searches.list 924 | A list of all searches 925 | .br 926 | If \-\-multi\-search is enabled, each search is separated by a new line 927 | .TP 928 | .IR watch_hist 929 | The watch history file. 930 | .TP 931 | .IR \- 932 | An instance's parent folder. 933 | .br 934 | If no search was given it uses the name "SCRAPE\-\-" instead. 935 | .TP 936 | .IR post-scrape 937 | A folder that contains files relating to the scraping of a selected result. 938 | .TP 939 | .IR \- 940 | Created when a submenu is opened (eg: when a channel/playlist is selected). 941 | .TP 942 | .IR thumbnails 943 | Stores the thumbnails for the instance (only with \-t). 944 | .TP 945 | .IR environment 946 | Every variable that is set and it's value, in that instance of ytfzf 947 | .TP 948 | .IR tmp 949 | Stores less important temporarily used files. 950 | .TP 951 | .IR curl_config 952 | The configuration file for curl for downloading thumbnails (only with \-t). 953 | .TP 954 | .IR .html 955 | For scrapers that need to scrape websites, this is the output of curl. 956 | .TP 957 | .IR .json 958 | The json scraped from a website. 959 | .TP 960 | .IR .json.final 961 | The final json scraped from a website. (Is used when multiple threads are used for scraping) 962 | .TP 963 | .IR menu_keypress 964 | The key pressed in fzf. 965 | .TP 966 | .IR all-ids.json 967 | File that contains all scraped ids. Mainly to compare against downloaded-ids.json 968 | .TP 969 | .IR downloaded-ids.json 970 | File that contains which thumbnails have been downloaded 971 | .TP 972 | .IR ids 973 | The file that stores the id of each selected video. 974 | .TP 975 | .IR videos_json 976 | The file that stores a json of all videos displayed in fzf. 977 | .br 978 | This file is very helpful for making playlists as it is in the same format. 979 | 980 | .SH AUTHOR 981 | Originally written by pystardust. 982 | .IR < https://github.com/pystardust > 983 | 984 | .SH BUGS 985 | Report bugs on github 986 | .IR < https://github.com/pystardust/ytfzf/issues > 987 | 988 | .SH SEE ALSO 989 | .BR ytfzf (5) 990 | .BR youtube\-dl (1), 991 | .BR mpv (1) 992 | .BR fzf (1) 993 | .BR dmenu (1) 994 | 995 | .SH COPYRIGHT 996 | .PP 997 | \fBytfzf\fR is free software: 998 | you can redistribute it and/or modify it under the terms of the 999 | \fIGNU General Public License version 3\fR as published by the Free Software Foundation. 1000 | .PP 1001 | \fBytfzf\fR is distributed in the hope that it will be useful but WITHOUT ANY WARRANTY; 1002 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 1003 | See the GNU General Public License for more details. 1004 | .PP 1005 | You should have received a copy of the GNU General Public License along with \fBytfzf\fR. 1006 | If not, see 1007 | .IR < https://www.gnu.org/licenses/ >. 1008 | -------------------------------------------------------------------------------- /docs/man/ytfzf.5: -------------------------------------------------------------------------------- 1 | .TH YTFZF 5 "2021 September" "ytfzf 2.0" 2 | 3 | .SH NAME 4 | ytfzf \- the configuration file for \fBytfzf\fR. 5 | 6 | .SH SYNOPSIS 7 | .I ~/.config/ytfzf/conf.sh 8 | 9 | .SH DESCRIPTION 10 | .PP 11 | The configuration file is a \fI.sh\fR file and can be used as such. 12 | The file can be completely empty and \fBytfzf\fR will still work with the default settings. 13 | The options should be set as environment variables and functions. 14 | 15 | .SH CONFIGURATION FILES 16 | .PP 17 | Configuration files are stored in 18 | .IR "$XDG_CONFIG_HOME/ytfzf (or $HOME/.config/ytfzf)" . 19 | 20 | .SS conf.sh 21 | .PP 22 | A shell script that gets sourced into ytfzf when ytfzf is run. 23 | 24 | .SS subscriptions 25 | .PP 26 | A file that is a list of links to youtube channel's video page, such as: 27 | .RS 28 | .EX 29 | .I https://www.youtube.com/channel/UCtMVHI3AJD4Qk4hcbZnI9ZQ 30 | .EE 31 | .RE 32 | .PP 33 | Each link should be separated by a new line. 34 | .PP 35 | If a channel appears similar to "https://www.youtube.com/c/SomeOrdinaryGamers", run 36 | .br 37 | ytfzf --channel-link="https://www.youtube.com/c/SomeOrdinaryGamers" 38 | .br 39 | Before adding it to your subscriptions file 40 | .PP 41 | A comment can be created by having '#comment' on its own line or after a link. 42 | For example: 43 | .RS 44 | .EX 45 | .I link-to-channel 46 | #comment 47 | .IR link-to-channel " #best channel" 48 | .EE 49 | .RE 50 | 51 | .SS scrapers 52 | .PP 53 | A folder that contains executable files that scrape websites 54 | See CUSTOM SCRAPERS for more information 55 | .RE 56 | 57 | 58 | .SH CONFIGURATION OPTIONS 59 | 60 | .SS VARIABLES 61 | 62 | .PP 63 | conf.sh is used mainly for setting variables for \fBytfzf\fR. 64 | If a variable has no default, that means it is set to an empty string. 65 | .br 66 | To set a variable in conf.sh be sure to use 67 | .br 68 | variable_name="value" 69 | .br 70 | leave out the $ at the start of the variable name. 71 | 72 | .PP 73 | Files and directories 74 | .RS 75 | 76 | .TP 77 | .RB $ cache_dir 78 | The directory to store cache files in. 79 | .br 80 | It is highly recommended to only change this if $__is_submenu is 0, or funky things could happen 81 | .br 82 | .IR default: " $XDG_CACHE_HOME/ytfzf (or $HOME/.cache/ytfzf)" 83 | 84 | .TP 85 | .RB $ hist_file 86 | The file to write watch history to if $enable_hist is 1 87 | .br 88 | It is highly recommended to only change this if $__is_submenu is 0, or funky things could happen 89 | .br 90 | .IR default: " $cache_dir/watch_hist" 91 | 92 | .TP 93 | .RB $ search_hist_file 94 | The file to write search history to if $enable_search_hist is 1 95 | .br 96 | .IR default: " $cache_dir/search_hist" 97 | 98 | .RE 99 | 100 | .PP 101 | How to play the selected videos. 102 | 103 | .RS 104 | 105 | .TP 106 | .RB $ is_detach 107 | Whether or not to detach the video player from the terminal. 108 | .br 109 | .IR default: " 0" 110 | 111 | .TP 112 | .RB $ is_audio_only 113 | Whether or not to only play audio. 114 | .br 115 | .IR default: " 0" 116 | 117 | .TP 118 | .RB $ url_handler 119 | The function/programs to handle the selected videos 120 | .br 121 | It is recommended to not set this variable directly, but rather to use the command load_url_handler 122 | .br 123 | See URL handlers for a list of built-in url handlers 124 | .IR default: " multimedia_player" 125 | 126 | .TP 127 | .RB $ yt_video_link_domain 128 | The domain to play youtube videos from (does not apply to odysee and peertube or youtube playlists) 129 | .br 130 | If left blank, the value will be whatever invidious instance ytfzf chose 131 | .br 132 | .IR default: " https://www.youtube.com" 133 | 134 | .TP 135 | .RB $ info_to_print 136 | The information to print instead of playing a video. 137 | The available options for this variable are: 138 | .RS 139 | .TP 140 | .IR L " | " l " | " link 141 | Print the URL of the selected videos. 142 | .TP 143 | .IR VJ " | " vj " | " video\-json 144 | Prints the json of the selected videos. 145 | .TP 146 | .IR J " | " j " | " json 147 | Prints the json of all the videos in the search results. 148 | .TP 149 | .IR F " | " f " | " format 150 | Prints the video format being used. 151 | .TP 152 | .IR R " | " r " | " raw 153 | Prints the data of the selected videos, as appears in the menu. 154 | .RE 155 | 156 | .TP 157 | .RB $ info_wait_action 158 | The action to do when $info_wait is 1. 159 | .br 160 | Valid actions: 161 | .RS 162 | .TP 163 | .IR q 164 | quit (will go back to menu, if $is_loop is 1). 165 | .TP 166 | .IR Q 167 | quit regardless if loop is enabled or not. 168 | .TP 169 | .IR m 170 | return to menu. 171 | .br 172 | .IR default: " q" 173 | .RE 174 | 175 | .TP 176 | .BR $ info_wait 177 | Whether or not to wait for input after printing info. 178 | .br 179 | .IR default: " 0" 180 | 181 | .TP 182 | .RB $ video_pref 183 | The video preference to use for youtube-dl in mpv. 184 | .br 185 | .IR default: " bestvideo" 186 | 187 | .TP 188 | .RB $ audio_pref 189 | The audio preference to use for youtube-dl in mpv. 190 | .br 191 | .IR default: " bestaudio" 192 | 193 | .TP 194 | .RB $ ytdl_pref 195 | The preference to use for youtube-dl in mpv. 196 | .br 197 | .IR default: " $video_pref+$audio_pref/best/$video_pref/$audio_pref" 198 | 199 | .TP 200 | .RB $ url_handler_opts 201 | Opts to pass to the url handler, by default this will pass extra opts to mpv. 202 | This can also be set in the config file with 203 | .BR url_handler_opts 204 | .RE 205 | 206 | .RE 207 | 208 | .PP 209 | Menu options 210 | 211 | .RS 212 | 213 | .TP 214 | .RB $ interface 215 | The interface/menu to use. 216 | .br 217 | It is recommended to not use this variable directly, instead use 218 | .B load_interface "interface-name" 219 | .br 220 | Valid options. 221 | .RS 222 | .TP 223 | .IR ext 224 | same as \-D 225 | .TP 226 | .IR scripting 227 | is applied when \-a, \-r, or \-A is used 228 | .TP 229 | .IR "''" 230 | default 231 | .RE 232 | 233 | .TP 234 | .RB $ external_menu_len 235 | The amount of cols in interface_ext, (\-D) 236 | .br 237 | .IR default: " 210" 238 | 239 | .TP 240 | .RB $ fzf_preview_side 241 | The side to show the preview in fzf. 242 | .br 243 | Valid options: 244 | .RS 245 | .TP 246 | .IR left 247 | .TP 248 | .IR right 249 | .TP 250 | .IR up 251 | .TP 252 | .IR down 253 | .TP 254 | .IR default: " left" 255 | .RE 256 | 257 | .TP 258 | .RB $ preview_window_width 259 | The amount of space to use for the fzf preview 260 | .br 261 | .IR default: " 50%" 262 | 263 | .TP 264 | .RB $ thumbnail_viewer 265 | The program to display images for thumbnail previews 266 | .br 267 | It is recommended to not use this variable directly, instead use 268 | .B load_thumbnail_viewer "viewer" 269 | .br 270 | Valid options: 271 | .RS 272 | .TP 273 | .IR sixel 274 | .TP 275 | .IR iterm2 276 | .TP 277 | .IR kitty 278 | .TP 279 | .IR sway 280 | .TP 281 | .IR wayland 282 | .TP 283 | .IR ueberzug 284 | .TP 285 | .IR chafa 286 | .TP 287 | .IR chafa-16 288 | Uses chafa with 16 colors 289 | .TP 290 | .IR chafa-tty 291 | Uses chafa with 4 colors 292 | .TP 293 | .IR catimg 294 | .TP 295 | .IR catimg-256 296 | Uses catimg with 256 colors 297 | .TP 298 | .IR mpv 299 | Uses the mpv player to display the images 300 | .br 301 | Works well with tiling window managers. 302 | .TP 303 | .IR imv 304 | Similar to mpv, but is a dedicated image viewer 305 | .TP 306 | .IR swayimg 307 | Only works on the sway wayland compositor 308 | .TP 309 | .IR swayimg-hyprland 310 | Only works on the hyprland compositor. 311 | Uses swayimg to render images. 312 | .TP 313 | .IR default: " ueberzug" 314 | .RE 315 | 316 | .TP 317 | .RB $ show_formats 318 | Whether or not to bring up the format selection menu. 319 | .br 320 | .IR default: " 0" 321 | 322 | .TP 323 | .RB $ format_selection_screen 324 | The format that selection screen will use. 325 | Types: 326 | .RS 327 | .IR simple 328 | .IR normal 329 | .br 330 | .IR default: " simple" 331 | .RE 332 | 333 | .TP 334 | .RB $ format_selection_sort 335 | The \-\-format\-sort to use in ytdl. 336 | .br 337 | .IR default: " height" 338 | 339 | .TP 340 | .RB $ enable_submenus 341 | Whether or not to enable submenus, 342 | .br 343 | A submenu is a menu that appears after a playlist or channel is selected. 344 | (Currently only supported with youtube/invidious scraper) 345 | .IR default: " 1" 346 | 347 | .TP 348 | .RB $ enable_actions 349 | Whether or not to enable actions such as submenus, and the back button. 350 | .br 351 | .IR default: " 1" 352 | 353 | .TP 354 | .BR $ keep_vars 355 | Whether or not options passed into ytfzf also get passed into submenus 356 | 357 | .TP 358 | .RB $ enable_back_button 359 | Whether or not to enable back button in submenus. 360 | .IR default: " 1" 361 | 362 | .TP 363 | .RB $ submenu_opts 364 | Options to use in submenus. 365 | .IR default: "" 366 | 367 | .TP 368 | .RB $ submenu_scraping_opts 369 | .B DEPRECATED "(use submenu_opts instead)" 370 | Does the same thing as $submenu_opts 371 | .IR default: "" 372 | 373 | .TP 374 | .RB $ is_sort 375 | Whether or not to sort scraped videos by date in the menu 376 | .IR default: " 0" 377 | 378 | .TP 379 | .RB $ fancy_subs 380 | Whether or not to have a separator between each subscription 381 | .IR default: " 0" 382 | 383 | .TP 384 | .RB $ fancy_subs_left 385 | The text to display on the left of the channel name when fancy_subs is 1. 386 | .IR default: " -------------" 387 | 388 | .TP 389 | .RB $ fancy_subs_right 390 | The text to display on the right of the channel name when fancy_subs is 1. 391 | .IR default: " $fancy_subs_left" 392 | 393 | .TP 394 | .RB $ show_thumbnails 395 | Whether or not to show thumbnails in fzf. 396 | .br 397 | .IR default: " 0" 398 | 399 | .BR $ async_thumbnails 400 | Whether or not to download thumbnails asynchronously. 401 | .br 402 | Downloading asynchronously will open the menu before all thumbnails are downloaded. 403 | .IR default: " 0" 404 | 405 | .TP 406 | .RB $ skip_thumb_download 407 | Whether or not to skip thumbnail download. 408 | .br 409 | .IR default: " 0" 410 | 411 | .TP 412 | .RB $ thumbnail_quality 413 | Select the quality of the thumbnails. 414 | Currently only supports youtube 415 | (uses invidious api). 416 | .br 417 | This does not work for the \(aq\fB-cS\fR\(aq scraper as it scrapes youtube not invidious 418 | (use \(aq\fBSI\fR\(aq instead). 419 | .br 420 | For lower internet speeds it is recommended to use default. 421 | .br 422 | Available options: 423 | .RS 424 | .TP 425 | .IR maxres 426 | .TP 427 | .IR maxresdefault 428 | .TP 429 | .IR sddefault 430 | .TP 431 | .IR high " (default)" 432 | .TP 433 | .IR medium 434 | .TP 435 | .IR default 436 | .TP 437 | .IR start 438 | The first frame of the video (low quality) 439 | .TP 440 | .IR middle 441 | The middle frame of the video (low quality) 442 | .TP 443 | .IR end 444 | The end frame of the video (low quality) 445 | .RE 446 | .br 447 | 448 | .TP 449 | .RB $notify_playing 450 | Whether or not to send a notification when a video is about to be played. 451 | .br 452 | .IR default: " 0" 453 | 454 | .TP 455 | .RB $ is_loop 456 | Whether or not to show the menu after the selected videos have stopped playing. 457 | .br 458 | .IR default: " 0" 459 | 460 | .TP 461 | .RB $ search_again 462 | Whether or not to make another search after fzf is closed. 463 | .br 464 | .IR default: " 0" 465 | 466 | .TP 467 | .RB $ selection_meta_key 468 | The meta key for shortcuts that select a video. 469 | .br 470 | .IR default: " alt" 471 | 472 | .TP 473 | .RB $ download_shortcut 474 | The shortcut to download the selected videos. 475 | .br 476 | .IR default: " $selection_meta_key-d" 477 | 478 | .TP 479 | .RB $ video_shortcut 480 | The shortcut to watch the selected videos. 481 | .br 482 | .IR default: " $selection_meta_key-v" 483 | 484 | .TP 485 | .RB $ audio_shortcut 486 | The shortcut to listen to the selected videos. 487 | .br 488 | .IR default: " $selection_meta_key-m" 489 | 490 | .TP 491 | .RB $ detach_shortcut 492 | The shortcut to use the detach player. 493 | .br 494 | .IR default: " $selection_meta_key-e" 495 | 496 | .TP 497 | .RB $ print_link_shortcut 498 | The shortcut to use to print the link. 499 | .br 500 | .IR default: " $selection_meta_key-l" 501 | 502 | .TP 503 | .RB $ show_formats_shortcut 504 | The shortcut to show formats before playing the video. 505 | .br 506 | .IR default: " $selection_meta_key-f" 507 | 508 | .TP 509 | .RB $ info_shortcut 510 | The shortcut to get all info about the selected video. 511 | .br 512 | .IR default: " $selection_meta_key-i" 513 | 514 | .TP 515 | .RB $ search_again_shortcut 516 | The shortcut to make another search. 517 | .br 518 | .IR default: " $selection_meta_key-s" 519 | 520 | .TP 521 | .RB $ action_meta_key 522 | The meta key for shorcuts that do something 523 | .br 524 | .IR default: " ctrl" 525 | 526 | .TP 527 | .RB next_page_action_shortcut 528 | The shortcut to scrape the next page. 529 | .br 530 | .IR default: " $action_meta_key-p" 531 | 532 | .TP 533 | .RB $ shortcut_binds 534 | The keys to listen for in fzf. 535 | .br 536 | .IR default: " Enter,double-click,$download_shortcut, 537 | $video_shortcut,$detach_shortcut,$print_link_shortcut,$show_formats_shortcut, 538 | $info_shortcut,$search_again_shortcut,$custom_shortcut_binds" 539 | 540 | .TP 541 | .RB $ custom_shortcut_binds 542 | The custom shortcut keys. Automatically appended to $shortcut_binds 543 | .br 544 | If $shortcut_binds is set manually, this must also manually be appended. 545 | 546 | .RE 547 | 548 | .PP 549 | Auto selecting 550 | 551 | .RS 552 | 553 | .TP 554 | .RB $ is_auto_select 555 | Whether or not to auto select the first \-n videos. (only works if $interface=scripting) 556 | .br 557 | .IR default: " 0" 558 | 559 | .TP 560 | .RB $ is_random_select 561 | Whether or not to randomly select \-n videos. (only works if $interface=scripting) 562 | .br 563 | .IR default: " 0" 564 | 565 | .TP 566 | .RB $ is_specific_select 567 | Whether or not to select a specific video (use $ scripting_video_count to specify which) (only works if $interface=scripting) 568 | .br 569 | .IR default: " 0" 570 | 571 | .TP 572 | .RB $ scripting_video_count 573 | The amount of videos to get with \-a or \-r. 574 | .br 575 | .IR default: " 1" 576 | 577 | .RE 578 | 579 | .PP 580 | Scrapers 581 | 582 | .RS 583 | 584 | .TP 585 | .RB $ scrape 586 | The website to scrape by default. 587 | The currently supported options are: 588 | .RS 589 | .TP 590 | .IR youtube , 591 | .TP 592 | .IR youtube\-trending , 593 | .TP 594 | .IR youtube\-subscriptions , 595 | .TP 596 | .IR peertube , 597 | .TP 598 | .IR odysee / lbry . 599 | .TP 600 | .IR youtube-playlist , 601 | .TP 602 | .IR youtube-channel , 603 | .TP 604 | .IR invidious-channel , 605 | .TP 606 | .IR video-recommended , 607 | .TP 608 | .IR playlist/json-file , 609 | .PP 610 | The search will be a path to a json file laid out as described in VIDEO JSON FORMAT 611 | .TP 612 | .IR history , 613 | .TP 614 | .IR url/U , 615 | .TP 616 | .IR u , 617 | .TP 618 | .IR M / multi , 619 | .TP 620 | .IR comments 621 | .br 622 | .IR default: " youtube" 623 | .RE 624 | 625 | .TP 626 | .RB $ multi_search 627 | Whether or not to enable multi search. 628 | .IR default: " 0" 629 | 630 | .TP 631 | .RB $ search_sort_by 632 | The attribute to sort by when searching. 633 | .RS 634 | .TP 635 | .IR relevance " (default)" 636 | .TP 637 | .IR rating " (youtube only)" 638 | .TP 639 | .IR upload_date 640 | .TP 641 | .IR oldest_first " (odysee only)" 642 | .TP 643 | .IR view_count " (youtube only)" 644 | .RE 645 | 646 | .TP 647 | .RB $ search_upload_date 648 | Search for videos within the last: 649 | .RS 650 | .TP 651 | .IR hour 652 | .TP 653 | .IR today 654 | .TP 655 | .IR week 656 | .TP 657 | .IR month 658 | .TP 659 | .IR year 660 | .RE 661 | 662 | .TP 663 | .RB $ search_video_duration 664 | Whether or not to search for long or short videos. 665 | Possible options: 666 | .RS 667 | .TP 668 | .IR short 669 | .TP 670 | .IR long 671 | .RE 672 | 673 | .TP 674 | .RB $ search_result_type 675 | The type of results to get. 676 | .RS 677 | .TP 678 | .IR video " (default)" 679 | .TP 680 | .IR playlist 681 | .TP 682 | .IR channel 683 | .TP 684 | .IR all " (may not work on some instances)" 685 | .RE 686 | 687 | .TP 688 | .RB $ nsfw 689 | Whether or not to search for nsfw videos in odysee/O. 690 | .br 691 | .IR default: " false" 692 | 693 | .TP 694 | .RB $ search_result_features 695 | The features to have on a video (comma separated). 696 | .RS 697 | .TP 698 | .IR hd 699 | .TP 700 | .IR subtitles 701 | .TP 702 | .IR creative_commons 703 | .TP 704 | .IR 3d 705 | .TP 706 | .IR live 707 | .TP 708 | .IR 4k 709 | .TP 710 | .IR 360 711 | .TP 712 | .IR location 713 | .TP 714 | .IR hdr 715 | .RE 716 | 717 | .TP 718 | .RB $ search_region 719 | The region (country code) to search. 720 | .IR default: " US" 721 | 722 | .TP 723 | .RB $ invidious_instance 724 | The instance of invidious to use. 725 | .br 726 | .IR default: " https://vid.puffyan.us" 727 | 728 | .TP 729 | .RB $ pages_to_scrape 730 | The amount of pages to scrape on youtube/invidious. 731 | .br 732 | .IR default: " 1" 733 | 734 | .TP 735 | .RB $ pages_start 736 | The starting page to scrape. 737 | .br 738 | .IR default: " 1" 739 | 740 | .TP 741 | .RB $ max_thread_count 742 | The amount of threads that can be used while scraping youtube search, playlists, and channels. 743 | (this does not apply to the subscription scraper) 744 | .br 745 | .IR default: " 20" 746 | 747 | .TP 748 | .RB $ odysee_video_search_count 749 | The amount of videos to scrape on odysee. 750 | .br 751 | .IR default: " 30" 752 | 753 | .TP 754 | .RB $ sub_link_count 755 | The amount of videos to scrape per channel when getting subscriptions. 756 | .br 757 | .IR default: " 2" 758 | 759 | .RE 760 | 761 | .PP 762 | Misc 763 | 764 | .RS 765 | 766 | .TP 767 | .RB $ gap_space 768 | The amount of space after the title in the thumbnail viewer menu. 769 | .IR default: "' '" 770 | 771 | .TP 772 | .RB $ scrape_search_exclude 773 | The scrapers to not ask for a search query. 774 | .br 775 | Be sure to have a space at the end and beginning of the string. 776 | .br 777 | .IR default: " youtube-subscriptions S SI T youtube-trending H history " 778 | 779 | .TP 780 | .RB $ custom_scrape_search_exclude 781 | Extra scrapers to not ask for a search query. 782 | .br 783 | This will automatically be appended to $ scrape_search_exclude. 784 | .br 785 | In addition, you do not need spaces at the start, and end, only between scrapers. 786 | .IR default: "" 787 | 788 | .TP 789 | .RB $ gap_space 790 | A number of spaces equal to half the width of your terminal 791 | .br 792 | .IR default: " 115 spaces" 793 | 794 | .TP 795 | .RB $ enable_hist 796 | Whether or not to keep track of history 797 | .br 798 | .IR default: " 1" 799 | 800 | .TP 801 | .RB $ enable_search_hist 802 | Whether or not to keep track of search history 803 | .br 804 | .IR default: " 1" 805 | 806 | .TP 807 | .RB $ search_source 808 | How to get the search query. The builtin values for this are: 809 | .RS 810 | .TP 811 | .RB args 812 | Use commandline arguments as the search (default) 813 | .TP 814 | .RB prompt 815 | Ask for a search via a prompt 816 | .TP 817 | .RB hist 818 | Use search history. 819 | .TP 820 | .RB next 821 | Used internally to use the next search in the list when \fBmulti_search\fR is enabled. 822 | .TP 823 | .RB fn-args 824 | Used internally to use the function arguments passed to the function as the source. 825 | .RE 826 | 827 | .TP 828 | .RB $ log_level 829 | How much debug information to log. 830 | .RS 831 | .TP 832 | .IR 2 833 | Log everything 834 | .TP 835 | .IR 1 836 | Log only warnings and errors 837 | .TP 838 | .IR 0 839 | Log only errors 840 | .TP 841 | .IR default: " 2" 842 | .RE 843 | 844 | .TP 845 | .RB $ thumbnail_debug_log 846 | The log file for thumbnail debug information. 847 | .IR default: "/dev/null" 848 | 849 | .TP 850 | .RB $ useragent 851 | The useragent to use when scraping websites. 852 | .br 853 | .IR default: " \(dqMozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.152 Safari/537.36\(dq" 854 | 855 | .TP 856 | .RB $ ytdl_opts 857 | The command\-line options to pass to youtube\-dl when downloading. 858 | 859 | .TP 860 | .RB $ ytdl_path 861 | Path to youtube\-dl or a fork of youtube\-dl for downloading. 862 | .br 863 | If 864 | .I yt-dlp 865 | is installed that will be preferred over 866 | .I youtube-dl 867 | .br 868 | .IR default: " youtube\-dl" 869 | 870 | .RE 871 | 872 | .PP 873 | Option Parsing 874 | 875 | .RS 876 | 877 | .TP 878 | .RB $ long_opt_char 879 | The char to use for long opts. 880 | .br 881 | .IR default: " \-" 882 | 883 | .TP 884 | .RB $ optstring 885 | The string of options to use for the \fBgetopts\fR function. 886 | .br 887 | It is highly unrecommended to change this variable, unless you know what you are doing. 888 | .br 889 | .IR default: " ac:de:fhi:lmn:qrstu:xADHI:LS:T:W:\fI$long_opt_char\fR:" 890 | 891 | .TP 892 | .RB $ YTFZF_CHECK_VARS_EXISTS 893 | Whether or not to check if variables in the environment already exist when setting default options. 894 | .br 895 | This option can not be set in the config, it must be set in your startup shell with export, or before running ytfzf such as: 896 | .I YTFZF_CHECK_VARS_EXISTS=0 ytfzf ... 897 | .br 898 | .IR default: 1 899 | 900 | .RE 901 | 902 | .PP 903 | State 904 | .br 905 | State values are \fBNOT\fR meant to be modified by the user. 906 | .RS 907 | 908 | .TP 909 | .RB $ __is_submenu 910 | Whether or not the script is in a submenu. 911 | 912 | .TP 913 | .RB $ __is_fzf_preview 914 | Whether or not the script is running to display an fzf preview 915 | 916 | .TP 917 | .RB __scrape_count 918 | The current scrape count starting at 1. 919 | 920 | 921 | .SS FUNCTIONS 922 | .PP 923 | Sometimes a variable is not good enough, instead functions should be defined. 924 | To find the default value of these, check the source code by searching for 925 | .IR "function_exists \(dq\(dq" . 926 | 927 | .PP 928 | Menu related functions 929 | .RS 930 | 931 | .TP 932 | .BR external_menu () 933 | When $\fBinterface\fR is \fIext_menu\fR, call this function instead of fzf. 934 | .br 935 | This function takes 1 argument, a prompt string. 936 | 937 | .TP 938 | .BR get_sort_by () 939 | This function is called to get the value to sort by when $\fBis_sort\fR is \fI1\fR. 940 | .br 941 | This function takes in a line in the form of 942 | .IR "\(dqtitle |channel |duration |views |date |id\(dq" . 943 | 944 | .TP 945 | .BR data_sort_fn () 946 | This function sorts the data that is being piped into it. 947 | .br 948 | This function takes no arguments, all data is piped into it. 949 | 950 | .TP 951 | .BR custom_info_wait_action_ () 952 | This function is called if an unknown $info_wait_action is given or read. 953 | .br 954 | should be replaced with the text wanted from $info_wait_action, eg: \fIcustom_info_wait_e\fR. 955 | .br 956 | This function takes no arguments. 957 | 958 | .TP 959 | .BR video_info_text () 960 | This function prints the text for the selection menu. 961 | .br 962 | Must end with a new line, 963 | .br 964 | The url must be the last thing printed. 965 | .br 966 | This function takes no arguments, the relevant variables are listed here: 967 | .RS 968 | .EX 969 | .I title 970 | .I channel 971 | .I duration 972 | .I views 973 | .I date 974 | .I url 975 | .EE 976 | It is recommended to check the script to see how each thing is printed. 977 | .RE 978 | 979 | .TP 980 | .BR thumbnail_video_info_text () 981 | This function prints text in the preview area of fzf when thumbnails are enabled. 982 | .br 983 | Everything can be printed however you like. 984 | .br 985 | This function takes no arguments, the relevant variables are listed here: 986 | .RS 987 | .EX 988 | .I title 989 | .I channel 990 | .I duration 991 | .I views 992 | .I date 993 | .I url 994 | .EE 995 | .RE 996 | 997 | .TP 998 | .BR thumbnail_video_info_text_ () 999 | This function is the same as thumbnail_video_info_text() for the scraper specified. 1000 | 1001 | .TP 1002 | .BR on_no_thumbnail () 1003 | This function is run when no thumbnail is found 1004 | 1005 | .TP 1006 | .BR get_ueberzug_positioning_left () 1007 | This function sets the variables, $width, $height, $x, and $y. 1008 | $x, and $y, should represent cols and lines not pixels. 1009 | .br 1010 | These variables will be used to position and size the image in the fzf preview when $fzf_preview_side is left. 1011 | .br 1012 | This function takes 2 arguments: 1013 | .RS 1014 | .EX 1015 | .I max_width 1016 | .I max_height 1017 | .RE 1018 | 1019 | .TP 1020 | .BR get_ueberzug_positioning_right () 1021 | This function sets the variables, $width, $height, $x, and $y. 1022 | $x, and $y, should represent cols and lines not pixels. 1023 | .br 1024 | These variables will be used to position and size the image in the fzf preview when $fzf_preview_side is right. 1025 | .br 1026 | This function takes 2 arguments: 1027 | .RS 1028 | .EX 1029 | .I max_width 1030 | .I max_height 1031 | .RE 1032 | 1033 | .TP 1034 | .BR get_ueberzug_positioning_up () 1035 | This function sets the variables, $width, $height, $x, and $y. 1036 | $x, and $y, should represent cols and lines not pixels. 1037 | .br 1038 | These variables will be used to position and size the image in the fzf preview when $fzf_preview_side is up. 1039 | .br 1040 | This function takes 2 arguments: 1041 | .RS 1042 | .EX 1043 | .I max_width 1044 | .I max_height 1045 | .RE 1046 | 1047 | .TP 1048 | .BR get_ueberzug_positioning_down () 1049 | This function sets the variables, $width, $height, $x, and $y. 1050 | $x, and $y, should represent cols and lines not pixels. 1051 | .br 1052 | These variables will be used to position and size the image in the fzf preview when $fzf_preview_side is down. 1053 | .br 1054 | This function takes 2 arguments: 1055 | .RS 1056 | .EX 1057 | .I max_width 1058 | .I max_height 1059 | .RE 1060 | 1061 | .TP 1062 | .BR get_swayimg_positioning_left () 1063 | This function sets the variables, $x, $y, $img_w, and $img_h. 1064 | $x, and $y, should represent pixels. 1065 | .br 1066 | These variables will be used to position and size the image in the fzf preview when $fzf_preview_side is left. 1067 | .br 1068 | This function takes 8 arguments: 1069 | .RS 1070 | .EX 1071 | .I img_w 1072 | .I img_h 1073 | .I max_width 1074 | .I max_height 1075 | .I max_height 1076 | .I term_x 1077 | .I term_y 1078 | .I col_px_width 1079 | .I line_px_height 1080 | .RE 1081 | 1082 | .TP 1083 | .BR get_swayimg_positioning_right () 1084 | This function sets the variables, $x, $y, $img_w, and $img_h. 1085 | $x, and $y, should represent pixels. 1086 | .br 1087 | These variables will be used to position and size the image in the fzf preview when $fzf_preview_side is right. 1088 | .br 1089 | This function takes 8 arguments: 1090 | .RS 1091 | .EX 1092 | .I img_w 1093 | .I img_h 1094 | .I max_width 1095 | .I max_height 1096 | .I max_height 1097 | .I term_x 1098 | .I term_y 1099 | .I col_px_width 1100 | .I line_px_height 1101 | .RE 1102 | 1103 | .TP 1104 | .BR get_swayimg_positioning_up () 1105 | This function sets the variables, $x, $y, $img_w, and $img_h. 1106 | $x, and $y, should represent pixels. 1107 | .br 1108 | These variables will be used to position and size the image in the fzf preview when $fzf_preview_side is up. 1109 | .br 1110 | This function takes 8 arguments: 1111 | .RS 1112 | .EX 1113 | .I img_w 1114 | .I img_h 1115 | .I max_width 1116 | .I max_height 1117 | .I max_height 1118 | .I term_x 1119 | .I term_y 1120 | .I col_px_width 1121 | .I line_px_height 1122 | .RE 1123 | 1124 | .TP 1125 | .BR get_swayimg_positioning_down () 1126 | This function sets the variables, $x, $y, $img_w, and $img_h. 1127 | $x, and $y, should represent pixels. 1128 | .br 1129 | These variables will be used to position and size the image in the fzf preview when $fzf_preview_side is down. 1130 | .br 1131 | This function takes 8 arguments: 1132 | .RS 1133 | .EX 1134 | .I img_w 1135 | .I img_h 1136 | .I max_width 1137 | .I max_height 1138 | .I max_height 1139 | .I term_x 1140 | .I term_y 1141 | .I col_px_width 1142 | .I line_px_height 1143 | .RE 1144 | 1145 | .TP 1146 | .BR search_prompt_menu () 1147 | This function asks the user to make a search query, and sets the variable $_search to the query. 1148 | .br 1149 | This function is called if ytfzf is started without a search. (and is using the default interface) 1150 | .br 1151 | This function takes no arguments. 1152 | 1153 | .TP 1154 | .BR search_prompt_ext () 1155 | This function asks the user to make a search query, and sets the variable $_search to the query. 1156 | .br 1157 | This function is called if ytfzf is started without a search. (and is using the \-D flag) 1158 | .br 1159 | This function takes no arguments. 1160 | 1161 | .TP 1162 | .BR quick_menu () 1163 | This function should take user input and echo it back 1164 | .br 1165 | This function is called with -f, and -q. Or any other time a generic menu is needed. (and the default interface is being used) 1166 | .br 1167 | This function takes 1 argument, and takes input from stdin 1168 | .br 1169 | 1: The prompt to use. 1170 | .br 1171 | stdin: the items to choose from (separated by new lines) 1172 | 1173 | .TP 1174 | .BR quick_menu_ext () 1175 | This function should do the same thing as quick_menu() 1176 | .br 1177 | This function is called when quick_menu() would be called, but when \-D is enabled. 1178 | .br 1179 | This function takes 1 argument, and takes input from stdin 1180 | .br 1181 | 1: The prompt to use. 1182 | .br 1183 | stdin: the items to choose from (separated by new lines) 1184 | 1185 | .TP 1186 | .BR info_wait_prompt () 1187 | The prompt to use when \fBinfo_wait\fR is enabled. 1188 | .br 1189 | This function takes no arguments. 1190 | 1191 | .TP 1192 | .BR info_wait_prompt_ext () 1193 | Same as \fBinfo_wait_prompt()\fR when \-D is used. 1194 | .br 1195 | This function takes no arguments. 1196 | 1197 | .TP 1198 | .BR info_wait_prompt_wrapper () 1199 | Call info_wait_prompt_ 1200 | .br 1201 | This function takes no arguments. 1202 | 1203 | .TP 1204 | .BR display_text () 1205 | Print text to standard out. 1206 | .br 1207 | This function takes an unlimited number of arguments to print. 1208 | 1209 | .TP 1210 | .BR display_text_ext () 1211 | Print text to standard out. 1212 | .br 1213 | This function takes an unlimited number of arguments to print. 1214 | .RE 1215 | 1216 | .TP 1217 | .BR display_text_wrapper () 1218 | Call display_text_ 1219 | .br 1220 | This function takes an unlimited number of arguments to print. 1221 | .RE 1222 | 1223 | .PP 1224 | URL handlers 1225 | .RS 1226 | .PP 1227 | A URL handler is a function that handles the urls given, 1228 | .br 1229 | URL handlers should take into account these modifier values, 1230 | .B $video_pref ", " 1231 | .B $is_audio_only ", " 1232 | and 1233 | .B $is_detach 1234 | .PP 1235 | Modifier variables will be piped into a URL handler to allow for URL handlers to be written in any language. 1236 | .br 1237 | They will be piped in the order shown above separated by spaces. 1238 | 1239 | .TP 1240 | .BR multimedia_player () 1241 | The handler that is called by default. 1242 | .br 1243 | This function opens either video_player() or audio_player() depending on whether or not 1244 | .br 1245 | $is_audio_only (\-m) is enabled. 1246 | .br 1247 | This function takes in an unlimited amount of arguments, each of which is a link to a video. 1248 | 1249 | .TP 1250 | .BR video_player () 1251 | Plays the urls with a video player 1252 | .br 1253 | This function takes in an unlimited amount of arguments, each of which is a link to a video. 1254 | 1255 | .TP 1256 | .BR audio_player () 1257 | Plays the urls with an audio player 1258 | .br 1259 | This function takes in an unlimited amount of arguments, each of which is a link to a video. 1260 | 1261 | .TP 1262 | .BR downloader () 1263 | Downloads the urls 1264 | .br 1265 | This function takes in an unlimited amount of arguments, each of which is a link to a video. 1266 | 1267 | .TP 1268 | .BR get_video_format_() 1269 | A custom format selection screen 1270 | .br 1271 | should be the the wanted value of $format_selection_screen 1272 | .br 1273 | This function should set ytdl_pref 1274 | .br 1275 | This function takes all urls as separate arguments. 1276 | 1277 | .TP 1278 | .BR on_open_url_handler_() 1279 | This function is run when \fBopen_url_handler\fR is called, but before the url handler is actually opened. 1280 | .br 1281 | This function takes an unlimited number of arguments, each argument is a url that was opened. 1282 | 1283 | 1284 | .TP 1285 | .BR close_url_handler_ 1286 | should be the name of the url handler with \- replaced with _. 1287 | .br 1288 | A function that happens after the url handler has finished playing. 1289 | .br 1290 | The point of this function is to clean up anything that the url handler did. 1291 | 1292 | .TP 1293 | .BR after_close_url_handler_ 1294 | A function that happens after the url handler has finished playing, and after 1295 | .BR close_url_handler_ 1296 | has happened. 1297 | .br 1298 | If 1299 | .BR is_detach 1300 | is 1301 | .I 1 1302 | this function may be called immediately after 1303 | .BR url_handler 1304 | opens. 1305 | 1306 | .RE 1307 | 1308 | .PP 1309 | Search History 1310 | .RS 1311 | 1312 | .TP 1313 | .BR handle_search_history() 1314 | This function handles appending the search to the given search file. 1315 | .br 1316 | This function takes 2 arguments: 1317 | .RS 1318 | .TP 1319 | .IR 1 1320 | The search to write 1321 | .TP 1322 | .IR 2 1323 | The file to append to. 1324 | .RE 1325 | 1326 | .TP 1327 | .BR parse_search_hist_file() 1328 | This function should parse the search history file, and print out each search separated by new lines. 1329 | .br 1330 | The search history file will be fed through stdin. 1331 | 1332 | .TP 1333 | .BR get_search_from_() 1334 | If search_source is set to this function will be called. 1335 | This function is expected to set the variable \fB_search\fR to a search query. 1336 | .br 1337 | This function takes no arguments. 1338 | 1339 | .TP 1340 | .BR on_search () 1341 | This function gets called each time a website is scraped. 1342 | .br 1343 | This function takes 2 arguments: 1344 | .EX 1345 | .I 1 1346 | .ti +4 1347 | The search query 1348 | .I 2 1349 | .ti +4 1350 | The current scrape 1351 | .EE 1352 | 1353 | .TP 1354 | .BR on_search_ () 1355 | This function gets called each time a website is scraped if the current search matches . 1356 | .br 1357 | This function takes 1 argument. 1358 | .RS 1359 | .TP 1360 | .I 1 1361 | The current scrape 1362 | .RE 1363 | 1364 | .RE 1365 | 1366 | .PP 1367 | Misc 1368 | 1369 | .RS 1370 | 1371 | .TP 1372 | .BR get_requested_info_() 1373 | If is in \fBinfo_to_print\fR this function will be run. 1374 | .br 1375 | This function takes no arguments. 1376 | 1377 | .TP 1378 | .BR handle_playing_notifications() 1379 | This function sends a notification for the videos that are about to be played. 1380 | .br 1381 | This function takes an unknown amount of urls as arguments. 1382 | 1383 | .TP 1384 | .BR post_scrape() 1385 | This function happens after all scraping is complete 1386 | 1387 | .TP 1388 | .BR post_scrape_() 1389 | Same as post_scrape() but for each extension 1390 | 1391 | .TP 1392 | .BR on_opt_parse () 1393 | This function gets called after an option is parsed, and sets variables based the options passed into it. 1394 | A non 0 exit code will override the default behavior of a specific option. 1395 | .br 1396 | This function takes 4 arguments: 1397 | .EX 1398 | .I 1 1399 | .ti +4 1400 | The current option being parsed 1401 | .I 2 1402 | .ti +4 1403 | The current option argument being parsed 1404 | .I 3 1405 | .ti +4 1406 | The unmodified option being parsed. 1407 | .ti +4 1408 | For an option such as \-a, this value will be the same as $1. 1409 | .ti +4 1410 | However, for every \-\-long\-option this value will be "\-". 1411 | .I 4 1412 | .ti +4 1413 | The unmodified option argument being parsed. 1414 | .ti +4 1415 | For an option such as \-c S, this value will be the same as $2. 1416 | .ti +4 1417 | However, for every \-\-long\-option=value, this value will be \-long\-option=value. 1418 | .EE 1419 | 1420 | .TP 1421 | .BR on_opt_parse_ () 1422 | This function gets called before an option is parsed. 1423 | A non 0 exit code will override the default behavior of a specific option. 1424 | .br 1425 | This function takes 3 arguments: 1426 | .RS 1427 | .TP 1428 | .I 1 1429 | The the optarg 1430 | .TP 1431 | .I 2 1432 | The raw opt 1433 | .TP 1434 | .I 3 1435 | The raw optarg 1436 | .RE 1437 | 1438 | .TP 1439 | .BR on_post_set_vars () 1440 | This function gets called after all vars are set, and all opts are parsed. 1441 | .br 1442 | This function takes no arguments. 1443 | 1444 | .TP 1445 | .BR on_clean_up () 1446 | This function is called when the script is cleaning up files from the search, or when the script exits. 1447 | .br 1448 | This function takes no arguments. 1449 | 1450 | .TP 1451 | .BR usage () 1452 | This function calls the print_help_ for each loaded extension. 1453 | .br 1454 | This function takes no arguments. 1455 | 1456 | .TP 1457 | .BR handle_custom_keypresses () 1458 | This function gets called in the internal handle_keypress() function. This function should return 0 to not override the default handle_keypress() function. 1459 | .br 1460 | This function takes 1 argument: 1461 | .EX 1462 | .I 1 1463 | .ti +4 1464 | The key pressed. 1465 | .EE 1466 | 1467 | .TP 1468 | .BR handle_custom_post_keypresses () 1469 | This function gets called in the internal handle_post_keypress() function, this function should return 0 to not override the default handle_post_keypress() function. 1470 | .br 1471 | The job of this function is to undo the changes of the last keypress. 1472 | .br 1473 | This function takes no arguments, it must get the keypress from $keypress_file 1474 | 1475 | .TP 1476 | .BR handle_keypress_* () 1477 | The name of this function should replace the "*" with the name of the shortcut, eg: \fIalt_d\fR 1478 | .br 1479 | in addition replace any "\-" with "_". 1480 | .br 1481 | This function is called after handle_custom_keypresses() if it returned 0, and the shortcut is not a built-in shortcut. 1482 | .br 1483 | This function takes 0 arguments. 1484 | 1485 | .TP 1486 | .BR handle_post_keypress_* () 1487 | The name of this function should replace the "*" with the name of the shortcut, eg: \fIalt_d\fR 1488 | .br 1489 | in addition replace any "\-" with "_". 1490 | .br 1491 | This function is called after handle_custom_post_keypresses() if it returned 0, and the shortcut is not a built-in shortcut. 1492 | .br 1493 | this function takes 0 arguments. 1494 | 1495 | .TP 1496 | .BR handle_custom_action () 1497 | This function is called when an unknown action (as described in VIDEO JSON FORMAT) is given. 1498 | .br 1499 | This function takes 1 argument: 1500 | .EX 1501 | .I 1502 | .ti +4 1503 | The action. 1504 | .EE 1505 | .br 1506 | Exit Codes: 1507 | .RS 1508 | .TP 1509 | .IR 1 1510 | go back to menu 1511 | .TP 1512 | .IR 2 1513 | exit 1514 | .RE 1515 | .RE 1516 | 1517 | .SH CUSTOM THUMBNAILS 1518 | .PP 1519 | Custom thumbnails are located in $YTFZF_CUSTOM_THUMBNAILS_DIR. 1520 | The name of the image must be 1521 | .I .jpg 1522 | .PP 1523 | To see an example, make a search with \fIytfzf\fR and locate the \fIthumbnails\fR folder in $cache_dir/search 1524 | .PP 1525 | Custom thumbnails are going to try to be loaded before the official thumbnail. 1526 | .br 1527 | If a custom thumbnail, and the official thumbnail doesn't exist, ytfzf will try to use 1528 | .I $YTFZF_CUSTOM_THUMBNAILS_DIR/YTFZF:DEFAULT.jpg . 1529 | 1530 | .SH VIDEO JSON FORMAT 1531 | .PP 1532 | This is the format used for playlists, and custom scrapers. 1533 | .br 1534 | Videos should be objects in a list. 1535 | .PP 1536 | Required object keys: 1537 | .EX 1538 | .RE 1539 | ID (string): a unique id to the video 1540 | url (string): the url to the video 1541 | title (string): the title of the video 1542 | scraper (string): The scraper that created the json (used for thumbnails) 1543 | .EE 1544 | .RE 1545 | .PP 1546 | .RS 1547 | .EX 1548 | thumbs (string): a url to a thumbnail/image 1549 | channel (string): the channel name 1550 | duration (string): length of the video (standard: [HH:]MM:SS) 1551 | views (string): amount of views a video has 1552 | date (string): upload date (standard: date is relative to current day, eg: 3 days ago) 1553 | action (string): an action in the format of "action [key=value key2=value2...]" 1554 | .EE 1555 | .RE 1556 | .PP 1557 | Example JSON: 1558 | .EX 1559 | [ 1560 | { 1561 | "ID": "dQw4w9WgXcQ", 1562 | "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", 1563 | "title": "definitely not never gonna give you up" 1564 | } 1565 | ] 1566 | .EE 1567 | 1568 | 1569 | .SH PLAYLISTS 1570 | .PP 1571 | A playlist is a json file in the format of VIDEO JSON FORMAT, 1572 | To easily get the formatted json for a video, run 1573 | .I "ytfzf -I VJ 1594 | .RS 1595 | This function will be called when the scraper is sourced (which is when the user asks for it). 1596 | .PP 1597 | This function takes no arguments. 1598 | .RE 1599 | .PP 1600 | .I thumbnail_video_info_text_ 1601 | .RS 1602 | This function shall print information for the thumbnails interface. 1603 | .PP 1604 | This function is effectively the same as thumbnail_video_info_text(). 1605 | .RE 1606 | .PP 1607 | .IR scrape_next_page_ 1608 | .RS 1609 | .PP 1610 | This function shall scrape more videos from . 1611 | .br 1612 | In order for this function to be called properly with alt-p, or ctrl-p, the page it left off on must be written to ${session_cache_dir}/-current-page. 1613 | .br 1614 | Where scraper is the name of the scraper with _ instead of \- 1615 | .PP 1616 | should be the name put in the "scraper" attribute in VIDEO JSON FORMAT 1617 | .PP 1618 | This function will happen if the user presses alt-p in fzf. 1619 | .PP 1620 | This function takes no arguments. 1621 | .RE 1622 | .PP 1623 | .IR handle_custom_action_ 1624 | .RS 1625 | .PP 1626 | This function shall handle a custom action. 1627 | .PP 1628 | should be the name of the action replacing any "\-" with "_". 1629 | .PP 1630 | This function takes 1 argument. 1631 | .RS 1632 | .TP 1633 | .IR 1 1634 | The action arguments 1635 | .RE 1636 | Exit Codes: 1637 | .RS 1638 | .TP 1639 | .IR 1 1640 | go back to menu 1641 | .TP 1642 | .IR 2 1643 | exit 1644 | .RS 1645 | .RE 1646 | 1647 | .RE 1648 | 1649 | .SH CUSTOM INTERFACES 1650 | .PP 1651 | Custom interfaces are shell scripts located in $YTFZF_CUSTOM_INTERFACES_DIR. 1652 | .br 1653 | An interface is responsible for letting the user pick a video from "$ytfzf_video_json_file", then writing the url(s) to "$ytfzf_selected_urls" 1654 | .br 1655 | The shell script must be the same shell as your /bin/sh. 1656 | .br 1657 | In addition, the script must also define the function 1658 | .I interface_ 1659 | .br 1660 | With _ replacing \-. 1661 | .br 1662 | This function could handle everything itself, or call another program written in any language to handle it. 1663 | .RE 1664 | .PP 1665 | interface_ will take a path to the json file holding all data about all the videos as the first argument. 1666 | .br 1667 | The second argument will be a path to a file to store the selected url in, separated by new lines. 1668 | .PP 1669 | Other functions the scraper may define: 1670 | 1671 | .TP 1672 | .BR search_prompt_menu_ () 1673 | This function should do the same thing as search_prompt_menu(). 1674 | This function takes no arguments. 1675 | .br 1676 | If this function is not defined, search_prompt_menu_ext() will be called instead. 1677 | 1678 | .TP 1679 | .BR quick_menu_ () 1680 | This function should do the same thing as quick_menu(). 1681 | This function takes no arguments. 1682 | .br 1683 | If this function is not defined, quick_menu_ext() will be called instead. 1684 | 1685 | .TP 1686 | .BR display_text_ () 1687 | This function should display text in a way that is copy and pasteable. 1688 | This function takes an unlimited number of arguments. 1689 | .RS 1690 | .TP 1691 | .IR ... 1692 | The text to display 1693 | .RE 1694 | 1695 | .TP 1696 | .BR info_wait_prompt_ () 1697 | This function should ask the user to pick an \fBinfo_wait_action\fR option. 1698 | It should then set the variable \fBinfo_wait_action\fR equal to the user's choice. 1699 | This function takes no arguments. 1700 | 1701 | 1702 | .SH THUMBNAIL VIEWERS 1703 | Custom thumbnail viewers are programs in $YTFZF_THUMBNAIL_VIEWERS_DIR. 1704 | Arguments: 1705 | .RS 1706 | .TP 1707 | .IR 1 1708 | An action, there are 3 actions, start, stop, view, no-img 1709 | .TP 1710 | .IR 2 1711 | The path to the thumbnail. 1712 | .TP 1713 | .IR 3 1714 | x position (in columns) of the image 1715 | .TP 1716 | .IR 4 1717 | y position (in lines) of the image 1718 | .TP 1719 | .IR 5 1720 | width of image (in columns) 1721 | .TP 1722 | .IR 6 1723 | height of image (in lines) 1724 | .TP 1725 | .IR 7 1726 | max width of image (in columns) (width already accounts for this) 1727 | .TP 1728 | .IR 8 1729 | max height of image (in lines) (height already accounts for this) 1730 | .TP 1731 | .IR 9 1732 | side of the terminal to display the image (x, y, width, height already account for this) 1733 | .br 1734 | this will be either \fIup\fR \fIdown\fR \fIleft\fR \fIright\fR 1735 | .RE 1736 | 1737 | .SH EXTENSIONS 1738 | .PP 1739 | Extensions are essentially extra config files that you can load in your own config file. 1740 | .PP 1741 | Extensions should either be in $YTFZF_EXTENSIONS_DIR or $YTFZF_SYSTEM_ADDON_DIR/extensions 1742 | .PP 1743 | An extension can do anything a config file can, this includes modifying the default utility functions in ytfzf (which could break the script) 1744 | .PP 1745 | Extensions will be given a variable called \fB$__loaded_path\fR which is the full path to the extension that is loaded. 1746 | .br 1747 | This is helpful when needing to determine the current runtime path for the extension. 1748 | .PP 1749 | To load an extension add 1750 | .I "load_extension name-of-extension" 1751 | to $YTFZF_CONFIG_FILE 1752 | .PP 1753 | There are two automatically loaded extensions, 1754 | .I __ytfzf__ 1755 | and 1756 | .I __ytfzf_multisearch__ 1757 | .br 1758 | __ytfzf_multisearch__ is only loaded if 1759 | .B $multi_search 1760 | is 1761 | .I 1 1762 | .PP 1763 | Functions for extensions, where is replaced with the name of the extension, and all \- are replaced with _. 1764 | 1765 | .TP 1766 | .BR on_clean_up_ () 1767 | Runs when the script exits. 1768 | This function takes no arguments. 1769 | 1770 | .TP 1771 | .BR on_no_thumbnail_ () 1772 | Runs when no thumbnail is found. 1773 | This function takes no arguments. 1774 | 1775 | .TP 1776 | .BR after_close_url_handler_ () 1777 | Runs after the url handler is closed. 1778 | This function takes no arguments. 1779 | 1780 | .TP 1781 | .BR on_post_set_vars_ () 1782 | Replace ext_name with the name of an extension (with - replaced with _). 1783 | This function is the same as \fBon_post_set_vars\fR 1784 | 1785 | .TP 1786 | .BR ext_on_search_ () 1787 | Runs for before the website is scraped, for every search query. 1788 | This function takes two arguments. 1789 | .RS 1790 | .TP 1791 | .IR 1 1792 | The current search 1793 | .TP 1794 | .IR 2 1795 | The current scrape type 1796 | .RE 1797 | 1798 | .TP 1799 | .BR post_scrape_ () 1800 | Runs after scraping is complete. 1801 | This function takes no arguments. 1802 | 1803 | .TP 1804 | .BR on_init_search_ () 1805 | Runs when the search is being initialized. 1806 | This function takes one argument. 1807 | .RS 1808 | .TP 1809 | .IR 1 1810 | The search 1811 | .RE 1812 | 1813 | .SH UTILITY FUNCTIONS 1814 | .PP 1815 | A utility function is any function that can be used in 1816 | .I configuration files, 1817 | .I extensions, 1818 | .I thumbnail viewers, 1819 | .I interfaces, 1820 | .I search-names, 1821 | 1822 | .TP 1823 | .BR refresh_inv_instances () 1824 | This function writes the response from "https://api.invidious.io/instances.json?sort_by=type,health,api" to $cache_dir/instances.json. 1825 | This function takes no arguments. 1826 | 1827 | .TP 1828 | .BR get_invidious_instances () 1829 | This function grabs invidious instances that have an api and prints them separated by a new line. 1830 | This function takes no arguments. 1831 | 1832 | .TP 1833 | .BR create_sorted_video_data () 1834 | This function prints a sorted jsonl string of the scraped videos that can be used in an interface 1835 | This function takes no arguments. 1836 | 1837 | .TP 1838 | .BR run_interface () 1839 | Runs the function \fBinterface_\fR, and passes \fB$ytfzf_video_json_file\fR, and \fB$ytfzf_selected_urls\fR 1840 | This function takes no arguments. 1841 | 1842 | .TP 1843 | .BR download_thumbnails () 1844 | If \fB$skip_thumb_download\fR is 0, this function downloads thumbnails. 1845 | This function takes an unlimited number of arguments formatted in the following way: 1846 | .EX 1847 | .I ; 1848 | .EE 1849 | This function should download and save it as .jpg in the \fB$thumb_dir\fR folder. 1850 | 1851 | .TP 1852 | .BR prepare_for_set_args () 1853 | This function sets the variable \fB$OLD_IFS\fR to \fB$IFS\fR, then sets \fB$IFS\fR to the first argument, lastly it runs \fBset -f\fR. 1854 | This function takes one argument. 1855 | .RS 1856 | .TP 1857 | .I 1 1858 | The new \fBIFS\fR. 1859 | .RE 1860 | 1861 | .TP 1862 | .BR end_of_set_args () 1863 | This function sets \fBIFS\fR to \fBOLD_IFS\fR. 1864 | This function takes no arguments. 1865 | 1866 | .TP 1867 | .BR modify_ifs () 1868 | This function sets \fBIFS\fR to the first argument. 1869 | This function takes one argument. 1870 | .RS 1871 | .TP 1872 | .I 1 1873 | The value to set \fBIFS\fR to. 1874 | .RE 1875 | 1876 | .TP 1877 | .BR end_modify_ifs () 1878 | This function unsets \fR$IFS\fB. 1879 | This function takes no arguments. 1880 | 1881 | .TP 1882 | .BR mul_str () 1883 | This function does string multiplication, then prints the result to stdout. 1884 | This function takes two arguments. 1885 | .RS 1886 | .TP 1887 | .I 1 1888 | The string. 1889 | 1890 | .TP 1891 | .I 2 1892 | The amount to duplicate the string. 1893 | .RE 1894 | 1895 | .TP 1896 | .BR remove_ansi_escapes () 1897 | This function removes ansi escape sequences from a string passed to this function through stdin. 1898 | This function then prints the final string to stdout. 1899 | This function takes no arguments. 1900 | 1901 | .TP 1902 | .BR do_an_event_function () 1903 | This function runs an event if it exists, then runs event_ for every loaded extension \fB$loaded_extensions\fR, if they exist. 1904 | This function takes an unlimited number of arguments. 1905 | .RS 1906 | .TP 1907 | .I 1 1908 | The name of the event. 1909 | .TP 1910 | .I ... 1911 | The arguments to pass to event 1912 | .RE 1913 | 1914 | .TP 1915 | .BR detach_cmd () 1916 | This function detaches a command from the terminal. 1917 | This function takes an unlimited number of arguments. 1918 | .RS 1919 | .TP 1920 | .I 1 1921 | The command to run 1922 | .TP 1923 | .I ... 1924 | The arguments to pass to the command. 1925 | .RE 1926 | 1927 | .TP 1928 | .BR source_scrapers () 1929 | This function goes through each scraper listed in \fB$scrape\fR, and sources the appropriate file. 1930 | After sourcing the file, it runs the \fBon_startup_\fR function, if \fB$__is_fzf_preview\fR is \fI0\fB. 1931 | This function checks the following locations for the file to source. 1932 | .RS 1933 | .TP 1934 | .I \fB$YTFZF_CUSTOM_SCRAPERS_DIR\fR 1935 | .TP 1936 | .I \fB$YTFZF_SYSTEM_ADDON_DIR/scrapers\fR 1937 | .TP 1938 | .I \fB$YTFZF_CUSTOM_SCRAPERS_DIR\fR 1939 | .TP 1940 | .I \fB$YTFZF_SYSTEM_ADDON_DIR/scrapers\fR 1941 | .RE 1942 | 1943 | .TP 1944 | .BR add_commas () 1945 | This function adds commas to a number (in the standard english way). 1946 | This function gets a number from stdin, and prints the result to stdout. 1947 | This function takes no arguments. 1948 | 1949 | .TP 1950 | .BR command_exists () 1951 | This function checks if a command exists. 1952 | This function exits with status code \fI0\fR if it exists, and \fI1\fR if it does not. 1953 | This function takes 1 argument. 1954 | .RS 1955 | .TP 1956 | .I 1 1957 | The command to check 1958 | .RE 1959 | 1960 | .TP 1961 | .BR get_key_value () 1962 | This function gets the value by a key in a key_value string. 1963 | .br 1964 | A key_value string is formatted in the following way: 1965 | .EX 1966 | key=value... 1967 | where is argument 3, by default a space. 1968 | .EE 1969 | If there is only one key_value pair, there must still be on each side. 1970 | .br 1971 | After printing the value to stdout, this function sets the variable \fB$KEY_VALUE\fR to the value of the key. 1972 | .br 1973 | This function exits with status code \fI0\fR if the value is not empty, and \fI1\fR if it is empty. 1974 | .br 1975 | This function takes three arguments. 1976 | .RS 1977 | .TP 1978 | .I 1 1979 | The key_value string. 1980 | .TP 1981 | .I 2 1982 | The key to find the value for. 1983 | .TP 1984 | .I 3 (optional) 1985 | The separator that separates each key_value pair. 1986 | By default this is a space. 1987 | .RE 1988 | 1989 | .TP 1990 | .BR title_str () 1991 | This function capitalizes the first letter of a string, and prints it to stdout. 1992 | This function takes one argument. 1993 | .RS 1994 | .TP 1995 | .I 1 1996 | The string to title. 1997 | .RE 1998 | 1999 | .TP 2000 | .BR shuf () 2001 | This function is only created if \fBshuf\fR is not installed. 2002 | This function should act the same as the base functionality of \fBshuf\fR 2003 | 2004 | .TP 2005 | .BR print_info () 2006 | This function prints information to stderr, if \fB$log_level\fR is greater than or equal to \fI2\fR. 2007 | This function takes one argument. 2008 | .RS 2009 | .TP 2010 | .I 1 2011 | The text to print 2012 | .RE 2013 | 2014 | .TP 2015 | .BR print_warning () 2016 | This function prints a warning to stderr, if \fB$log_level\fR is greater than or equal to \fI1\fR. 2017 | This function takes one argument. 2018 | .RS 2019 | .TP 2020 | .I 1 2021 | The text to print 2022 | .RE 2023 | 2024 | .TP 2025 | .BR print_error () 2026 | This function prints a warning to stderr, if \fB$log_level\fR is greater than or equal to \fI0\fR. 2027 | This function takes one argument. 2028 | .RS 2029 | .TP 2030 | .I 1 2031 | The text to print 2032 | .RE 2033 | 2034 | .TP 2035 | .BR clean_up () 2036 | This function kills all \fBytfzf(1)\fR subprocesses, and removes \fB$session_cache_dir\fR if \fB$session_cache_dir\fR exists, and if \fB$keep_cache\fR equals \fI0\fB. 2037 | .br 2038 | Lastly, it runs the \fBon_clean_up\fR event. 2039 | This function takes no arguments. 2040 | 2041 | .TP 2042 | .BR is_relative_dir () 2043 | Checks if a string is a relative path. 2044 | This function exits with status code \fI0\fR if it is relative, and \fI1\fR if it is not. 2045 | This function takes one argument. 2046 | .RS 2047 | .TP 2048 | .I 1 2049 | The path to check 2050 | .RE 2051 | 2052 | .TP 2053 | .BR die () 2054 | This function runs \fBprint_error\fR with a string, then exits with a status code. 2055 | This function takes two arguments. 2056 | .RS 2057 | .TP 2058 | .I 1 2059 | The exit status 2060 | .TP 2061 | .I 2 2062 | The text to print with \fBprint_error\fR. 2063 | .RE 2064 | 2065 | .TP 2066 | .BR trim_url () 2067 | This function reads lines from stdin, and prints the url from them to stdout. 2068 | The lines should be formatted in the following way: 2069 | .EX 2070 | text....|url 2071 | .EE 2072 | This function takes no arguments. 2073 | 2074 | .TP 2075 | .BR get_search_from_source () 2076 | This function sets \fB_search\fR to a search depending on the source. 2077 | This function takes an unlimited number of arguments. 2078 | .RS 2079 | .TP 2080 | .I 1 2081 | The source to get the search from. 2082 | .TP 2083 | .I ... 2084 | The arguments to use as the search if \fB$1\fR is \fIfn-args\fR, or if \fB1\fR is not matched, ... is given to \fBget_search_from_<1>\fR. 2085 | .RE 2086 | 2087 | .TP 2088 | .BR load_extension () 2089 | This function adds an extension to the \fB$loaded_extensions\fR variable, and sources the extension. 2090 | This function checks the following locations for the extension 2091 | .EX 2092 | .B $YTFZF_EXTENSIONS_DIR 2093 | .B $YTFZF_SYSTEM_ADDON_DIR/extensions 2094 | .B $PATH 2095 | .EE 2096 | This function exits with the same exit code as when the extension was loaded. 2097 | This function takes one argument. 2098 | .RS 2099 | .TP 2100 | .I 1 2101 | The extension to load 2102 | .RE 2103 | 2104 | .TP 2105 | .BR extension_is_loaded () 2106 | This function checks if an extension is loaded. 2107 | This function takes one argument. 2108 | .RS 2109 | .TP 2110 | .I 1 2111 | The extension to check 2112 | .RE 2113 | 2114 | .TP 2115 | .BR load_sort_name () 2116 | This function loads a sort-name. 2117 | This function checks the following locations for the sort-name. 2118 | .EX 2119 | .B $YTFZF_SORT_NAMES_DIR 2120 | .B $YTFZF_SYSTEM_ADDON_DIR/sort-names 2121 | .EE 2122 | This function exits with status code \fI1\fR if the sort-name does not exist, otherwise it exits with the same status code as when the sort-name was loaded. 2123 | This function takes one argument. 2124 | .RS 2125 | .TP 2126 | .I 1 2127 | The sort-name to load 2128 | .RE 2129 | 2130 | .TP 2131 | .BR load_url_handler () 2132 | This function sets \fB$url_handler\fR to a url-handler. 2133 | This function checks the following locations for the url-handler. 2134 | .EX 2135 | .B $PATH, and functions in config file 2136 | .B $YTFZF_URL_HANDLERS_DIR 2137 | .B $YTFZF_SYSTEM_ADDON_DIR/url-handlers 2138 | If the url-handler does not exist, this function runs \fBdie\fR. 2139 | This function takes one argument. 2140 | .RS 2141 | .TP 2142 | .I 1 2143 | The url-handler to load 2144 | .RE 2145 | 2146 | .TP 2147 | .BR load_interface () 2148 | This function sets \fB$interface\fR to an interface. 2149 | If \fB$1\fR is equal to \fIext\fR, \fIscripting\fR, or "", \fB$interface\fR is set to \fB1\fR. 2150 | This function checks the following locations for the interface. 2151 | .EX 2152 | .B $1 2153 | .B $YTFZF_CUSTOM_INTERFACES_DIR 2154 | .B $YTFZF_SYSTEM_ADDON_DIR/interfaces 2155 | .EE 2156 | This function takes one argument. 2157 | .RS 2158 | .TP 2159 | .I 1 2160 | The interface to load 2161 | .RE 2162 | 2163 | .TP 2164 | .BR load_thumbnail_viewer () 2165 | This function sets \fB$thumbnail_viewer\fR to a thumbnail-viewer. 2166 | If \fB$i\fR is equal to a built-in thumbnail-viewer, \fB$thumbnail_viewer\fR is set to that. 2167 | Otherwise, the following locations are checked. 2168 | .EX 2169 | .B $1 2170 | .B $YTFZF_THUMBNAIL_VIEWERS_DIR 2171 | .B $YTFZF_SYSTEM_ADDON_DIR 2172 | .EE 2173 | This function takes one argument. 2174 | .RS 2175 | .TP 2176 | .I 1 2177 | The thumbnail viewer to load 2178 | .RE 2179 | 2180 | .TP 2181 | .BR _get_request () 2182 | This function sends a request to a server and prints the response. 2183 | This function takes Unlimited arguments. 2184 | .RS 2185 | .TP 2186 | .I 1 2187 | The url to send a request to 2188 | .TP 2189 | .I ... 2190 | Arguments to pass to \fBcurl(1)\fR 2191 | .RE 2192 | 2193 | .TP 2194 | .BR _get_real_channel_link () 2195 | This function converts a youtube channel link in the form of 2196 | .EX 2197 | .I @user 2198 | .I https://www.youtube.com/user/ 2199 | .I https://www.youtube.com/c/ 2200 | .EE 2201 | This function takes one argument. 2202 | .RS 2203 | .TP 2204 | .I 1 2205 | The link to convert 2206 | .RE 2207 | -------------------------------------------------------------------------------- /docs/subscriptions: -------------------------------------------------------------------------------- 1 | #This is an example subscriptions file 2 | #this file should be in ~/.config/ytfzf for ytfzf to use it with the -cS or -cSI options 3 | 4 | https://www.youtube.com/channel/UC5UAwBUum7CPN5buc-_N1Fw #The Linux Experiment 5 | https://invidious.snopyta.org/channel/UCl2mFZoRqjw_ELax4Yisf6w #luis rossman 6 | @pewdiepie #pewdiepie's channel 7 | --------------------------------------------------------------------------------