|
1 | 1 | // ==UserScript== |
2 | 2 | // @name YouTube Save to Playlist filter |
3 | 3 | // @namespace fred.vatin.yt-playlists-filter |
4 | | -// @version 1.0 |
| 4 | +// @version 1.1.0 |
5 | 5 | // @description Tap P key to open the “save to playlist” menu where your can type to filter |
6 | 6 | // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com |
7 | 7 | // @author Fred Vatin, Flemming Steffensen |
|
49 | 49 | const selector_Header = "yt-panel-header-view-model"; |
50 | 50 | const selector_List = ".ytListViewModelHost"; |
51 | 51 | const selector_ListItems = ".toggleableListItemViewModelHost"; |
| 52 | + const selector_SelItems = `:scope > yt-list-item-view-model[aria-pressed="true"]`; |
52 | 53 | const selector_Item = ".yt-core-attributed-string"; |
53 | 54 | const selector_OpenMenuParent = "ytd-app ytd-popup-container"; |
54 | 55 | const InputId = "filterPlaylist"; |
|
314 | 315 | function addFilterInput(elements = {}) { |
315 | 316 | const { list = null, header = null } = elements; |
316 | 317 |
|
| 318 | + autoTopList(list); |
| 319 | + |
317 | 320 | const existingFilterInput = document.getElementById(InputId); |
318 | 321 |
|
319 | 322 | if (!existingFilterInput) { |
|
362 | 365 | * @param {string} filterText - The text to filter playlist items by. Items not containing this text will be hidden. |
363 | 366 | */ |
364 | 367 | function filterPlaylist(playlist, filterText) { |
| 368 | + if (!playlist) { |
| 369 | + console.log("❌ filterPlaylist(playlist, filterText): playlist is null or invalid"); |
| 370 | + return; |
| 371 | + } |
| 372 | + |
365 | 373 | const listItems = playlist.querySelectorAll(selector_ListItems); |
366 | 374 | // const listItems = query(playlist, selector_ListItems); |
367 | 375 | const text = filterText.trim().toLowerCase(); |
|
374 | 382 | }); |
375 | 383 | } |
376 | 384 |
|
| 385 | + /** |
| 386 | + * Automatically sorts playlist items by placing selected items at the top. |
| 387 | + * Selected items are playlists in which the current video already exists. |
| 388 | + * @param {HTMLElement} playlist - The playlist container element to sort |
| 389 | + * @returns {void} |
| 390 | + * @description |
| 391 | + * This function reorganizes playlist items so that selected items appear first, |
| 392 | + * followed by unselected items. It filters items using predefined selectors, |
| 393 | + * logs the operation status, and reinserts items in the new order. |
| 394 | + * @example |
| 395 | + * const playlistElement = document.querySelector('.playlist'); |
| 396 | + * autoTopList(playlistElement); |
| 397 | + */ |
| 398 | + function autoTopList(playlist) { |
| 399 | + if (!playlist) { |
| 400 | + console.log("❌ autoTopList(playlist): playlist is null or invalid"); |
| 401 | + return; |
| 402 | + } |
| 403 | + |
| 404 | + // Collect all items |
| 405 | + const listItems = Array.from(playlist.querySelectorAll(selector_ListItems)); |
| 406 | + console.log("✅ autoTopList(playlist), listItems number: ", listItems.length); |
| 407 | + |
| 408 | + // Separates the selected ones from the rest |
| 409 | + const selected = listItems.filter((item) => item.querySelector(selector_SelItems)); |
| 410 | + const unselected = listItems.filter((item) => !item.querySelector(selector_SelItems)); |
| 411 | + |
| 412 | + if (selected.length > 0) { |
| 413 | + console.log(`✅ autoTopList(playlist), this video belongs to ${selected.length} playlist(s)`); |
| 414 | + // Reinsert in the desired order |
| 415 | + [...selected, ...unselected].forEach((item) => { |
| 416 | + playlist.appendChild(item); |
| 417 | + }); |
| 418 | + |
| 419 | + console.log(`✅ autoTopList(playlist), playlists have been sorted.`); |
| 420 | + |
| 421 | + const sortedItems = Array.from(playlist.querySelectorAll(selector_ListItems)); |
| 422 | + console.log("✅ autoTopList(playlist), sortedItems number: ", sortedItems.length); |
| 423 | + } else { |
| 424 | + console.log("❌ autoTopList(playlist): this video doesn’t belong to any existing playlist"); |
| 425 | + } |
| 426 | + } |
| 427 | + |
377 | 428 | /** |
378 | 429 | * Sets focus to the input element with the given ID, resets its value, |
379 | 430 | * and dispatches an "input" event to trigger any associated handlers. |
|
0 commit comments