Guest User

op-helper.py v0.5 by C-Anon

a guest
Nov 28th, 2022
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.78 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # op-helper.py for making threads on /vt/
  3. # Released under MIT License, software is provided as-is and all that crap.
  4. # v0.5 by C-Anon
  5.  
  6. import re, json, urllib.request
  7. import datetime, time, os
  8.  
  9. ##########################
  10. #   CONFIGURATION
  11.  
  12. # Ignore streams if they're set this amount of days from now (or more)
  13. ignore_days = 10
  14.  
  15. # Sort streams by starting timestamp
  16. sort_streams = False
  17.  
  18. # Show stream titles
  19. show_live_titles = False
  20. show_upcoming_titles = False
  21.  
  22. # Show timezones
  23. show_timezones = False
  24.  
  25. # If true, data is output to a file instead of the terminal
  26. output_to_file = False
  27. outfname = 'op-helper.txt'
  28.  
  29. # Quiet mode, don't show "retrieving channel..."
  30. quiet_mode = True
  31.  
  32. # Channel data
  33. #   The format is channel ID followed by a space, then followed
  34. #   by the name of the channel. Empty lines or lines starting
  35. #   with a # are ignored.
  36. #   If you have a channel name instead of a channel ID, you can
  37. #   specify it as /c/ChannelName
  38. #   You can also add a channel with its custom name like @ChannelName
  39. channel_data = """
  40. UC8P3OkWBUsrk93f1pV0b5bQ Ageha Himeragi
  41. UCEnhASxlFG-ZPCpYDTYIQZA Charo Nemurime
  42. UCAx0YWXJgyvXx5oDvrDaN_A Himari Inumaki
  43. UCvm34tgJ0ZaKzTinWVyXpcA Himea D'Almaria
  44. UC6tSB9TnO0f01OBeo9UEJZA Hina Misora
  45. UCVtuciDzkjxCP_O7r-0QhXQ Ito Shinonome
  46. UCJePO0Zl-zZTqjpHO82RNNA Lia Mitsurugi
  47. UCN3mosAMYBdogyQovOhPrxA Luna Rurine
  48. UClXfBZMVxt-JgNMaHGr5NMQ Mahiru Kumaboshi
  49. UCUUjykb68Lf85CbWX6gAmog Mireille Kuuma
  50. UCM6iy_rSgSMbFjx10Z6VVGA Miu Hizuki
  51. UCQdqGzhc5Ey_5nh_bOowAhg Pal Sumeragi
  52. UCIm8pnnTNhCgGAtNxrQQv-g Yue Saohime
  53. @NeonKuroyuri Neon Kuroyuri
  54.  
  55. # Honorary members
  56.  
  57. # UCzkwiov0OShlyRPdzwf0Xrg Hana Yomeguri
  58. # UC_ulAd_3TQIlLRAo5zNhnoQ Hyoga Yome
  59. # /c/MerunCh森野めるん Merun
  60. # UCrAsByHNIFObjr2sEaZHkCg Otsuka Ray
  61. # /c/冬原そのSonoFuyuhara_seitokaicho Sono Fuyuhara
  62. """
  63.  
  64. # Timezone data
  65. #   You can specify timezones to display along with the other information.
  66. #   The format is the name of the timezone followed by a space, then followed
  67. #   by the number of hours from UTC (e.g. Mexico City timezone is GMT-5 in summer)
  68. #
  69. #   This might need to be adjusted as countries adopt and abandon daylight savings time
  70. #   throughout the year.
  71. timezone_data = """
  72. MEX -5
  73. ARG -3
  74. ESP 2
  75. JAP 9
  76. """
  77.  
  78. #   END OF CONFIGURATION
  79. ##########################
  80.  
  81. now = time.time
  82.  
  83. def getStreams(channel_id, channel_owner):
  84.     if re.match("^/c/.+", channel_id):
  85.         url = 'https://www.youtube.com/%s/' % (channel_id)
  86.     elif re.match("^(?:@|%40).+", channel_id):
  87.         url = 'https://www.youtube.com/%s' % (channel_id)
  88.     else:
  89.         url = 'https://www.youtube.com/channel/%s/' % (channel_id)
  90.  
  91.     headers = {
  92.         'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0',
  93.         'Accept-Language': 'en-US,en;q=0.5'
  94.     }
  95.     try:
  96.         req = urllib.request.Request(url, headers=headers)
  97.         conn = urllib.request.urlopen(req)
  98.     except (urllib.error.URLError, ValueError) as e:
  99.         # print("Error! Couldn't retrieve URL. ", str(e).capitalize())
  100.         return None
  101.     response = conn.read().decode('utf-8')
  102.     temp = re.findall('<script nonce="[^"]+">var ytInitialData = (.*?);</script>', response)
  103.     if not temp:
  104.         return None
  105.  
  106.     streams = {}
  107.     streamIds = []
  108.     json_obj = json.loads(temp[0])
  109.     first_tab = json_obj['contents']['twoColumnBrowseResultsRenderer']['tabs'][0]
  110.     for section in first_tab['tabRenderer']['content']['sectionListRenderer']['contents']:
  111.         if 'shelfRenderer' not in section['itemSectionRenderer']['contents'][0] and 'channelFeaturedContentRenderer' not in section['itemSectionRenderer']['contents'][0]:
  112.             continue
  113.         if 'shelfRenderer' in section['itemSectionRenderer']['contents'][0]:
  114.             shelf_type = section['itemSectionRenderer']['contents'][0]['shelfRenderer']['title']['runs'][0]['text']
  115.         else:
  116.             shelf_type = 'Featured'
  117.  
  118.         if  shelf_type not in ['Featured', 'Live now', 'Upcoming live streams']:
  119.             continue
  120.  
  121.         if shelf_type == 'Featured':
  122.             row = section['itemSectionRenderer']['contents'][0]['channelFeaturedContentRenderer']['items']
  123.             vrname = 'videoRenderer'
  124.         else:
  125.             if 'expandedShelfContentsRenderer' not in section['itemSectionRenderer']['contents'][0]['shelfRenderer']['content']:
  126.                 row = section['itemSectionRenderer']['contents'][0]['shelfRenderer']['content']['horizontalListRenderer']['items']
  127.                 vrname = 'gridVideoRenderer'
  128.             else:
  129.                 row = section['itemSectionRenderer']['contents'][0]['shelfRenderer']['content']['expandedShelfContentsRenderer']['items']
  130.                 vrname = 'videoRenderer'
  131.  
  132.         for elem in row:
  133.             if elem[vrname]['videoId'] in streamIds:
  134.                 continue
  135.             new_stream = {'videoId': '', 'type'   : '', 'start'  : '', 'title'  : '', 'owner':''}
  136.             if shelf_type == "Live now":
  137.                 new_stream['videoId'] = elem[vrname]['videoId']
  138.                 new_stream['type']    = "live"
  139.                 new_stream['start']   = ""
  140.                 new_stream['owner']   = channel_owner
  141.                 new_stream['title']   = elem[vrname]['title']['simpleText']
  142.                 if 'live' not in streams:
  143.                     streams['live'] = [new_stream]
  144.                 else:
  145.                     streams['live'].append(new_stream)
  146.             elif shelf_type == "Upcoming live streams":
  147.                 if 'upcomingEventData' not in elem[vrname]:
  148.                     continue
  149.                 new_stream['videoId'] = elem[vrname]['videoId']
  150.                 new_stream['type']    = "upcoming"
  151.                 new_stream['start']   = elem[vrname]['upcomingEventData']['startTime']
  152.                 new_stream['owner']   = channel_owner
  153.                 new_stream['title']   = elem[vrname]['title']['simpleText']
  154.                 if 'upcoming' not in streams:
  155.                     streams['upcoming'] = [new_stream]
  156.                 else:
  157.                     streams['upcoming'].append(new_stream)
  158.             elif shelf_type == "Featured":
  159.                 new_stream['videoId'] = elem[vrname]['videoId']
  160.                 new_stream['type']    = "live"
  161.                 new_stream['start']   = ""
  162.                 new_stream['owner']   = channel_owner
  163.                 new_stream['title']   = elem[vrname]['title']['runs'][0]['text']
  164.                 if 'live' not in streams:
  165.                     streams['live'] = [new_stream]
  166.                 else:
  167.                     streams['live'].append(new_stream)
  168.             streamIds.append(elem[vrname]['videoId'])
  169.     return streams
  170.  
  171. def getTimeString(ts):
  172.     ret = ""
  173.     last_date = ""
  174.     for tz in timezones:
  175.         dts = ts + (tz['delta'] * 3600)
  176.         dts = datetime.datetime.utcfromtimestamp(dts)
  177.         date = datetime.datetime.strftime(dts, '%Y-%m-%d')
  178.         if last_date != date:
  179.             ret += date + " "
  180.         ret += datetime.datetime.strftime(dts, '%H:%M') + " " + tz['name'] + " "
  181.         last_date = date
  182.     return ret
  183.  
  184. def log(txt):
  185.     if output_to_file and fp:
  186.         fp.write(txt + "\n")
  187.     else:
  188.         print(txt)
  189.  
  190. channels = []
  191. for line in channel_data.splitlines():
  192.     if not line or line == "" or re.match('^#|\/\/', line):
  193.         continue
  194.     tmp = line.split(' ')
  195.     channels.append({'channelId': urllib.parse.quote(tmp[0]), 'channelOwner': " ".join(tmp[1:])})
  196.  
  197. timezones = []
  198. for line in timezone_data.splitlines():
  199.     if not line or line == "":
  200.         continue
  201.     tmp = line.split(' ')
  202.     timezones.append({'name': tmp[0], 'delta': float(tmp[1])})
  203.  
  204. # Sort timezones
  205. timezones = sorted(timezones, key=lambda kv: kv['delta'])
  206.  
  207. live_streams = []
  208. upcoming_streams = []
  209. # Retrieve information from channels
  210. for chan in channels:
  211.     if not quiet_mode:
  212.         print("Retrieving %s channel (%s) " % (chan['channelOwner'], chan['channelId'].replace("%40", "@")), end="")
  213.     try:
  214.         streams = getStreams(chan['channelId'], chan['channelOwner'])
  215.     except:
  216.         streams = None
  217.     if streams is None:
  218.         if not quiet_mode:
  219.             print(" ... ERROR")
  220.         continue
  221.     if not quiet_mode:
  222.         print(" ... OK")
  223.     if 'live' in streams:
  224.         live_streams += streams['live']
  225.     if 'upcoming' in streams:
  226.         upcoming_streams += streams['upcoming']
  227.  
  228. print("")
  229. if output_to_file:
  230.     fp = open(outfname, 'w', encoding='utf-8')
  231.  
  232. live_streams = sorted(live_streams, key=lambda kv: kv['owner'])
  233. if live_streams:
  234.     log('LIVE STREAMS:' if len(live_streams) < 4 else 'ONGOING BATTLEROYALE')
  235.     last_owner = ""
  236.     for stream in live_streams:
  237.         if stream['owner'] != last_owner:
  238.             log("\n" + stream['owner'])
  239.         if show_live_titles:
  240.             log(stream['title'])
  241.         log("https://youtu.be/%s" % stream['videoId'])
  242.         last_owner = stream['owner']
  243. log("")
  244. if sort_streams:
  245.     upcoming_streams = sorted(upcoming_streams, key=lambda kv: kv['start'])
  246. if upcoming_streams:
  247.     log('UPCOMING STREAMS:')
  248.     last_owner = ''
  249.     for stream in upcoming_streams:
  250.         # If upcoming stream is more than 'ignore_days' days away, ignore it
  251.         if now() + (60 * 60 * 24 * ignore_days) < int(stream['start']):
  252.             continue
  253.  
  254.         timestr = getTimeString(int(stream['start']))
  255.         if stream['owner'] != last_owner:
  256.             log("\n" + stream['owner'])
  257.         if show_upcoming_titles:
  258.             log(stream['title'])
  259.         log("https://youtu.be/%s" % (stream['videoId']))
  260.         if show_timezones:
  261.             log(timestr)
  262.         last_owner = stream['owner']
  263. if output_to_file:
  264.     print("Output to file %s" % outfname)
  265.  
  266. if "PROMPT" not in os.environ and "PWD" not in os.environ:
  267.     input("\nPress ENTER to exit")
  268.  
Advertisement
Add Comment
Please, Sign In to add comment