ytmdl
ytmdl copied to clipboard
Sync/Download Private Youtube Music Library
Feature Request
Description
I thought it would be a neat feature to be able to download the whole personal Youtube Music library ( Albums & Playlists ) with help from ytmusicapi to retrieve info @deepjyoti30
@jja88 Thanks for raising the request.
I am currently working to add support for playlists. Once that's done, perhaps I can work on albums. Adding playlist support is a priority though at the moment.
@jja88 Thanks for raising the request.
I am currently working to add support for playlists. Once that's done, perhaps I can work on albums. Adding playlist support is a priority though at the moment.
I've noticed that Albums are basically Playlists on Youtube for example https://music.youtube.com/playlist?list=OLAK5uy_l8N9VhE-C5aH8Cj0Lfn2nk3IJexBcN3EI https://www.youtube.com/playlist?list=OLAK5uy_l8N9VhE-C5aH8Cj0Lfn2nk3IJexBcN3EI
@jja88 If albums are indeed playlists, I am positive adding support for playlists will work for them! Thanks for letting me know about that though!
Nevermind, ignore this comment, ytmusicapi is a better way to go. I am too dumb to read.
The problem with youtube-music extraction is that you need to have logged-in access to that page which hosts your playlists,
https://music.youtube.com/library/playlists
gets you the account specific page on which there are multiple playlists, one of which is the Liked Music one. This one is by far the most difficult one to obtain as it uses a generic tag of LM and needs a user-account to obtain correctly.
Below is the hyperlink for a playlist from the https://music.youtube.com/library/playlists
page.
<ytmusic-two-row-item-renderer class="style-scope ytmusic-grid-renderer" aspect-ratio="MUSIC_TWO_ROW_ITEM_THUMBNAIL_ASPECT_RATIO_SQUARE"><!--css-build:shady--><a class="yt-simple-endpoint image-wrapper style-scope ytmusic-two-row-item-renderer" tabindex="-1" title="Your Likes" href="/playlist?list=LM">
<ytmusic-thumbnail-renderer class="image style-scope ytmusic-two-row-item-renderer" thumbnail-crop_="MUSIC_THUMBNAIL_CROP_UNSPECIFIED"><!--css-build:shady--><yt-img-shadow id="image" class="image style-scope ytmusic-thumbnail-renderer no-transition" object-fit="COVER" style="background-color: transparent;" loaded=""><!--css-build:shady--><img id="img" class="style-scope yt-img-shadow" alt="" width="226" src="https://www.gstatic.com/youtube/media/ytm/images/pbg/[email protected]"></yt-img-shadow>
</ytmusic-thumbnail-renderer>
<dom-if class="style-scope ytmusic-two-row-item-renderer"><template is="dom-if"></template></dom-if>
The webpage loads in chunks too needing to get more data every 50 songs or so, A full scroll (I tried it using selenium) or a reverse engineered api access would be needed.
Although I have a somewhat working implementation of the method described here, at https://github.com/DevParapalli/yt-music-playlist-extract/, I believe its better to let @deepjyoti30 impliment it correctly.
The song data is kept inside the tags properly, as in
<ytmusic-list-item-renderer class="style-scope ytmusic-shelf-renderer" play-button-state="default"><!--css-build:shady-->
<dom-if class="style-scope ytmusic-list-item-renderer"><template is="dom-if"></template></dom-if>
<div class="left-items style-scope ytmusic-list-item-renderer">
<ytmusic-item-thumbnail-overlay-renderer class="thumbnail-overlay style-scope ytmusic-list-item-renderer" indexed="" content-position="MUSIC_ITEM_THUMBNAIL_OVERLAY_CONTENT_POSITION_CENTERED" display-style="MUSIC_ITEM_THUMBNAIL_OVERLAY_DISPLAY_STYLE_PERSISTENT" play-button-state="default" animate-transitions_=""><!--css-build:shady--><ytmusic-background-overlay-renderer id="background" class="style-scope ytmusic-item-thumbnail-overlay-renderer" style="--ytmusic-background-overlay-background:linear-gradient(rgba(0,0,0,0),rgba(0,0,0,0));"><!--css-build:shady--></ytmusic-background-overlay-renderer>
<div id="content" class="style-scope ytmusic-item-thumbnail-overlay-renderer">
<ytmusic-play-button-renderer id="play-button" class="style-scope ytmusic-item-thumbnail-overlay-renderer" role="button" tabindex="0" animated="" state="default" aria-label="Play Hot Nickel Ball on a Pussy" size="MUSIC_PLAY_BUTTON_SIZE_SMALL" elevation="1" aria-disabled="false" style="--ytmusic-play-button-icon-color:rgba(255,255,255,1); --ytmusic-play-button-icon-loading-color:rgba(0,0,0,0); --ytmusic-play-button-background-color:rgba(0,0,0,0); --ytmusic-play-button-active-background-color:rgba(0,0,0,0); --ytmusic-play-button-loading-indicator-color:rgba(255,0,0,1); --ytmusic-play-button-active-scale-factor:1;"><!--css-build:shady--><div class="content-wrapper style-scope ytmusic-play-button-renderer">
<yt-icon class="icon style-scope ytmusic-play-button-renderer"><svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope yt-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope yt-icon"><path d="M8 5v14l11-7z" class="style-scope yt-icon"></path></g></svg><!--css-build:shady--></yt-icon>
<paper-spinner-lite class="loading-indicator style-scope ytmusic-play-button-renderer" hidden="" aria-hidden="true"><!--css-build:shady--><div id="spinnerContainer" class=" style-scope paper-spinner-lite"><div class="spinner-layer style-scope paper-spinner-lite"><div class="circle-clipper left style-scope paper-spinner-lite"><div class="circle style-scope paper-spinner-lite"></div></div><div class="circle-clipper right style-scope paper-spinner-lite"><div class="circle style-scope paper-spinner-lite"></div></div></div></div></paper-spinner-lite>
</div>
</ytmusic-play-button-renderer>
<dom-if class="style-scope ytmusic-item-thumbnail-overlay-renderer"><template is="dom-if"></template></dom-if>
</div>
</ytmusic-item-thumbnail-overlay-renderer>
<dom-if class="style-scope ytmusic-list-item-renderer"><template is="dom-if"></template></dom-if>
<div class="index style-scope ytmusic-list-item-renderer">1</div>
<yt-img-shadow class="thumbnail style-scope ytmusic-list-item-renderer no-transition" hidden="true" style="background-color: transparent;"><!--css-build:shady--><img id="img" class="style-scope yt-img-shadow" alt="" width="32" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"></yt-img-shadow>
<yt-icon class="error style-scope ytmusic-list-item-renderer" icon="error"><svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope yt-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope yt-icon"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" class="style-scope yt-icon"></path></g></svg><!--css-build:shady--></yt-icon>
</div>
<div class="title-wrapper style-scope ytmusic-list-item-renderer">
<div class="title style-scope ytmusic-list-item-renderer">Hot Nickel Ball on a Pussy</div>
<yt-icon class="explicit style-scope ytmusic-list-item-renderer" icon="yt-icons:music_explicit_badge"><svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope yt-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope yt-icon"><path d="M0 0h24v24H0z" fill="none" class="style-scope yt-icon"></path><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-4 6h-4v2h4v2h-4v2h4v2H9V7h6v2z" class="style-scope yt-icon"></path></g></svg><!--css-build:shady--></yt-icon>
</div>
<dom-if class="style-scope ytmusic-list-item-renderer"><template is="dom-if"></template></dom-if>
<div id="menu" class="menu style-scope ytmusic-list-item-renderer"><ytmusic-data-bound-menu-renderer class="style-scope ytmusic-list-item-renderer"><!--css-build:shady-->
<ytmusic-data-bound-top-level-menu-item class="style-scope ytmusic-data-bound-menu-renderer"><!--css-build:shady--><div id="contents" class="style-scope ytmusic-data-bound-top-level-menu-item"></div>
</ytmusic-data-bound-top-level-menu-item>
<dom-repeat class="style-scope ytmusic-data-bound-menu-renderer"><template is="dom-repeat"></template></dom-repeat>
<dom-if class="style-scope ytmusic-data-bound-menu-renderer"><template is="dom-if"></template></dom-if>
<ytmusic-menu-renderer class="style-scope ytmusic-data-bound-menu-renderer"><!--css-build:shady--><div id="top-level-buttons" class="style-scope ytmusic-menu-renderer"><ytmusic-like-button-renderer class="style-scope ytmusic-menu-renderer" like-status="INDIFFERENT"><!--css-build:shady-->
<tp-yt-paper-icon-button class="dislike style-scope ytmusic-like-button-renderer" title="Dislike" aria-label="Dislike" role="button" tabindex="0" aria-disabled="false" aria-pressed="false"><!--css-build:shady--><tp-yt-iron-icon id="icon" class="style-scope tp-yt-paper-icon-button"><svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope tp-yt-iron-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope tp-yt-iron-icon"><path d="M14.9 3H6c-.8 0-1.5.5-1.8 1.2l-3 7.3c-.1.2-.2.4-.2.7v2c0 1.1.9 2 2 2h6.3l-1 4.7v.3c0 .4.2.8.4 1.1.6.7 1.5.7 2.1.1l5.5-5.7c.4-.4.6-.9.6-1.4V5c0-1.1-.9-2-2-2zm-.2 12.6l-3.5 3.6c-.2.2-.5 0-.4-.2l1-4.6H4c-.6 0-1-.5-1-1v-1.1l2.7-6.6c.2-.5.6-.7 1-.7H14c.5 0 1 .5 1 1v8.8c-.1.3-.2.6-.3.8zM19 3h4v12h-4V3z" class="style-scope tp-yt-iron-icon"></path></g></svg><!--css-build:shady-->
</tp-yt-iron-icon></tp-yt-paper-icon-button>
<tp-yt-paper-icon-button class="like style-scope ytmusic-like-button-renderer" title="Like" aria-label="Like" role="button" tabindex="0" aria-disabled="false" aria-pressed="false"><!--css-build:shady--><tp-yt-iron-icon id="icon" class="style-scope tp-yt-paper-icon-button"><svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope tp-yt-iron-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope tp-yt-iron-icon"><path d="M9 21h9c.8 0 1.5-.5 1.8-1.2l3-7.3c.1-.2.2-.4.2-.7V9.7c0-1.1-.9-2.1-2-2.1h-6.3l1-4.7v-.3c0-.4-.2-.8-.4-1.1-.6-.6-1.5-.5-2.1.1L7.6 7.3c-.4.4-.6.9-.6 1.4V19c0 1.1.9 2 2 2zm.3-12.6l3.5-3.6c.2-.2.5 0 .4.2l-1 4.7H20c.6 0 1 .5 1 1v1l-2.7 6.7c-.2.3-.6.6-1 .6H10c-.6 0-1-.5-1-1V9.2c0-.4.1-.6.3-.8zM5 21H1V9h4v12z" class="style-scope tp-yt-iron-icon"></path></g></svg><!--css-build:shady-->
</tp-yt-iron-icon></tp-yt-paper-icon-button>
</ytmusic-like-button-renderer></div>
<tp-yt-paper-icon-button id="button" class="dropdown-trigger style-scope ytmusic-menu-renderer" icon="yt-icons:more_vert" title="More actions" aria-label="More actions" role="button" tabindex="0" aria-disabled="false"><!--css-build:shady--><tp-yt-iron-icon id="icon" class="style-scope tp-yt-paper-icon-button"><svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope tp-yt-iron-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope tp-yt-iron-icon"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" class="style-scope tp-yt-iron-icon"></path></g></svg><!--css-build:shady-->
</tp-yt-iron-icon></tp-yt-paper-icon-button>
<dom-if class="style-scope ytmusic-menu-renderer"><template is="dom-if"></template></dom-if>
</ytmusic-menu-renderer>
<dom-if class="style-scope ytmusic-data-bound-menu-renderer"><template is="dom-if"></template></dom-if>
</ytmusic-data-bound-menu-renderer></div>
<div class="duration style-scope ytmusic-list-item-renderer">2:19</div>
</ytmusic-list-item-renderer>
The html is parseable such that the path to duration would be
TAG:ytmusic-list-item-renderer -> TAG,CLASS:div,duration -> TEXT
@DevParapalli Thanks for the detailed explanation.
I understand you tried to extract YouTube Music playlist data using a script. Kudos on that. However, I just checked, basically YouTube Music uses same ID's for videos/playlists etc as the ones used by Youtube.
This means, if we are to extract a playlist from Youtube Music, we can easily use the ID and pass it as an Youtube URL. Since there are so many tools to extract playlists/videos from YouTube, it becomes way easier that way.
@DevParapalli @jja88 Just a confirmation that youtube music URL's actually do work with ytmdl
. I think a lot of users are actually using the functionality and it works neatly.
This comes down to the fact that the actual extractor of data is the underlying youtube-dl
(now yt-dlp
) package.
Since this is now confirmed as supported, I will close the issue.