Skip to content

Commit

Permalink
podcasts features ✨ (#559)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigma67 authored Mar 6, 2024
1 parent c412d7c commit 90d172b
Show file tree
Hide file tree
Showing 46 changed files with 802 additions and 318 deletions.
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Features

| **Library management**:
* get library contents: playlists, songs, artists, albums and subscriptions
* get library contents: playlists, songs, artists, albums and subscriptions, podcasts, channels
* add/remove library content: rate songs, albums and playlists, subscribe/unsubscribe artists
* get and modify play history

Expand All @@ -62,6 +62,7 @@ Features
* get podcasts
* get episodes
* get channels

| **Uploads**:
Expand Down
5 changes: 5 additions & 0 deletions docs/source/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ Library
.. automethod:: YTMusic.get_library_albums
.. automethod:: YTMusic.get_library_artists
.. automethod:: YTMusic.get_library_subscriptions
.. automethod:: YTMusic.get_library_podcasts
.. automethod:: YTMusic.get_library_channels
.. automethod:: YTMusic.get_liked_songs
.. automethod:: YTMusic.get_saved_episodes
.. automethod:: YTMusic.get_history
.. automethod:: YTMusic.add_history_item
.. automethod:: YTMusic.remove_history_items
Expand All @@ -74,6 +77,8 @@ Playlists

Podcasts
--------
.. automethod:: YTMusic.get_channel
.. automethod:: YTMusic.get_channel_episodes
.. automethod:: YTMusic.get_podcast
.. automethod:: YTMusic.get_episode

Expand Down
27 changes: 26 additions & 1 deletion pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,5 @@ dev = [
"mypy>=1.8.0",
"pytest>=7.4.4",
"pytest-cov>=4.1.0",
"types-requests>=2.31.0.20240218",
]
14 changes: 14 additions & 0 deletions tests/mixins/test_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ def test_get_library_subscriptions(self, config, yt_brand, yt_empty):
artists = yt_empty.get_library_subscriptions()
assert len(artists) == 0

def test_get_library_podcasts(self, yt_brand, yt_empty):
podcasts = yt_brand.get_library_podcasts(limit=50, order="a_to_z")
assert len(podcasts) > 25

empty = yt_empty.get_library_podcasts()
assert len(empty) == 1 # saved episodes playlist is always there

def test_get_library_channels(self, yt_brand, yt_empty):
channels = yt_brand.get_library_channels(limit=50, order="recently_added")
assert len(channels) > 25

empty = yt_empty.get_library_channels()
assert len(empty) == 0

def test_get_liked_songs(self, yt_brand, yt_empty):
songs = yt_brand.get_liked_songs(200)
assert len(songs["tracks"]) > 100
Expand Down
37 changes: 25 additions & 12 deletions tests/mixins/test_podcasts.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
class TestPodcasts:
def test_get_channel(self, config, yt):
podcast_id = config["podcasts"]["channel_id"]
channel = yt.get_channel(podcast_id)
assert len(channel["episodes"]["results"]) == 10
assert len(channel["podcasts"]["results"]) > 5

def test_get_channel_episodes(self, config, yt_oauth):
channel_id = config["podcasts"]["channel_id"]
channel = yt_oauth.get_channel(channel_id)
channel_episodes = yt_oauth.get_channel_episodes(channel_id, channel["episodes"]["params"])
assert len(channel_episodes) >= 150
assert len(channel_episodes[0]) == 9

def test_get_podcast(self, config, yt, yt_brand):
podcast_id = config["podcasts"]["podcast_id"]
results = yt.get_podcast(podcast_id)
assert len(results["episodes"]) == 100
assert not results["saved"]
podcast = yt.get_podcast(podcast_id)
assert len(podcast["episodes"]) == 100
assert not podcast["saved"]

results = yt_brand.get_podcast(podcast_id, limit=None)
assert len(results["episodes"]) > 100
assert results["saved"]
podcast = yt_brand.get_podcast(podcast_id, limit=None)
assert len(podcast["episodes"]) > 100
assert podcast["saved"]

def test_many_podcasts(self, yt):
results = yt.search("podcast", filter="podcasts")
Expand All @@ -17,13 +30,13 @@ def test_many_podcasts(self, yt):

def test_get_episode(self, config, yt, yt_brand):
episode_id = config["podcasts"]["episode_id"]
result = yt.get_episode(episode_id)
assert len(result["description"]) >= 20
assert not result["saved"]
assert result["playlistId"] is not None
episode = yt.get_episode(episode_id)
assert len(episode["description"]) >= 20
assert not episode["saved"]
assert episode["playlistId"] is not None

result = yt_brand.get_episode(episode_id)
assert result["saved"]
episode = yt_brand.get_episode(episode_id)
assert episode["saved"]

def test_many_episodes(self, yt):
results = yt.search("episode", filter="episodes")
Expand Down
14 changes: 11 additions & 3 deletions tests/setup/setup_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
yt_brand = YTMusic(Path(__file__).parent.joinpath("oauth.json").as_posix(), brand_account)


def populate_account():
def populate_music():
"""idempotent requests to populate an account"""
# library
playlist_id = "RDCLAK5uy_l9ex2d91-Qb1i-W7d0MLCEl_ZjRXss0Dk" # fixed playlist with many artists
Expand Down Expand Up @@ -53,10 +53,18 @@ def populate_account():
)
print(f"Created playlist {playlistId}, don't forget to set this in test.cfg playlists/own")

