├── .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 |
14 |
15 |
16 |
17 |
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 |
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 <": Adds a channel to your subscriptions file
7 | --remove-sub="": Removes a channel (or multiple channels) from your subscriptions file
8 | EOF
9 | }
10 |
11 | on_opt_parse_add_sub () {
12 | real_link="$(_get_real_channel_link "$1")"
13 | printf "%s >> " "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 |
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 |
--------------------------------------------------------------------------------