documented the renderer section
This commit is contained in:
parent
db6d5d9e4c
commit
d819450e4b
File diff suppressed because it is too large
Load Diff
8040
documentation/html/youtube-music/search/general-result.json
Normal file
8040
documentation/html/youtube-music/search/general-result.json
Normal file
File diff suppressed because it is too large
Load Diff
59
documentation/html/youtube-music/search/search.md
Normal file
59
documentation/html/youtube-music/search/search.md
Normal 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. ...
|
1
src/music_kraken/pages/youtube_music/__init__.py
Normal file
1
src/music_kraken/pages/youtube_music/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .youtube_music import YoutubeMusic
|
@ -6,14 +6,16 @@ import json
|
||||
from dataclasses import dataclass
|
||||
import re
|
||||
|
||||
from ..utils.exception.config import SettingValueError
|
||||
from ..utils.shared import PROXIES_LIST, YOUTUBE_MUSIC_LOGGER, DEBUG
|
||||
from ..utils.config import CONNECTION_SECTION, write_config
|
||||
from ..utils.functions import get_current_millis
|
||||
from ...utils.exception.config import SettingValueError
|
||||
from ...utils.shared import PROXIES_LIST, YOUTUBE_MUSIC_LOGGER, DEBUG
|
||||
from ...utils.config import CONNECTION_SECTION, write_config
|
||||
from ...utils.functions import get_current_millis
|
||||
if DEBUG:
|
||||
from ...utils.debug_utils import dump_to_file
|
||||
|
||||
from ..objects import Source, DatabaseObject
|
||||
from .abstract import Page
|
||||
from ..objects import (
|
||||
from ...objects import Source, DatabaseObject
|
||||
from ..abstract import Page
|
||||
from ...objects import (
|
||||
Artist,
|
||||
Source,
|
||||
SourcePages,
|
||||
@ -22,8 +24,8 @@ from ..objects import (
|
||||
Label,
|
||||
Target
|
||||
)
|
||||
from ..connection import Connection
|
||||
from ..utils.support_classes import DownloadResult
|
||||
from ...connection import Connection
|
||||
from ...utils.support_classes import DownloadResult
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
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__(
|
||||
host="https://music.youtube.com/",
|
||||
logger=logger,
|
||||
hearthbeat_interval=113.25,
|
||||
header_values={
|
||||
"Accept-Language": accept_language
|
||||
}
|
||||
)
|
||||
|
||||
# cookie consent for youtube
|
||||
@ -88,7 +94,7 @@ class YoutubeMusic(Page):
|
||||
LOGGER = YOUTUBE_MUSIC_LOGGER
|
||||
|
||||
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(
|
||||
api_key=CONNECTION_SECTION.YOUTUBE_MUSIC_API_KEY.object_from_value,
|
||||
ctoken="",
|
||||
@ -214,6 +220,7 @@ class YoutubeMusic(Page):
|
||||
return super().get_source_type(source)
|
||||
|
||||
def general_search(self, search_query: str) -> List[DatabaseObject]:
|
||||
search_query = search_query.strip()
|
||||
self.LOGGER.info(f"general search for {search_query}")
|
||||
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 [
|
||||
Song(title="Lore Ipsum")
|
18
src/music_kraken/utils/debug_utils.py
Normal file
18
src/music_kraken/utils/debug_utils.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user