From 829b9369517a205578975191ec6732b34df1db54 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sat, 8 Aug 2020 00:22:48 +1200 Subject: [PATCH] Working code; channels and basic videos --- index.py | 188 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 + 2 files changed, 190 insertions(+) create mode 100644 index.py create mode 100644 requirements.txt diff --git a/index.py b/index.py new file mode 100644 index 0000000..235488b --- /dev/null +++ b/index.py @@ -0,0 +1,188 @@ +import cherrypy +import json +import youtube_dl +import datetime + +ytdl_opts = { + "quiet": True, + "dump_single_json": True, + "playlist_items": "1-100", + "extract_flat": "in_playlist" +} +ytdl = youtube_dl.YoutubeDL(ytdl_opts) + +class HelloWorld(object): + def _cp_dispatch(self, vpath): + if vpath[:2] == ["api", "v1"] and len(vpath) >= 4: + endpoints = ["channels", "videos"] + for e in endpoints: + if vpath[2] == e: + vpath[:3] = [e] + return self + + return vpath + + @cherrypy.expose + @cherrypy.tools.json_out() + def videos(self, id): + try: + info = ytdl.extract_info(id, download=False) + + year = int(info["upload_date"][:4]) + month = int(info["upload_date"][4:6]) + day = int(info["upload_date"][6:8]) + + def format_is_adaptive(format): + return format["acodec"] == "none" or format["vcodec"] == "none" + + def format_type(format): + sense = "audio" + codecs = [] + if format["vcodec"] != "none": + sense = "video" + codecs.append(format["vcodec"]) + if format["acodec"] != "none": + codecs.append(format["acodec"]) + return '{}/{}; codecs="{}"'.format(sense, format["ext"], ", ".join(codecs)) + + return { + "type": "video", + "title": info["title"], + "videoId": info["id"], + "videoThumbnails": None, + "storyboards": None, + "description": info["description"], + "descriptionHtml": None, + "published": int(datetime.datetime(year, month, day).timestamp()), + "publishedText": None, + "keywords": None, + "viewCount": info["view_count"], + "likeCount": info["like_count"], + "dislikeCount": info["dislike_count"], + "paid": None, + "premium": None, + "isFamilyFriendly": None, + "allowedRegions": [], + "genre": None, + "genreUrl": None, + "author": info["uploader"], + "authorId": info["channel_id"], + "authorUrl": info["channel_url"], + "second__uploaderId": info["uploader_id"], + "second__uploaderUrl": info["uploader_url"], + "authorThumbnails": [], + "subCountText": None, + "lengthSeconds": info["duration"], + "allowRatings": None, + "rating": info["average_rating"], + "isListed": None, + "liveNow": None, + "isUpcoming": None, + "dashUrl": None, + "adaptiveFormats": list({ + "index": None, + "bitrate": str(int(format["tbr"]*1000)), + "init": None, + "url": format["url"], + "itag": format["format_id"], + "type": format_type(format), + "clen": str(format["filesize"]), + "lmt": None, + "projectionType": None, + "fps": format["fps"], + "container": format["ext"], + "encoding": None, + "resolution": format["format_note"], + "qualityLabel": format["format_note"], + "second__width": format["width"], + "second__height": format["height"] + } for format in info["formats"] if format_is_adaptive(format)), + "formatStreams": list({ + "url": format["url"], + "itag": format["format_id"], + "type": format_type(format), + "quality": None, + "fps": format["fps"], + "container": format["ext"], + "encoding": None, + "resolution": format["format_note"], + "qualityLabel": format["format_note"], + "size": "{}x{}".format(format["width"], format["height"]), + "second__width": format["width"], + "second__height": format["height"] + } for format in info["formats"] if not format_is_adaptive(format)), + "captions": [], + "recommendedVideos": [] + } + + except youtube_dl.DownloadError: + return { + "error": "Video unavailable", + "identifier": "VIDEO_DOES_NOT_EXIST" + } + + @cherrypy.expose + @cherrypy.tools.json_out() + def channels(self, *suffix): + ucid = "" + part = "" + if len(suffix) == 1: + ucid = suffix[0] + else: # len(suffix) >= 2 + if suffix[0] == "videos" or suffix[0] == "latest": + [part, ucid] = suffix + else: + [ucid, part] = suffix + + try: + info = ytdl.extract_info("https://www.youtube.com/channel/{}".format(ucid), download=False) + + response = { + "author": info["uploader"], + "authorId": info["uploader_id"], + "authorUrl": info["uploader_url"], + "authorBanners": [], + "authorThumbnails": [], + "subCount": None, + "totalViews": None, + "joined": None, + "paid": None, + "autoGenerated": None, + "isFamilyFriendly": None, + "description": None, + "descriptionHtml": None, + "allowedRegions": [], + "latestVideos": list({ + "type": "video", + "title": video["title"], + "videoId": video["id"], + "author": info["uploader"], + "authorId": info["uploader_id"], + "authorUrl": info["uploader_url"], + "videoThumbnails": [], + "description": None, + "descriptionHtml": None, + "viewCount": None, + "published": None, + "publishedText": None, + "lengthSeconds": None, + "liveNow": None, + "paid": None, + "premium": None, + "isUpcoming": None + } for video in info["entries"]), + "relatedChannels": [] + } + + if part == "videos" or part == "latest": + return response["latestVideos"] + else: + return response + + except youtube_dl.DownloadError: + return { + "error": "This channel does not exist.", + "identifier": "CHANNEL_DOES_NOT_EXIST" + } + +cherrypy.quickstart(HelloWorld()) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..076c8cc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +cherrypy +youtube-dl