mirror of
https://git.sr.ht/~cadence/NewLeaf
synced 2024-11-22 15:47:30 +00:00
Add recommended videos in a terrible way
This commit is contained in:
parent
132f814f3a
commit
ad018b0a0c
@ -14,7 +14,6 @@ These endpoints are somewhat implemented:
|
|||||||
|
|
||||||
## The future
|
## The future
|
||||||
|
|
||||||
- Video recommendations
|
|
||||||
- RSS as a source for channel listings
|
- RSS as a source for channel listings
|
||||||
- Searches
|
- Searches
|
||||||
- Dash manifests
|
- Dash manifests
|
||||||
|
89
index.py
89
index.py
@ -2,6 +2,10 @@ import cherrypy
|
|||||||
import json
|
import json
|
||||||
import youtube_dl
|
import youtube_dl
|
||||||
import datetime
|
import datetime
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import traceback
|
||||||
|
|
||||||
ytdl_opts = {
|
ytdl_opts = {
|
||||||
"quiet": True,
|
"quiet": True,
|
||||||
@ -11,7 +15,15 @@ ytdl_opts = {
|
|||||||
}
|
}
|
||||||
ytdl = youtube_dl.YoutubeDL(ytdl_opts)
|
ytdl = youtube_dl.YoutubeDL(ytdl_opts)
|
||||||
|
|
||||||
class HelloWorld(object):
|
ytdl_save_opts = ytdl_opts.copy()
|
||||||
|
ytdl_save_opts["write_pages"] = True
|
||||||
|
ytdl_save = youtube_dl.YoutubeDL(ytdl_save_opts)
|
||||||
|
|
||||||
|
def length_text_to_seconds(text):
|
||||||
|
s = text.split(":")
|
||||||
|
return sum([int(x) * 60**(len(s)-i-1) for i, x in enumerate(s)])
|
||||||
|
|
||||||
|
class Second(object):
|
||||||
def _cp_dispatch(self, vpath):
|
def _cp_dispatch(self, vpath):
|
||||||
if vpath[:2] == ["api", "v1"] and len(vpath) >= 4:
|
if vpath[:2] == ["api", "v1"] and len(vpath) >= 4:
|
||||||
endpoints = ["channels", "videos"]
|
endpoints = ["channels", "videos"]
|
||||||
@ -26,15 +38,17 @@ class HelloWorld(object):
|
|||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
def videos(self, id):
|
def videos(self, id):
|
||||||
try:
|
try:
|
||||||
info = ytdl.extract_info(id, download=False)
|
info = ytdl_save.extract_info(id, download=False)
|
||||||
|
|
||||||
year = int(info["upload_date"][:4])
|
year = int(info["upload_date"][:4])
|
||||||
month = int(info["upload_date"][4:6])
|
month = int(info["upload_date"][4:6])
|
||||||
day = int(info["upload_date"][6:8])
|
day = int(info["upload_date"][6:8])
|
||||||
|
|
||||||
|
# Adaptive formats have either audio or video, format streams have both
|
||||||
def format_is_adaptive(format):
|
def format_is_adaptive(format):
|
||||||
return format["acodec"] == "none" or format["vcodec"] == "none"
|
return format["acodec"] == "none" or format["vcodec"] == "none"
|
||||||
|
|
||||||
|
# just the "type" field
|
||||||
def format_type(format):
|
def format_type(format):
|
||||||
sense = "audio"
|
sense = "audio"
|
||||||
codecs = []
|
codecs = []
|
||||||
@ -45,7 +59,7 @@ class HelloWorld(object):
|
|||||||
codecs.append(format["acodec"])
|
codecs.append(format["acodec"])
|
||||||
return '{}/{}; codecs="{}"'.format(sense, format["ext"], ", ".join(codecs))
|
return '{}/{}; codecs="{}"'.format(sense, format["ext"], ", ".join(codecs))
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
"type": "video",
|
"type": "video",
|
||||||
"title": info["title"],
|
"title": info["title"],
|
||||||
"videoId": info["id"],
|
"videoId": info["id"],
|
||||||
@ -57,6 +71,8 @@ class HelloWorld(object):
|
|||||||
"publishedText": None,
|
"publishedText": None,
|
||||||
"keywords": None,
|
"keywords": None,
|
||||||
"viewCount": info["view_count"],
|
"viewCount": info["view_count"],
|
||||||
|
"second__viewCountText": None,
|
||||||
|
"second__viewCountTextShort": None,
|
||||||
"likeCount": info["like_count"],
|
"likeCount": info["like_count"],
|
||||||
"dislikeCount": info["dislike_count"],
|
"dislikeCount": info["dislike_count"],
|
||||||
"paid": None,
|
"paid": None,
|
||||||
@ -115,6 +131,71 @@ class HelloWorld(object):
|
|||||||
"recommendedVideos": []
|
"recommendedVideos": []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Now try to get more stuff by manually examining the saved file
|
||||||
|
# Figure out what the name of the saved file was
|
||||||
|
possible_files = [f for f in os.listdir() if f.startswith("{}_".format(info["id"]))]
|
||||||
|
try:
|
||||||
|
if len(possible_files) == 1:
|
||||||
|
filename = possible_files[0]
|
||||||
|
with open(filename) as file:
|
||||||
|
r = re.compile(r"""^\s*window\["ytInitialData"\] = (\{.*\});\n?$""")
|
||||||
|
for line in file:
|
||||||
|
match_result = re.search(r, line)
|
||||||
|
if match_result:
|
||||||
|
yt_initial_data = json.loads(match_result.group(1))
|
||||||
|
views = yt_initial_data["contents"]["twoColumnWatchNextResults"]["results"]["results"]["contents"][0]\
|
||||||
|
["videoPrimaryInfoRenderer"]["viewCount"]["videoViewCountRenderer"]
|
||||||
|
result["second__viewCountText"] = views["viewCount"]["simpleText"]
|
||||||
|
result["second__viewCountTextShort"] = views["shortViewCount"]["simpleText"]
|
||||||
|
recommendations = yt_initial_data["contents"]["twoColumnWatchNextResults"]["secondaryResults"]\
|
||||||
|
["secondaryResults"]["results"]
|
||||||
|
|
||||||
|
def get_useful_recommendation_data(r):
|
||||||
|
if "compactVideoRenderer" in r:
|
||||||
|
return r["compactVideoRenderer"]
|
||||||
|
if "compactAutoplayRenderer" in r:
|
||||||
|
return r["compactAutoplayRenderer"]["contents"][0]["compactVideoRenderer"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_view_count(r):
|
||||||
|
text = r["viewCountText"]["simpleText"]
|
||||||
|
if text == "Recommended for you":
|
||||||
|
return 0 # subject to change?
|
||||||
|
else:
|
||||||
|
return int(text.replace(",", "").split(" ")[0])
|
||||||
|
|
||||||
|
def get_view_count_text(r):
|
||||||
|
text = r["viewCountText"]["simpleText"]
|
||||||
|
if text == "Recommended for you":
|
||||||
|
return "Recommended for you" # subject to change?
|
||||||
|
else:
|
||||||
|
return text
|
||||||
|
|
||||||
|
# result["recommendedVideos"] = recommendations
|
||||||
|
# return result
|
||||||
|
|
||||||
|
result["recommendedVideos"] = list({
|
||||||
|
"videoId": r["videoId"],
|
||||||
|
"title": r["title"]["simpleText"],
|
||||||
|
"videoThumbnails": [],
|
||||||
|
"author": r["longBylineText"]["runs"][0]["text"],
|
||||||
|
"authorUrl": r["longBylineText"]["runs"][0]["navigationEndpoint"]["browseEndpoint"]["canonicalBaseUrl"],
|
||||||
|
"authorId": r["longBylineText"]["runs"][0]["navigationEndpoint"]["browseEndpoint"]["browseId"],
|
||||||
|
"lengthSeconds": length_text_to_seconds(r["lengthText"]["simpleText"]),
|
||||||
|
"second__lengthText": r["lengthText"]["simpleText"],
|
||||||
|
"viewCountText": get_view_count_text(r),
|
||||||
|
"viewCount": get_view_count(r)
|
||||||
|
} for r in [get_useful_recommendation_data(r) for r in recommendations if get_useful_recommendation_data(r)])
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
for file in possible_files:
|
||||||
|
os.unlink(file)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
except youtube_dl.DownloadError:
|
except youtube_dl.DownloadError:
|
||||||
return {
|
return {
|
||||||
"error": "Video unavailable",
|
"error": "Video unavailable",
|
||||||
@ -186,4 +267,4 @@ class HelloWorld(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
cherrypy.config.update({"server.socket_port": 3000})
|
cherrypy.config.update({"server.socket_port": 3000})
|
||||||
cherrypy.quickstart(HelloWorld())
|
cherrypy.quickstart(Second())
|
||||||
|
Loading…
Reference in New Issue
Block a user