Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: differentiate shorts, lives and long videos #371

Conversation

arjitcodes
Copy link
Contributor

@arjitcodes arjitcodes commented Oct 27, 2024

Fixes #366 #367

Changes in scraper

  1. Add userLongUploadsPlaylist, userShortUploadsPlaylist, and userLivesPlaylist in channel.json, which contain the user's uploaded long videos, shorts, and live videos, respectively, to mirror YouTube's organized experience, even offline, such as dedicated user uploads Videos (longs) tab, shorts tab, and lives tab.

Changes in zimui

  1. Add CSS in vjs-youtube.css to correct short video resolution.
  2. Add videos shorts lives tab
    • videos contains long videos uploaded by the channel.
    • shorts contains shorts uploaded by the channel
    • lives contains lives by the channel
  3. Add the channel-home route, which points to the home tab. The home tab was previously named the videos tab.
  4. Update channelHeader for dynamic tabs to display only available tabs, such as shorts, videos, and lives only if available in the channel.

Visuals

Old Home
Screenshot from 2024-10-27 12-29-12
New Home
Screenshot from 2024-10-27 12-49-22

Old Player
Screenshot from 2024-10-27 12-29-27

New Player
Screenshot from 2024-10-27 12-49-31

@arjitcodes
Copy link
Contributor Author

@kelson42

@kelson42 kelson42 requested a review from benoit74 October 27, 2024 14:55
Copy link
Collaborator

@benoit74 benoit74 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I'm amazed by the intent of this first contribution.

Unfortunately, I still have many questions / doubts that need to be clarified before we can dive in code details:

  • why are you sometimes speaking about normal videos and at other times about long videos? I'm a bit puzzled. And I don't get why we have only one very specific playlist here, where from my experience we often have many playlists of normal/long videos per channel in many cases
  • why are you making new calls to retrieve specific playlist IDs (get_long_videos_playlist_id, ...) where get_channel_playlists_json is already supposed to retrieve all playlists of the channel?
  • why are you using so much magic in is_short function where you have a magic cut-off date, a magic maximum duration ... this needs to be justified at least
  • why are you making a new API call in is_short instead of just checking the playlist ID of current video starts with the magic string? We need to limit API calls at their strict minimum to avoid having throttling issues
  • why do you differentiate lives from long videos? Are they really so special they need special care? does it make any sense in an offline scenario? Do live videos stays in the live playlist once the live is over?

@arjitcodes arjitcodes changed the title Fix: differentiate shorts, lives and normal videos Fix: differentiate shorts, lives and long videos Oct 28, 2024
@arjitcodes
Copy link
Contributor Author

arjitcodes commented Oct 28, 2024

  • I apologize for the confusion caused by my use of both 'normal videos' and 'long videos' – they actually refer to the same type of content. To clarify, 'normal/long videos' represent user-uploaded videos that are longer in format and don't include Shorts or live streams, which are already organized separately within the channel. The long_videos_playlist is specifically designed to help us render these videos in a style similar to YouTube's main video section, making it easier to access the user uploaded long-form content in a dedicated playlist. Thanks for pointing this out, and I've made the necessary adjustments to ensure consistency!

  • The main reason for these additional calls to retrieve specific playlist IDs like get_long_videos_playlist_id is that get_channel_playlists_json gives us a full list of the channel's playlists, including the general 'user uploads' playlist, which contains all videos (shorts, long videos, and live streams). However, to mirror YouTube's organized experience, even offline, I’m specifically targeting separate playlists for long_videos_playlist, shorts_playlist, and lives_playlist. These allow us to categorize user-uploaded long videos, shorts, and live content separately, just as YouTube has distinct tabs for Videos (long videos), Shorts, and Live. This approach gives users a familiar, organized browsing experience similar to YouTube’s.
    I'm currently refining these naming conventions to make their purpose clearer, and I’m open to any suggestions you may have!

  • I see where you're coming from! I initially added the is_short function to help identify short videos based on a cutoff date and maximum duration, but in retrospect, it’s unnecessary since now the frontend player can show the short video correctly without explicitly marking it. To simplify things and avoid adding extra logic, I’ll remove the is_short function for now. Thanks for catching this, and I apologize for the added complexity—I’ll be more cautious to avoid similar situations in the future!

  • The main reason for differentiating live videos from long videos is to mirror the YouTube experience, where live streams have their own dedicated tab.

