Verified Commit b61bd81d authored by Javinator9889's avatar Javinator9889 🎼

YouTube search uses the YouTube API class

There is a new behaviour defined: on `YouTubeVideoData` it is possible to ignore errors and fallback to default values (`None`). So, for YouTube search results in `YouTubeAPI` class now can be merged and combined with the `YouTubeVideoData` class
parent f1a7c4b4
Pipeline #71 passed with stage
in 6 minutes and 56 seconds
......@@ -19,35 +19,70 @@ except ImportError:
import json
from urllib.request import urlopen
import isodate
from googleapiclient.discovery import build
from isodate import parse_duration
from ..constants import YOUTUBE
from ..errors import EmptyBodyError
class YouTubeVideoData(object):
def __init__(self, data: dict):
def __init__(self, data: dict, ignore_errors: bool = False):
if not data.get("items"):
raise EmptyBodyError("The data object has no items") = None
self.title = None
self.thumbnail = None
self.artist = None
self.duration = None
self.views = None
self.likes = None
self.dislikes = None
if len(data.get("items")) >= 1:
content = data.get("items")[0]
snippet = content.get("snippet")
details = content.get("contentDetails")
statistics = content.get("statistics")
if not snippet:
if not snippet and not ignore_errors:
raise EmptyBodyError("No information available to requested video")
if not details:
elif not snippet and ignore_errors:
snippet_available = False
snippet_available = True
if not details and not ignore_errors:
raise EmptyBodyError("No video details available")
if not statistics:
elif not details and ignore_errors:
details_available = False
details_available = True
if not statistics and not ignore_errors:
raise EmptyBodyError("No statistics available")
self.title = snippet["title"]
self.thumbnail = snippet["thumbnails"]["maxres"]["url"]
self.artist = snippet["channelTitle"]
self.duration = isodate.parse_duration(details["duration"]).total_seconds()
self.view = int(statistics["viewCount"]) = int(statistics["likeCount"])
self.dislike = int(statistics["dislikeCount"])
elif not statistics and ignore_errors:
statistics_available = False
statistics_available = True = content["id"]
if isinstance(, dict): ="videoId")
if snippet_available:
self.title = snippet["title"]
self.thumbnail = snippet["thumbnails"]["maxres"]["url"]
except KeyError:
self.thumbnail = snippet["thumbnails"]["high"]["url"]
except KeyError:
self.thumbnail = snippet["thumbnails"]["medium"]["url"]
except KeyError:
self.thumbnail = snippet["thumbnails"]["default"]["url"]
self.artist = snippet["channelTitle"]
if details_available:
self.duration = parse_duration(details["duration"]).total_seconds()
if statistics_available:
self.views = int(statistics["viewCount"])
self.likes = int(statistics["likeCount"])
self.dislikes = int(statistics["dislikeCount"])
class YouTubeAPI(object):
......@@ -67,6 +102,5 @@ class YouTubeAPI(object):
def video_details(video_id: str) -> YouTubeVideoData:
api_url = YOUTUBE["endpoint"].format(video_id, YOUTUBE["key"])
data = urlopen(url=api_url)
return YouTubeVideoData(data=json.loads(
......@@ -39,6 +39,7 @@ class MetadataIdentifier(object):
self.score: float = 0.0
self.cover: bytes = bytes(0)
self.duration: int = 0
self.youtube_data: bool = False
self._downloader = downloader
......@@ -62,8 +63,6 @@ class MetadataIdentifier(object):
meta="recordings releaseids")
self.result = data
if self._is_valid_result(data):
for result in data["results"]:
if "recordings" not in result:
......@@ -91,4 +90,5 @@ class MetadataIdentifier(object):
self.artist = video_data.artist
self.duration = video_data.duration
self.cover = urlopen(video_data.thumbnail).read()
self.youtube_data = True
return data
......@@ -75,13 +75,17 @@ class IdentifierTest(unittest.TestCase):
self.song_info[downloader.get_url()] = {
"title": identifier.title,
"artist": identifier.artist,
"score": identifier.score,
"record_id": "{0}"
"release_id": "{0}"
"artist": identifier.artist
if not identifier.youtube_data:
self.song_info[downloader.get_url()]["score"] = identifier.score
self.song_info[downloader.get_url()]["record_id"] = \
self.song_info[downloader.get_url()]["release_id"] = \
self.song_info[downloader.get_url()]["duration"] = identifier.duration
self.song_info[downloader.get_url()]["youtube_data"] = True
import unittest
from YouTubeMDBot.api import YouTubeAPI
from YouTubeMDBot.api import YouTubeVideoData
class TestSearch(unittest.TestCase):
def test_search(self):
s = YouTubeAPI()
search: dict ="test")
data = YouTubeVideoData(data=search, ignore_errors=True)
print("Title: {0}\n"
"Artist: {1}\n"
"Thumbnail: {2}\n"
"Duration: {3}\n"
"Views: {4}\n"
"Likes: {5}\n"
"Dislikes: {6}\n"
"Id: {7}".format(data.title, data.artist, data.thumbnail,
data.duration, data.views, data.likes,
if __name__ == '__main__':
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment