documented the renderer section

This commit is contained in:
Hellow 2023-07-27 23:05:49 +02:00
parent db6d5d9e4c
commit d819450e4b
6 changed files with 8144 additions and 7738 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
# Search
## Files
what is it | query | file
---|---|---
general search response | `psychonaut 4`, [general-result.json](general-result.json)
## A general search yields
- **Top Result**
- The top Artist
- The most popular songs of said artist
- **Songs** (3) excluding the top songs
- Videos (3)
- **Albums** (3)
- Community playlists (3)
- **Artists** (3) excluding the top artist
- if you search for a artist, it might return simmilar artists in style, not in name
### Different Renderers
#### `runs`
This should be pretty consistently all over the response be parsebal to a list of Music Elements.
`runs` usually is a list. If a element of the list has the key `navigationEndpoint`, it represents a music elements in a following manner:
- `text` the name
- `navigationEndpoint` -> `browseEndpoint`
- `browseId` the id of the artist/song/album...
- `browseEndpointContextSupportedConfigs` -> `browseEndpointContextMusicConfig` -> `pageType` the type of the header like element
#### musicCardShelfRenderer
Used by e.g. the `Top Results`.
Contains:
- One Main-Element (a header like music object) | consists of these keys:
- `thumbnail` the image of the header
- `title` -> `runs`
- for details look [here](#runs).
### Details
You can get the contents (a list of [renderers](#musiccardshelfrenderer)) this way:
```python
data = r.json().get("contents", {}).get("tabbedSearchResultsRenderer", {}).get("tabs", [{}])[0].get("tabRenderer").get("content", {}).get("sectionListRenderer", {}).get("contents", [])
```
Then the list contains following items, in following order:
1. _About these results_ (an infobutton)
2. The **Top result**
3. The **Songs** [_musicShelfRenderer_]
4. ...

View File

@ -0,0 +1 @@
from .youtube_music import YoutubeMusic

View File

@ -6,14 +6,16 @@ import json
from dataclasses import dataclass from dataclasses import dataclass
import re import re
from ..utils.exception.config import SettingValueError from ...utils.exception.config import SettingValueError
from ..utils.shared import PROXIES_LIST, YOUTUBE_MUSIC_LOGGER, DEBUG from ...utils.shared import PROXIES_LIST, YOUTUBE_MUSIC_LOGGER, DEBUG
from ..utils.config import CONNECTION_SECTION, write_config from ...utils.config import CONNECTION_SECTION, write_config
from ..utils.functions import get_current_millis from ...utils.functions import get_current_millis
if DEBUG:
from ...utils.debug_utils import dump_to_file
from ..objects import Source, DatabaseObject from ...objects import Source, DatabaseObject
from .abstract import Page from ..abstract import Page
from ..objects import ( from ...objects import (
Artist, Artist,
Source, Source,
SourcePages, SourcePages,
@ -22,8 +24,8 @@ from ..objects import (
Label, Label,
Target Target
) )
from ..connection import Connection from ...connection import Connection
from ..utils.support_classes import DownloadResult from ...utils.support_classes import DownloadResult
def get_youtube_url(path: str = "", params: str = "", query: str = "", fragment: str = "") -> str: def get_youtube_url(path: str = "", params: str = "", query: str = "", fragment: str = "") -> str:
@ -41,11 +43,15 @@ class YoutubeMusicConnection(Connection):
--> average delay in between: 1.8875 min --> average delay in between: 1.8875 min
""" """
def __init__(self, logger: logging.Logger): def __init__(self, logger: logging.Logger, accept_language: str):
# https://stackoverflow.com/questions/30561260/python-change-accept-language-using-requests
super().__init__( super().__init__(
host="https://music.youtube.com/", host="https://music.youtube.com/",
logger=logger, logger=logger,
hearthbeat_interval=113.25, hearthbeat_interval=113.25,
header_values={
"Accept-Language": accept_language
}
) )
# cookie consent for youtube # cookie consent for youtube
@ -88,7 +94,7 @@ class YoutubeMusic(Page):
LOGGER = YOUTUBE_MUSIC_LOGGER LOGGER = YOUTUBE_MUSIC_LOGGER
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.connection: YoutubeMusicConnection = YoutubeMusicConnection(logger=self.LOGGER) self.connection: YoutubeMusicConnection = YoutubeMusicConnection(logger=self.LOGGER, accept_language="en-US,en;q=0.5")
self.credentials: YouTubeMusicCredentials = YouTubeMusicCredentials( self.credentials: YouTubeMusicCredentials = YouTubeMusicCredentials(
api_key=CONNECTION_SECTION.YOUTUBE_MUSIC_API_KEY.object_from_value, api_key=CONNECTION_SECTION.YOUTUBE_MUSIC_API_KEY.object_from_value,
ctoken="", ctoken="",
@ -214,6 +220,7 @@ class YoutubeMusic(Page):
return super().get_source_type(source) return super().get_source_type(source)
def general_search(self, search_query: str) -> List[DatabaseObject]: def general_search(self, search_query: str) -> List[DatabaseObject]:
search_query = search_query.strip()
self.LOGGER.info(f"general search for {search_query}") self.LOGGER.info(f"general search for {search_query}")
print(self.credentials) print(self.credentials)
@ -249,7 +256,14 @@ class YoutubeMusic(Page):
} }
) )
print(r) self.LOGGER.debug(str(r))
# dump_to_file(f"{search_query}.json", r.content, is_json=True)
renderer_list = r.json().get("contents", {}).get("tabbedSearchResultsRenderer", {}).get("tabs", [{}])[0].get("tabRenderer").get("content", {}).get("sectionListRenderer", {}).get("contents", [])
if DEBUG:
for i, content in enumerate(renderer_list):
dump_to_file(f"{i}-renderer.json", json.dumps(content), is_json=True, exit_after_dump=False)
return [ return [
Song(title="Lore Ipsum") Song(title="Lore Ipsum")

View File

@ -0,0 +1,18 @@
from pathlib import Path
import json
from .path_manager import LOCATIONS
def dump_to_file(file_name: str, payload: str, is_json: bool = False, exit_after_dump: bool = True):
path = Path(LOCATIONS.TEMP_DIRECTORY, file_name)
print(f"Dumping payload to: \"{path}\"")
if is_json:
payload = json.dumps(json.loads(payload), indent=4)
with path.open("w") as f:
f.write(payload)
if exit_after_dump:
exit()