@arjitcodes arjitcodes marked this pull request as draft October 28, 2024 11:36
@arjitcodes arjitcodes marked this pull request as ready for review October 31, 2024 08:09
@arjitcodes arjitcodes requested a review from benoit74 October 31, 2024 08:09
Copy link
Collaborator

@benoit74 benoit74 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you !

I still don't get why you are making additional API calls. The playlists you need are already in the results of get_channel_playlists_json, why can't you simply use information already retrieved here? To me it looks like it is just a matter of finding the playlist with the ID matching the convention you are using.

And on top of that, regarding this convention (short playlist is "UUSH" + channel_id[2:], live playlist is "UULF" + channel_id[2:], video playlist is "UULV" + channel_id[2:]), where does it comes from? I have at least one example where this is purely not working. For CarbiDIY channel (ID UCuct2fNB1nSdS8Dl6XzdA1g), the short playlist ID is PLUo94RsjxOG82eLooMlRoCS2vWOqzB4_m, there are multiple long videos playlists (all these videos are visible in the "Videos" tab on youtube) and the live playlist is not listed in the playlists API endpoint. See below. Do I miss something?

Playlists of channel UCuct2fNB1nSdS8Dl6XzdA1g (CabriDIY)

{ "kind": "youtube#playlistListResponse", "etag": "RqabC8kHsOhhJm-tqxTMpmM5RoM", "pageInfo": { "totalResults": 22, "resultsPerPage": 25 }, "items": [ { "kind": "youtube#playlist", "etag": "vn_h-lgruI6dTb8FkcNMTKyxkfs", "id": "PLUo94RsjxOG82eLooMlRoCS2vWOqzB4_m", "snippet": { "publishedAt": "2023-10-18T13:49:02Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Short", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/U84iQbF7ob8/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/U84iQbF7ob8/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/U84iQbF7ob8/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/U84iQbF7ob8/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/U84iQbF7ob8/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Short", "description": "" } }, "contentDetails": { "itemCount": 7 } }, { "kind": "youtube#playlist", "etag": "G9NnDqOYdC61FlYVMF0NQPRRgBc", "id": "PLUo94RsjxOG9XYbTrSBwaUBRz_dSzC4am", "snippet": { "publishedAt": "2023-06-17T13:55:16Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "TikTok", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/tgfpj-Q1m80/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/tgfpj-Q1m80/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/tgfpj-Q1m80/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/tgfpj-Q1m80/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/tgfpj-Q1m80/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "TikTok", "description": "" } }, "contentDetails": { "itemCount": 6 } }, { "kind": "youtube#playlist", "etag": "4rPLddxHvgD-EVEOR3H2OOV0Mu0", "id": "PLUo94RsjxOG_C3ybIF7YxiRfhxwLYoMKq", "snippet": { "publishedAt": "2022-03-20T15:51:41Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Marathon", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/tyMbsYPd0zo/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/tyMbsYPd0zo/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/tyMbsYPd0zo/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/tyMbsYPd0zo/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/tyMbsYPd0zo/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Marathon", "description": "" } }, "contentDetails": { "itemCount": 3 } }, { "kind": "youtube#playlist", "etag": "AD5glo4vlrDp72umzslhUpFyH4s", "id": "PLUo94RsjxOG_Yt5kh1iklXpeH8zGMo5gB", "snippet": { "publishedAt": "2021-12-16T22:05:32Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "PlayStation 1 miroir", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/1TeRgE25fjo/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/1TeRgE25fjo/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/1TeRgE25fjo/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/1TeRgE25fjo/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/1TeRgE25fjo/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "PlayStation 1 miroir", "description": "" } }, "contentDetails": { "itemCount": 6 } }, { "kind": "youtube#playlist", "etag": "k-tihhlyFRaZwXxLFDDczkniMZQ", "id": "PLUo94RsjxOG8FEjZBeAcoM1u3j7b8rqJj", "snippet": { "publishedAt": "2021-07-28T11:14:15Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Réparation de l'extrême", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/BDjmFoSoqEM/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/BDjmFoSoqEM/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/BDjmFoSoqEM/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/BDjmFoSoqEM/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/BDjmFoSoqEM/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Réparation de l'extrême", "description": "" } }, "contentDetails": { "itemCount": 2 } }, { "kind": "youtube#playlist", "etag": "lfUx3NpnZfPx85tzXhAFM9XLeiM", "id": "PLUo94RsjxOG_QCmJOaSICFWmmcl3YdJ_k", "snippet": { "publishedAt": "2021-06-23T16:08:48Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Présentation PPT", "description": "Voici la playlist des différentes présentations de console réalisés en live sur Twitch. Certaines sont des VODs complètent mais commencent par la présentation de la console.", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/3JyY84OZQ5U/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/3JyY84OZQ5U/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/3JyY84OZQ5U/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/3JyY84OZQ5U/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/3JyY84OZQ5U/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Présentation PPT", "description": "Voici la playlist des différentes présentations de console réalisés en live sur Twitch. Certaines sont des VODs complètent mais commencent par la présentation de la console." } }, "contentDetails": { "itemCount": 8 } }, { "kind": "youtube#playlist", "etag": "Hq0KyqZZij6lSxObn5zqURiX5C8", "id": "PLUo94RsjxOG8e0lku3sOAY3rsg4N7o7CP", "snippet": { "publishedAt": "2020-11-04T20:43:01Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Free copyright music", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/kcUh5viJCAo/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/kcUh5viJCAo/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/kcUh5viJCAo/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/kcUh5viJCAo/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/kcUh5viJCAo/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Free copyright music", "description": "" } }, "contentDetails": { "itemCount": 4 } }, { "kind": "youtube#playlist", "etag": "SxVJiqpTVvoCKFwrerSJlXYIcwo", "id": "PLUo94RsjxOG8E7f5OaaX_iMPqC2UGizdT", "snippet": { "publishedAt": "2020-11-03T17:15:56Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Modernisation Radio 1950", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/00jJBwZoOC4/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/00jJBwZoOC4/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/00jJBwZoOC4/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/00jJBwZoOC4/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/00jJBwZoOC4/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Modernisation Radio 1950", "description": "" } }, "contentDetails": { "itemCount": 14 } }, { "kind": "youtube#playlist", "etag": "aygeqh8XE7uiYTu6krKoU1OfjZM", "id": "PLUo94RsjxOG_w4TLMGd-1wTNeCkuebmql", "snippet": { "publishedAt": "2020-10-02T20:41:08Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Soirée réact", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/SHlyzqfIh3o/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/SHlyzqfIh3o/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/SHlyzqfIh3o/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/SHlyzqfIh3o/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/SHlyzqfIh3o/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Soirée réact", "description": "" } }, "contentDetails": { "itemCount": 7 } }, { "kind": "youtube#playlist", "etag": "jwBimkhzpWwpccSffbqFSgM9Psw", "id": "PLUo94RsjxOG9EaKOdzy7q4D7ran5sA-nF", "snippet": { "publishedAt": "2020-09-14T18:38:11Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "PS4 portable V2", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/cuHzEORnE2g/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/cuHzEORnE2g/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/cuHzEORnE2g/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/cuHzEORnE2g/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/cuHzEORnE2g/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "PS4 portable V2", "description": "" } }, "contentDetails": { "itemCount": 20 } }, { "kind": "youtube#playlist", "etag": "-fEslU_EX5frfIe8EyQnHE3C048", "id": "PLUo94RsjxOG9wv2-UzGNgaEz2szI1_lpX", "snippet": { "publishedAt": "2020-09-11T07:00:11Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "GB Studio", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/omdEc4cE46o/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/omdEc4cE46o/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/omdEc4cE46o/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/omdEc4cE46o/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/omdEc4cE46o/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "GB Studio", "description": "" } }, "contentDetails": { "itemCount": 4 } }, { "kind": "youtube#playlist", "etag": "9M3m8iQCh67ApXf1YppOGDt6YWU", "id": "PLUo94RsjxOG_hgit0aNBiKFqm1E9Di6Xc", "snippet": { "publishedAt": "2020-07-23T04:57:38Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Forza", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/ULrOv_IRvJ0/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/ULrOv_IRvJ0/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/ULrOv_IRvJ0/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/ULrOv_IRvJ0/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/ULrOv_IRvJ0/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Forza", "description": "" } }, "contentDetails": { "itemCount": 1 } }, { "kind": "youtube#playlist", "etag": "NlK9rgt3E4dLiGAne4BJmSRBgGo", "id": "PLUo94RsjxOG8RkLZrgkY3vwZaqLWTuySa", "snippet": { "publishedAt": "2020-07-16T20:18:45Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Warzone", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/dwuM0LNJxOg/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/dwuM0LNJxOg/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/dwuM0LNJxOg/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/dwuM0LNJxOg/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/dwuM0LNJxOg/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Warzone", "description": "" } }, "contentDetails": { "itemCount": 7 } }, { "kind": "youtube#playlist", "etag": "EJ7Sv9i2EXYwkOKv9KWhD99IRQc", "id": "PLUo94RsjxOG9b9DvyewIF-9UwLCQX50mW", "snippet": { "publishedAt": "2020-07-06T20:59:55Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "PS4 portable", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/a_3tiiofN2k/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/a_3tiiofN2k/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/a_3tiiofN2k/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/a_3tiiofN2k/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/a_3tiiofN2k/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "PS4 portable", "description": "" } }, "contentDetails": { "itemCount": 7 } }, { "kind": "youtube#playlist", "etag": "-jinXOHlEOQt_m7Bqy16cPwEJ2s", "id": "PLUo94RsjxOG_9LT_F-L7diQfTdVjROpl-", "snippet": { "publishedAt": "2020-04-20T17:54:00Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "PlayStation 2 Minitel", "description": "Cette playlist contient les rediffusions de mes lives ou j'ai créé une Minitel Playstation 2. Donc mettre une vraie carte mère de PS2 dans un Minitel ainsi que rendre portable le tout avec une batterie tout en essayant de dénaturer le moins possible la forme originelle du Minitel.\n\nBon visionnage :)", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/0DW-fvakHto/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/0DW-fvakHto/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/0DW-fvakHto/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/0DW-fvakHto/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/0DW-fvakHto/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "PlayStation 2 Minitel", "description": "Cette playlist contient les rediffusions de mes lives ou j'ai créé une Minitel Playstation 2. Donc mettre une vraie carte mère de PS2 dans un Minitel ainsi que rendre portable le tout avec une batterie tout en essayant de dénaturer le moins possible la forme originelle du Minitel.\n\nBon visionnage :)" } }, "contentDetails": { "itemCount": 13 } }, { "kind": "youtube#playlist", "etag": "61bs7IQg4EwDv8LFgxBihsmZedo", "id": "PLUo94RsjxOG-pCzntzqcqSC0lFUqjBsHW", "snippet": { "publishedAt": "2020-03-31T10:07:28Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Unity", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/JaBpqoWtUlo/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/JaBpqoWtUlo/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/JaBpqoWtUlo/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/JaBpqoWtUlo/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/JaBpqoWtUlo/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Unity", "description": "" } }, "contentDetails": { "itemCount": 5 } }, { "kind": "youtube#playlist", "etag": "YpXl-nmnBGR1-SIkEmkolQAHS54", "id": "PLUo94RsjxOG83s148IMG7sWiQKKVYw9GO", "snippet": { "publishedAt": "2020-02-10T23:52:42Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Soirée Découverte", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/dPyVmhXx5zI/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/dPyVmhXx5zI/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/dPyVmhXx5zI/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/dPyVmhXx5zI/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/dPyVmhXx5zI/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Soirée Découverte", "description": "" } }, "contentDetails": { "itemCount": 38 } }, { "kind": "youtube#playlist", "etag": "OcFw5SvGmFG-zUhM7RzO3vMQr7o", "id": "PLUo94RsjxOG88Cdl01ZowabzfVfywKx17", "snippet": { "publishedAt": "2019-12-10T09:08:38Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Tutoriel", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/FEMwcdcnsHs/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/FEMwcdcnsHs/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/FEMwcdcnsHs/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/FEMwcdcnsHs/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/FEMwcdcnsHs/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Tutoriel", "description": "" } }, "contentDetails": { "itemCount": 4 } }, { "kind": "youtube#playlist", "etag": "qE17gYd5af5SmRmJuujH5Mr3pdE", "id": "PLUo94RsjxOG_ri33kAW5MrPhgrTGqkSxz", "snippet": { "publishedAt": "2019-09-12T10:46:28Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Vlog", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/X3gJKZnU1Xs/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/X3gJKZnU1Xs/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/X3gJKZnU1Xs/hqdefault.jpg", "width": 480, "height": 360 } }, "channelTitle": "CabriDIY", "localized": { "title": "Vlog", "description": "" } }, "contentDetails": { "itemCount": 2 } }, { "kind": "youtube#playlist", "etag": "flZzgqMsz3FdJ47xYc-4qvK2cRg", "id": "PLUo94RsjxOG_EUKSlM0VRocmsmkD2zYIn", "snippet": { "publishedAt": "2019-03-18T20:53:38Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Rétro Découverte", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/G_tNO82T2Pw/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/G_tNO82T2Pw/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/G_tNO82T2Pw/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/G_tNO82T2Pw/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/G_tNO82T2Pw/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Rétro Découverte", "description": "" } }, "contentDetails": { "itemCount": 25 } }, { "kind": "youtube#playlist", "etag": "cSZsHlZVokco2TEEyp0oKLmYA1o", "id": "PLUo94RsjxOG_MUINY63Xc7bZk5FK5iEfE", "snippet": { "publishedAt": "2018-06-20T18:00:02Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Montage", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/SCUpyAwtFlU/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/SCUpyAwtFlU/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/SCUpyAwtFlU/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/SCUpyAwtFlU/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/SCUpyAwtFlU/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Montage", "description": "" } }, "contentDetails": { "itemCount": 11 } }, { "kind": "youtube#playlist", "etag": "_xRRJyYLN5wYZBoN4zIheQUhdHk", "id": "PLUo94RsjxOG-SvUixo4LLRZX8Fy7xTO2E", "snippet": { "publishedAt": "2018-05-23T17:48:31Z", "channelId": "UCuct2fNB1nSdS8Dl6XzdA1g", "title": "Rediffusion", "description": "", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/3JiHpX1K-pg/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/3JiHpX1K-pg/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/3JiHpX1K-pg/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/3JiHpX1K-pg/sddefault.jpg", "width": 640, "height": 480 }, "maxres": { "url": "https://i.ytimg.com/vi/3JiHpX1K-pg/maxresdefault.jpg", "width": 1280, "height": 720 } }, "channelTitle": "CabriDIY", "localized": { "title": "Rediffusion", "description": "" } }, "contentDetails": { "itemCount": 943 } } ] }

@arjitcodes
Copy link
Contributor Author

arjitcodes commented Oct 31, 2024

Thank you for your feedback!

The additional API calls are necessary because get_channel_playlists_json retrieves playlists manually created by the channel, but it doesn’t capture the distinct 'shorts' or 'long videos' separation we need. To provide a YouTube-like experience with separate tabs for shorts, long videos, and live streams, we differentiate user-uploaded content through specific playlist IDs.

In most cases, channels don't manually create a 'shorts' playlist, as YouTube generates this automatically by using an ID beginning with 'UUSH' (replacing 'UC' in the channel ID) for the 'Shorts' tab. This UUSH ID is the easiest and most consistent way to retrieve a channel's uploaded shorts; however, it’s not included in the get_channel_playlists_json results because its only contain the playlist creates manually by channel. In the CarbiDIY channel example, they manually created a playlist called 'shorts,' which includes all of their short videos. This is an exception rather than the rule, as most channels rely on YouTube’s auto-generated playlists.

user lives playlist and user long uploads playlist work the same way as shorts by adding UULV & UULF replacing UC in the channel ID.

@benoit74
Copy link
Collaborator

OK but then, if we run the scraper on CabriDIY channel, we will have no Lives tab, no Videos tab, and no Shorts tab. And we are far from moving close to Youtube UI. And #367 is not solved at all. Same for JamyEpicurieux channel which is the original target of the issue. Or am I missing something?

Do you have a sample channel where we have these "automatically created" playlists? It looks to me they are pretty rare (didn't found on in three channels I tested).

And regarding #366, it looks like your CSS changes are only targeting the player, and they not very aligned with Youtube UI. The ratio of preview pictures in "Shorts" tab is still 4:3 (or something like this) instead being 3:4 (or whatever ratio Youtube is using). And the player still exhibits black areas where it would be way better if it matches the video ratio.

@arjitcodes
Copy link
Contributor Author

"Automatically created" playlists are not in the result of get_playlist_channel_json thats why you don't have any lives, shorts or any lives tab and that’s the only reason I need to create get_long_uploads_playlist... to check if long videos are available by making a request to /playlist with "UULF" + channel_id[2:]. The response includes all long videos uploaded by the channel . If the response's playlist_items is not None, it means long videos is available, and only then do we add user_long_upload_playlist to playlists.json; otherwise, we don’t add it.

get_user_short_uploads_playlist and get_user_lives_playlist work the same way."

@benoit74
Copy link
Collaborator

I think you didn't got me correctly. What I'm saying, is that by executing the code of this PR, I think that I will have a ZIM without any Videos, Live or Shorts tabs, despite your new code and the fact that these are displayed in Youtube online. I didn't tested from end-to-end, I just called your new method on my sample channel and got None, so I assume I won't have a tab. If so, this PR does not yet solve aforementioned issues at all. Do not hesitate to correct me if I'm wrong.

@arjitcodes
Copy link
Contributor Author

I understand the concern, and I’ve thoroughly tested the code on the CarbiDIY channel and other sample channels, ensuring that the Videos, Live, and Shorts tabs appear correctly. When calling the new methods, they retrieve the expected playlists, so it should work as intended for displaying these tabs offline as well.

It’s possible there could be specific cases or conditions affecting playlist retrieval. I'd be happy to go through any additional channels or scenarios you’re concerned about to make sure the PR is fully robust. do you have any suggestions ?

For CabriDIY, Videos Tab: UULFuct2fNB1nSdS8Dl6XzdA1g Shorts Tab: UUSHuct2fNB1nSdS8Dl6XzdA1g Lives Tab:UULVuct2fNB1nSdS8Dl6XzdA1g all are includes at the end -->
Screenshot from 2024-10-31 20-02-06

For JamyEpicurieux,
Screenshot from 2024-10-31 20-14-27

Copy link
Collaborator

@benoit74 benoit74 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, got it, I've probably simply made a typo when testing your code, sorry for that.

See inline comments for required changes.

Other than that, you need to have properly run invoke lintall (inside scraper folder) to lint your code and invoke checkall to type check your code. This will run in the CI, but this would avoid the CI fails.

And please find a way to not duplicate all tabs and grid components in Vue.JS, I'm pretty sure 90% of the code is identical, and duplicating all these files is only going to be painful to maintain (I can already very hardly tell what the difference is between these tabs and grids codes).

scraper/src/youtube2zim/scraper.py Outdated Show resolved Hide resolved
scraper/src/youtube2zim/youtube.py Outdated Show resolved Hide resolved
scraper/src/youtube2zim/youtube.py Outdated Show resolved Hide resolved
scraper/src/youtube2zim/youtube.py Outdated Show resolved Hide resolved
zimui/src/components/channel/tabs/ShortsTab.vue Outdated Show resolved Hide resolved
zimui/src/components/channel/tabs/VideosTab.vue Outdated Show resolved Hide resolved
zimui/src/components/channel/tabs/LivesTab.vue Outdated Show resolved Hide resolved
Copy link

codecov bot commented Nov 1, 2024

Codecov Report

Attention: Patch coverage is 0% with 35 lines in your changes missing coverage. Please review.

Project coverage is 1.24%. Comparing base (234e6fa) to head (90d391b).

Files with missing lines Patch % Lines
scraper/src/youtube2zim/youtube.py 0.00% 16 Missing ⚠️
scraper/src/youtube2zim/scraper.py 0.00% 15 Missing ⚠️
scraper/src/youtube2zim/schemas.py 0.00% 4 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main    #371      +/-   ##
========================================
- Coverage   1.25%   1.24%   -0.01%     
========================================
  Files         11      11              
  Lines       1120    1124       +4     
  Branches     165     164       -1     
========================================
  Hits          14      14              
- Misses      1106    1110       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

@arjitcodes arjitcodes marked this pull request as draft November 13, 2024 15:32
@arjitcodes arjitcodes requested a review from benoit74 November 14, 2024 19:11
@arjitcodes arjitcodes marked this pull request as ready for review November 14, 2024 19:12
Copy link
Collaborator

@benoit74 benoit74 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for your commitment, not an easy PR at all! But we are definitely getting much closer to a merge, I like it.

I've made some inline comments.

In addition to that, I tested the code on our test channel (ID UC8elThf5TGMpQfQc_VE917Q) https://www.youtube.com/@openZIM_testing/ and I've found (only) three additional issues.

First, we get two identical playlists : UULF8elThf5TGMpQfQc_VE917Q and UU8elThf5TGMpQfQc_VE917Q. The first one is the special Videos playlist you've added, the other one has always been returned by Youtube API when listing playlists of a channel. It seems to be a bit special as well given its ID. We probably need to have only one of these two. Don't really know how to do it properly. I think the safest solution (because I have no idea what can be found in the wild, at least I don't) is that when we have both UULF<magic_channel_suffix> and UU<magic_channel_suffix> playlists in the list, then we start a routine to compare contents of both playlists. If both lists are identical (which should be the case, at least it is on our test channel, but who knows ...) then we drop UU<magic_channel_suffix> from the list of playlists. Or maybe you have more info on this.

Second, I think that since special playlists have their dedicated tabs, they should not be listed on the Playlists tab anymore, just like on Youtube online.

Third, should we move special playlists to be the first displayed on the ChannelHomeListTab.vue? It seems to be the case in general on Youtube, but this is maybe customizable by channel owner, not sure. At least it is weird to me to have the Videos playlist at the end of the list.

scraper/src/youtube2zim/youtube.py Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
zimui/src/views/TabView.vue Outdated Show resolved Hide resolved
zimui/src/views/TabView.vue Outdated Show resolved Hide resolved
zimui/src/views/TabView.vue Outdated Show resolved Hide resolved
zimui/src/views/TabView.vue Outdated Show resolved Hide resolved
@arjitcodes
Copy link
Contributor Author

Since we have special playlists for long videos, shorts, and live streams, the UU (user-uploaded) playlist seems redundant. Here's the proposed plan:

  1. Remove the UU Playlist

    • Exclude the UU playlist from the playlist list and channel.json as it is no longer needed.
  2. Dynamic Assignment of mainPlaylist

    • Set mainPlaylist based on the availability of special playlists:
      • If the long videos playlist exists, set it as mainPlaylist.
      • If the long videos playlist is unavailable, fall back to the shorts playlist.
      • If both long videos and shorts playlists are unavailable, use the live playlist.
      • If no special playlists are available, set mainPlaylist to none.

This ensures mainPlaylist always points to the most relevant content.

Let me know if this approach works or if you have a better suggestion!

@benoit74
Copy link
Collaborator

Thank you! Mostly OK, slight adaptations and few missed points detailed below

  1. Remove the UU Playlist

Exclude the UU playlist from the playlist list and channel.json as it is no longer needed.

I would prefer that we do it conditionally, only if UU and UULF playlists have the same content, I'm not sure this is always the case and I don't want that we discover it too late. But yes, this is the idea.

  1. Dynamic Assignment of mainPlaylist

I forgot about this detail, good point! Looking further into the codebase, I see only two usage of this property:

  • in ChannelHomeGridTab.vue, we use mainPlaylist to know which playlist we want to display (we display only one playlist on this component); since ChannelHomeGridTab is used only when hideTabs is true, and this happens only when we have only one playlist
  • in ChannelHomeListTab.vue and PlaylistsTab.vue, we've "abused" this property to know when we've loaded the channel data

I hence propose something slightly different, because I think we need a nicer fallback to always have a mainPlaylist: set mainPlaylist based on the availability of special playlists or first playlist:

  • If the long videos playlist exists, set it as mainPlaylist.
  • If the long videos playlist is unavailable, fall back to the shorts playlist.
  • If both long videos and shorts playlists are unavailable, use the live playlist.
  • If no special playlists are available, set mainPlaylist to none. If no special playlists are available, set mainPlaylist to first playlist in the list
  1. remove special playlists from the Playlists tab PlaylistsTab.vue

Since special playlists have their dedicated tabs, they should not be listed on the Playlists tab anymore, just like on Youtube online.

  1. order playlists on ChannelHomeListTab.vue

Special playlists must be the first to be displayed on the ChannelHomeListTab.vue. It seems to be the case in general on Youtube, and it is nicer than having the special playlists at the end. Proper order is (of course ignoring playlists if they do not exists on given channel):

  • UU playlist
  • long videos playlist
  • shorts playlist
  • live playlist
  • other playlists

@arjitcodes arjitcodes marked this pull request as draft November 22, 2024 08:23
@arjitcodes arjitcodes requested a review from benoit74 November 22, 2024 19:05
@arjitcodes arjitcodes marked this pull request as ready for review November 22, 2024 19:06
@arjitcodes
Copy link
Contributor Author

Handling Identical Playlists

Since we have three special playlists (long videos, shorts, and live) and one main playlist (UU), the only case where we would encounter two identical playlists is if one of the special playlists exists while the others are missing. This would result in the main playlist (UU) and one of the special playlists being identical.

Solution:

  1. Identify Identical Playlists:
    If only one of the three special playlists (long videos, shorts, or live) is present, then the UU playlist will be identical to that special playlist.

  2. Remove Duplicate:
    In this case, we can safely remove the UU playlist from the list since it is redundant.

This solution allows us to detect the duplicate and remove it efficiently without needing to compare the contents of the playlists. By using the presence of the special playlists, we can determine if the UU playlist is redundant and drop it.

@benoit74
Copy link
Collaborator

If only one of the three special playlists (long videos, shorts, or live) is present, then the UU playlist will be identical to that special playlist.

Where does this statement comes from? I don't get it

@arjitcodes
Copy link
Contributor Author

arjitcodes commented Nov 24, 2024

The statement

If only one of the three special playlists (long videos, shorts, or live) is present, then the UU playlist will be identical to that special playlist

is based on the structure of the playlists.

The UU (user upload) playlist contains all types of uploaded content, regardless of their form (shorts, long videos, or lives). On the other hand, the three special playlists each contain only a specific type of content:

  • UUSH<> for uploaded shorts,
  • UULF<> for uploaded long videos,
  • UULV<> for recorded lives.

Therefore, the UU playlist can only be identical to one of the special playlists if the channel (or user) has only one type of content: either only shorts, only long videos, or only live streams. This is the case with the zimtest channel, where only one type of content (only long videos) is present at a time. If multiple types of content were present, the UU playlist would contain a mix of them and would not be identical to any single special playlist.

Let me know if this clears things up!

@benoit74
Copy link
Collaborator

OK, so I finally got your point. And I agree that we should simply remove the UU playlists as you suggested in #371 ; sorry for taking so long to understand, but it wasn't clear at all until your last comment.

Looking forward to see these last small changes implemented ^^

@arjitcodes
Copy link
Contributor Author

I've implemented the changes as discussed, including removing the UU playlists as per #371. Everything is ready for review now, and I'm excited to hear your thoughts! 😊

Looking forward to your feedback!

@benoit74 benoit74 force-pushed the fix/differentiate-shorts-lives-normal-videos branch from 90d391b to cb7f3ac Compare November 25, 2024 17:06
Copy link
Collaborator

@benoit74 benoit74 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arjitcodes I've simplified your code further down, it was way too complex for probably nothing ; note that among other simplifications, I renamed mainPlaylist to firstPlaylist for clarity / disambiguation, and I completely removed the UU playlist since anyway we were adding this playlist manually

Can you please have a look and indicate what I could have missed? (not sure I've everything in mind, you spent more time than I did on this PR)

@benoit74
Copy link
Collaborator

Nota: CI is failing due to secrets not being passed properly because you're an external contributor, I've opened #377

@arjitcodes
Copy link
Contributor Author

Thanks for simplifying the code—it looks great! I’ve reviewed the changes, and everything seems spot on.

@benoit74
Copy link
Collaborator

Superseeded by #378 (to ensure CI runs OK)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

"shorts" videos resolution seems wrong (too high)
2 participants