# podcasts

def populate_podcasts():
yt_brand.rate_playlist(config["podcasts"]["podcast_id"], rating="LIKE")
yt_brand.add_playlist_items("SE", [config["podcasts"]["episode_id"]])
podcasts = yt_brand.search("podcast", filter="podcasts", limit=40)
for podcast in podcasts:
playlist_id = podcast["browseId"][4:]
yt_brand.rate_playlist(playlist_id, rating="LIKE")
podcast = yt_brand.get_podcast(playlist_id)
yt_brand.subscribe_artists([podcast["author"]["id"]])


if __name__ == "__main__":
populate_account()
populate_music()
populate_podcasts()
5 changes: 5 additions & 0 deletions tests/test.example.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ private_album_id = sample_id_of_private_album
private_artist_id = sample_id_of_private_artist
private_upload_id = sample_video_id_of_private_upload

[podcasts]
channel_id = UCGwuxdEeCf0TIA2RbPOj-8g
podcast_id = PLxq_lXOUlvQDgCVFj9L79kqJybW0k6OaB
episode_id = KNkyHCLOr1o

[limits]
library_playlists = 100
library_songs = 200
Expand Down
Binary file modified ytmusicapi/locales/ar/LC_MESSAGES/base.mo
Binary file not shown.
36 changes: 22 additions & 14 deletions ytmusicapi/locales/ar/LC_MESSAGES/base.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-11 16:22+0100\n"
"POT-Creation-Date: 2024-03-05 20:13+0100\n"
"PO-Revision-Date: 2023-01-02 22:14+0530\n"
"Last-Translator: \n"
"Language-Team: \n"
Expand All @@ -17,54 +17,62 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.2.2\n"

#: parsers/i18n.py:16
#: parsers/i18n.py:22
msgid "artist"
msgstr "فنان"

#: parsers/i18n.py:16
#: parsers/i18n.py:23
msgid "playlist"
msgstr "قائمةالتشغيل"

#: parsers/i18n.py:16
#: parsers/i18n.py:24
msgid "song"
msgstr "أغنية"

#: parsers/i18n.py:16
#: parsers/i18n.py:25
msgid "video"
msgstr "فيديو"

#: parsers/i18n.py:16
#: parsers/i18n.py:26
msgid "station"
msgstr "محطة"

#: parsers/i18n.py:16
#: parsers/i18n.py:27
msgid "profile"
msgstr "الملف الشخصي"

#: parsers/i18n.py:16
#: parsers/i18n.py:28
msgid "podcast"
msgstr "بودكاست"

#: parsers/i18n.py:16
#: parsers/i18n.py:29
msgid "episode"
msgstr "حلقة"

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "albums"
msgstr "ألبومات"

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "singles"
msgstr "الفردي"

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "videos"
msgstr "أشرطة فيديو"

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "playlists"
msgstr "قوائم التشغيل"

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "related"
msgstr "ذات صلة"

#: parsers/i18n.py:35
msgid "episodes"
msgstr "أحدث الحلقات"

#: parsers/i18n.py:35
msgid "podcasts"
msgstr "بودكاست"
36 changes: 22 additions & 14 deletions ytmusicapi/locales/base.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-11 16:22+0100\n"
"POT-Creation-Date: 2024-03-05 20:13+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand All @@ -17,54 +17,62 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: parsers/i18n.py:16
#: parsers/i18n.py:22
msgid "artist"
msgstr ""

#: parsers/i18n.py:16
#: parsers/i18n.py:23
msgid "playlist"
msgstr ""

#: parsers/i18n.py:16
#: parsers/i18n.py:24
msgid "song"
msgstr ""

#: parsers/i18n.py:16
#: parsers/i18n.py:25
msgid "video"
msgstr ""

#: parsers/i18n.py:16
#: parsers/i18n.py:26
msgid "station"
msgstr ""

#: parsers/i18n.py:16
#: parsers/i18n.py:27
msgid "profile"
msgstr ""

#: parsers/i18n.py:16
#: parsers/i18n.py:28
msgid "podcast"
msgstr ""

#: parsers/i18n.py:16
#: parsers/i18n.py:29
msgid "episode"
msgstr ""

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "albums"
msgstr ""

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "singles"
msgstr ""

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "videos"
msgstr ""

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "playlists"
msgstr ""

#: parsers/i18n.py:21
#: parsers/i18n.py:35
msgid "related"
msgstr ""

#: parsers/i18n.py:35
msgid "episodes"
msgstr ""

#: parsers/i18n.py:35
msgid "podcasts"
msgstr ""
Binary file modified ytmusicapi/locales/de/LC_MESSAGES/base.mo
Binary file not shown.
Loading

0 comments on commit 90d172b

Please sign in to comment.