From f6628730d6579c892e2766e5a91988e42f760324 Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Fri, 7 Jan 2022 18:16:13 +0100 Subject: [PATCH] Use sqlite for storage --- src/Utils/__init__.py | 151 ++++++++++++++++++++++++++++++------------ src/main.py | 22 +++--- src/yt_subs.json | 1 + 3 files changed, 117 insertions(+), 57 deletions(-) create mode 100644 src/yt_subs.json diff --git a/src/Utils/__init__.py b/src/Utils/__init__.py index 7d105e3..e018c9a 100644 --- a/src/Utils/__init__.py +++ b/src/Utils/__init__.py @@ -1,15 +1,40 @@ #!/usr/bin/env python3 import io -from os import path -from posixpath import relpath +import json +import sqlite3 +from os import environ, makedirs, path +from typing import Callable, Union +from urllib.parse import urlparse import requests import wx -from typing import Union, Callable -SIZE = wx.Size(100,68) +SIZE = wx.Size(100, 68) MYPATH = path.dirname(path.abspath(__file__)) -SCREEN_WIDTH = 720 /2 +SCREEN_WIDTH = int(720 / 2) +BASEPATH = path.join(str(environ.get("HOME")), '.config/cast') +DB_FILE_NAME = 'cast.db' +SUB_TABLE = 'subscriptions' + + +def add_subscription(channel_id: str, + name: str, + basepath: str = BASEPATH, + filename: str = DB_FILE_NAME) -> None: + fullpath = path.join(basepath, filename) + if not path.isdir(basepath): + makedirs(basepath) + con = sqlite3.connect(fullpath) + cur = con.cursor() + create_query: str = '''CREATE TABLE IF NOT EXISTS {} + (channel_id TEXT PRIMARY KEY, channel_name TEXT)'''.format(SUB_TABLE) + cur.execute(create_query) + con.commit() + upsert_query: str = '''INSERT INTO {} (channel_id, channel_name) + VALUES('{}',"{}") ON CONFLICT(channel_id) DO NOTHING'''.format(SUB_TABLE, channel_id, name) + cur.execute(upsert_query) + con.commit() + def get_default_logo(providerid: str = 'default') -> wx.Bitmap: if providerid == 'SVT': @@ -19,32 +44,65 @@ def get_default_logo(providerid: str = 'default') -> wx.Bitmap: else: return wx.Bitmap('{}/assets/Default.png'.format(MYPATH)) -def make_sized_button(parent_pnl: wx.Panel, bitmap_or_str: Union[wx.Bitmap,str], text: str, callback: Callable) -> wx.BoxSizer: - btn_sizer = wx.BoxSizer(wx.HORIZONTAL) - if type(bitmap_or_str) == type(str): - if bitmap_or_str.startswith('http'): # type: ignore - bitmap = make_bitmap_from_url(bitmap_or_str) # type: ignore - else: - bitmap = wx.Bitmap(bitmap_or_str, wx.BITMAP_TYPE_ANY) + +def get_subscriptions(basepath: str = BASEPATH, + filename: str = DB_FILE_NAME) -> list[tuple[str, str]]: + subscriptions = list() + fullpath = path.join(basepath, filename) + con = sqlite3.connect(fullpath) + cur = con.cursor() + select_query = '''SELECT * FROM {}'''.format(SUB_TABLE) + cur.execute(select_query) + for result in cur.fetchall(): + print(result) + subscriptions.append(result) + return subscriptions + + +def import_from_newpipe(filename) -> None: + + if path.isfile(filename): + with open(filename, 'r') as subs: + sub_data = json.loads(subs.read()) + + for channel in sub_data['subscriptions']: + if channel['service_id'] == 0: + channel_id = urlparse(channel['url']).path.split('/').pop() + add_subscription(channel_id, channel['name']) + + +def make_sized_button(parent_pnl: wx.Panel, bitmap_or_str: Union[wx.Bitmap, + str], + text: str, callback: Callable) -> wx.BoxSizer: + btn_sizer = wx.BoxSizer(wx.HORIZONTAL) + if type(bitmap_or_str) == type(str): + if bitmap_or_str.startswith('http'): # type: ignore + bitmap = make_bitmap_from_url(bitmap_or_str) # type: ignore else: - bitmap = bitmap_or_str - btn_style = wx.BORDER_NONE | wx.BU_AUTODRAW | wx.BU_EXACTFIT | wx.BU_NOTEXT - btn_logo = wx.BitmapButton(parent_pnl, wx.ID_ANY, bitmap , style=btn_style) - btn_logo.SetMinSize(SIZE) - btn_logo.SetToolTip(text) - btn_sizer.Add(btn_logo, 0, wx.BOTTOM | wx.EXPAND | wx.LEFT | wx.TOP, 1) + bitmap = wx.Bitmap(bitmap_or_str, wx.BITMAP_TYPE_ANY) + else: + bitmap = bitmap_or_str + btn_style = wx.BORDER_NONE | wx.BU_AUTODRAW | wx.BU_EXACTFIT | wx.BU_NOTEXT + btn_logo = wx.BitmapButton(parent_pnl, wx.ID_ANY, bitmap, style=btn_style) + btn_logo.SetMinSize(SIZE) + btn_logo.SetToolTip(text) + btn_sizer.Add(btn_logo, 0, wx.BOTTOM | wx.EXPAND | wx.LEFT | wx.TOP, 1) - btn_text = wx.Button(parent_pnl, wx.ID_ANY, text, style=wx.BORDER_NONE | wx.BU_AUTODRAW) - btn_text.SetMinSize(wx.Size(SCREEN_WIDTH - SIZE.GetWidth(),SIZE.GetHeight())) - btn_text.SetToolTip(text) - btn_sizer.Add(btn_text, 0, wx.BOTTOM | wx.RIGHT | wx.TOP | wx.EXPAND, 1) - parent_pnl.Bind(wx.EVT_BUTTON, callback, btn_logo) - parent_pnl.Bind(wx.EVT_BUTTON, callback, btn_text) + btn_text = wx.Button(parent_pnl, + wx.ID_ANY, + text, + style=wx.BORDER_NONE | wx.BU_AUTODRAW) + btn_text.SetMinSize( + wx.Size(SCREEN_WIDTH - SIZE.GetWidth(), SIZE.GetHeight())) + btn_text.SetToolTip(text) + btn_sizer.Add(btn_text, 0, wx.BOTTOM | wx.RIGHT | wx.TOP | wx.EXPAND, 1) + parent_pnl.Bind(wx.EVT_BUTTON, callback, btn_logo) + parent_pnl.Bind(wx.EVT_BUTTON, callback, btn_text) - return btn_sizer + return btn_sizer -def make_bitmap_from_url(logo_url: str, size: wx.Size = SIZE ) -> wx.Bitmap: +def make_bitmap_from_url(logo_url: str, size: wx.Size = SIZE) -> wx.Bitmap: res = requests.get(logo_url) content = res.content content_bytes = io.BytesIO(content) @@ -52,53 +110,58 @@ def make_bitmap_from_url(logo_url: str, size: wx.Size = SIZE ) -> wx.Bitmap: scale_factor = image.GetWidth() / SCREEN_WIDTH size.SetWidth(SCREEN_WIDTH) height = image.GetHeight() - size.SetHeight(height/scale_factor) - image.Rescale(size.GetWidth(), size.GetHeight() ) + size.SetHeight(height / scale_factor) + image.Rescale(size.GetWidth(), size.GetHeight()) return wx.Bitmap(image) def make_bitmap_from_file(path, size: wx.Size = SIZE) -> wx.Bitmap: image = wx.Image(path, type=wx.BITMAP_TYPE_ANY, index=-1) - # scale_factor = image.GetWidth() / SCREEN_WIDTH - # size.SetWidth(SCREEN_WIDTH) - # height = image.GetHeight() - # size.SetHeight(height/scale_factor) - # image.Rescale(size.GetWidth(), size.GetHeight() ) + # scale_factor = image.GetWidth() / SCREEN_WIDTH + # size.SetWidth(SCREEN_WIDTH) + # height = image.GetHeight() + # size.SetHeight(height/scale_factor) + # image.Rescale(size.GetWidth(), size.GetHeight() ) return wx.Bitmap(image) + def resolve_svt_channel(svt_id: str) -> dict: channels = { "ch-barnkanalen": { "name": "Barnkanalen", - "thumbnail": make_bitmap_from_file('{}/assets/Barnkanalen.png'.format(MYPATH)) + "thumbnail": + make_bitmap_from_file('{}/assets/Barnkanalen.png'.format(MYPATH)) }, "ch-svt1": { - "name": - "SVT 1", - "thumbnail": make_bitmap_from_file('{}/assets/SVT1.png'.format(MYPATH)) + "name": "SVT 1", + "thumbnail": + make_bitmap_from_file('{}/assets/SVT1.png'.format(MYPATH)) }, "ch-svt2": { - "name": - "SVT 2", - "thumbnail": make_bitmap_from_file('{}/assets/SVT2.png'.format(MYPATH)) + "name": "SVT 2", + "thumbnail": + make_bitmap_from_file('{}/assets/SVT2.png'.format(MYPATH)) }, "ch-svt24": { "name": "SVT 24", - "thumbnail": make_bitmap_from_file('{}/assets/SVT24.png'.format(MYPATH)) + "thumbnail": + make_bitmap_from_file('{}/assets/SVT24.png'.format(MYPATH)) }, "ch-kunskapskanalen": { "name": "Kunskapskanalen", - "thumbnail": make_bitmap_from_file('{}/assets/Kunskapskanalen.png'.format(MYPATH)) + "thumbnail": + make_bitmap_from_file( + '{}/assets/Kunskapskanalen.png'.format(MYPATH)) }, "feed": { "name": "Senaste program", - "thumbnail": make_bitmap_from_file('{}/assets/SVT.png'.format(MYPATH)) + "thumbnail": + make_bitmap_from_file('{}/assets/SVT.png'.format(MYPATH)) }, } return channels[svt_id] - diff --git a/src/main.py b/src/main.py index 33cf638..8b4b45b 100644 --- a/src/main.py +++ b/src/main.py @@ -13,10 +13,10 @@ import wx.media from Channel import SVT, YouTube from ChannelProvider import ChannelProvider -from Utils import make_sized_button +from Utils import get_subscriptions, import_from_newpipe, make_sized_button -WIDTH = 720 / 2 -HEIGHT = 1440 / 2 +WIDTH = int(720 / 2) +HEIGHT = int(1440 / 2) BTN_HEIGHT = 40 SPACER_HEIGHT = 10 SCROLL_RATE = 5 @@ -91,15 +91,11 @@ class Cast(wx.Frame): subfile = 'yt_subs.json' if os.path.isfile(subfile): - with open(subfile, 'r') as subs: - janson = json.loads(subs.read()) - - for channel in janson['subscriptions']: - if channel['service_id'] == 0: - channel_id = urlparse( - channel['url']).path.split('/').pop() - youtube.append_channel( - YouTube.YouTube(channel_id, channel['name'])) + import_from_newpipe(subfile) + subscriptions = get_subscriptions() + for channel in subscriptions: + print(channel) + youtube.append_channel(YouTube.YouTube(channel[0], channel[1])) providers.append(youtube) @@ -217,7 +213,7 @@ class Cast(wx.Frame): inner_sizer = wx.GridBagSizer() self.m_control = wx.media.MediaCtrl( self.m_panel, - size=(WIDTH, HEIGHT / 2), + size=(WIDTH, int(HEIGHT / 2)), style=wx.SIMPLE_BORDER, szBackend=wx.media.MEDIABACKEND_GSTREAMER, ) diff --git a/src/yt_subs.json b/src/yt_subs.json new file mode 100644 index 0000000..3677de6 --- /dev/null +++ b/src/yt_subs.json @@ -0,0 +1 @@ +{"app_version":"0.21.14","app_version_int":980,"subscriptions":[{"service_id":0,"url":"https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw","name":"3Blue1Brown"},{"service_id":0,"url":"https://www.youtube.com/channel/UCHL9bfHTxCMi-7vfxQ-AYtg","name":"Abroad in Japan"},{"service_id":0,"url":"https://www.youtube.com/channel/UCiDJtJKMICpb9B1qf7qjEOA","name":"Adam Savage\u2019s Tested"},{"service_id":0,"url":"https://www.youtube.com/channel/UC3ts8coMP645hZw9JSD3pqQ","name":"Andreas Kling"},{"service_id":3,"url":"https://framatube.org/video-channels/fsf_channel","name":"Animated free software awareness videos "},{"service_id":0,"url":"https://www.youtube.com/channel/UCciQ8wFcVoIIMi-lfu8-cjQ","name":"Anton Petrov"},{"service_id":0,"url":"https://www.youtube.com/channel/UCIuCEEUjuBMBlNbU4Zmv_sw","name":"Arnaud Cousergue"},{"service_id":0,"url":"https://www.youtube.com/channel/UCPM5DQ0mxQdLAlnu6R4d1YA","name":"Art of One Dojo"},{"service_id":0,"url":"https://www.youtube.com/channel/UCUXLiFJcHU_Nn_601pi4zjg","name":"BACOUPLE"},{"service_id":0,"url":"https://www.youtube.com/channel/UCH4BNI0-FOK2dMXoFtViWHw","name":"Be Smart"},{"service_id":0,"url":"https://www.youtube.com/channel/UCS0N5baNlQWJCUrhCEo8WlA","name":"Ben Eater"},{"service_id":0,"url":"https://www.youtube.com/channel/UCGMTesZlKa0Lokb7ZNqOJXQ","name":"BilliSpeaks"},{"service_id":0,"url":"https://www.youtube.com/channel/UCKTehwyGCKF-b2wo0RKwrcg","name":"Bisqwit"},{"service_id":0,"url":"https://www.youtube.com/channel/UCJLLl6AraX1POemgLfhirwg","name":"Bits inside by René Rebe"},{"service_id":0,"url":"https://www.youtube.com/channel/UC_SvYP0k05UKiJ_2ndB02IA","name":"blackpenredpen"},{"service_id":0,"url":"https://www.youtube.com/channel/UCgBvlhT2XE8ryUumHiMom8w","name":"Bujinkan Paul Kasumi An & Yokohama Dojo"},{"service_id":0,"url":"https://www.youtube.com/channel/UCj3yW-PqJdek3zC4NNdoTpA","name":"Bujinkan Seijitsu dojo"},{"service_id":0,"url":"https://www.youtube.com/channel/UCvkUSUle3I-K1CYKc7SCAxA","name":"caleb"},{"service_id":0,"url":"https://www.youtube.com/channel/UCB1J6siDdmhwah7q0O2WJBg","name":"Charles Dowding"},{"service_id":3,"url":"https://share.tube/video-channels/chrisweredigital","name":"Chris Were Digital"},{"service_id":0,"url":"https://www.youtube.com/channel/UC2MJylovjrLtsGP0_4UrqrQ","name":"Cody'sBLab"},{"service_id":0,"url":"https://www.youtube.com/channel/UCu6mSoMNzHQiBIOCkHUa2Aw","name":"Cody'sLab"},{"service_id":0,"url":"https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA","name":"Computerphile"},{"service_id":0,"url":"https://www.youtube.com/channel/UCD5eL38hFtSLiVFP9cCUJEA","name":"Daniel Stenberg"},{"service_id":0,"url":"https://www.youtube.com/channel/UCVls1GmFKf6WlTraIb_IaJg","name":"DistroTube"},{"service_id":0,"url":"https://www.youtube.com/channel/UCUQo7nzH1sXVpzL92VesANw","name":"DIY Perks"},{"service_id":0,"url":"https://www.youtube.com/channel/UCSX0NhNdBA-ZnEFkYFzdw4A","name":"Dogen"},{"service_id":0,"url":"https://www.youtube.com/channel/UCYNbYGl89UUowy8oXkipC-Q","name":"Dr. Becky"},{"service_id":0,"url":"https://www.youtube.com/channel/UCSbyncU597LMwb3HhnAI_4w","name":"Epic Gardening"},{"service_id":0,"url":"https://www.youtube.com/channel/UC8EQAfueDGNeqb1ALm0LjHA","name":"Exploring Alternatives"},{"service_id":0,"url":"https://www.youtube.com/channel/UCD5B6VoXv41fJ-IW8Wrhz9A","name":"Fermilab"},{"service_id":0,"url":"https://www.youtube.com/channel/UCv1Kcz-CuGM6mxzL3B1_Eiw","name":"Gardiner Bryant"},{"service_id":0,"url":"https://www.youtube.com/channel/UC78Ib99EBhMN3NemVjYm3Ig","name":"Grant Sanderson"},{"service_id":0,"url":"https://www.youtube.com/channel/UC6mIxFTvXkWQVEHPsEdflzQ","name":"GreatScott!"},{"service_id":0,"url":"https://www.youtube.com/channel/UCOT2iLov0V7Re7ku_3UBtcQ","name":"hankschannel"},{"service_id":0,"url":"https://www.youtube.com/channel/UCsP7Bpw36J666Fct5M8u-ZA","name":"How To Cook That"},{"service_id":0,"url":"https://www.youtube.com/channel/UCfIqCzQJXvYj9ssCoHq327g","name":"How To Make Everything"},{"service_id":0,"url":"https://www.youtube.com/channel/UCeaKRrrpWiQFJJmiuon2WoQ","name":"Huw Richards"},{"service_id":0,"url":"https://www.youtube.com/channel/UCypN3QKwo-Fajp391i0IQ0A","name":"ingomar200"},{"service_id":0,"url":"https://www.youtube.com/channel/UC5fdssPqmmGhkhsJi4VcckA","name":"Innuendo Studios"},{"service_id":0,"url":"https://www.youtube.com/channel/UC-yuWVUplUJZvieEligKBkA","name":"javidx9"},{"service_id":0,"url":"https://www.youtube.com/channel/UCRIeMHsEdzA9RroG19kXdYg","name":"Jesse Enkamp"},{"service_id":0,"url":"https://www.youtube.com/channel/UC_iD0xppBwwsrM9DegC5cQQ","name":"Jon Gjengset"},{"service_id":0,"url":"https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q","name":"Kurzgesagt \u2013 In a Nutshell"},{"service_id":0,"url":"https://www.youtube.com/channel/UCNhX3WQEkraW3VHPyup8jkQ","name":"Langfocus"},{"service_id":0,"url":"https://www.youtube.com/channel/UCxuZNRnlprC70l1bnI0n-XQ","name":"Learn Japanese From Zero!"},{"service_id":0,"url":"https://www.youtube.com/channel/UCBkqDNqao03ldC3u78-Pp8g","name":"Linfamy"},{"service_id":0,"url":"https://www.youtube.com/channel/UCR-_DvrwKkk3rpQbzCRFHOw","name":"LINMOBnet"},{"service_id":0,"url":"https://www.youtube.com/channel/UC84u7JhM9EIAYzyjdf6cBbA","name":"Linus Groh"},{"service_id":0,"url":"https://www.youtube.com/channel/UClcE-kVhqyiHCcjYwcpfj9w","name":"LiveOverflow"},{"service_id":0,"url":"https://www.youtube.com/channel/UCe0Ha5QljsCV5UqIkobBrcQ","name":"Liz Zorab - Byther Farm"},{"service_id":0,"url":"https://www.youtube.com/channel/UCm9K6rby98W8JigLoZOh6FQ","name":"LockPickingLawyer"},{"service_id":0,"url":"https://www.youtube.com/channel/UCOMrUmOTPD_AnSivjxptxpA","name":"Louis Weisz"},{"service_id":0,"url":"https://www.youtube.com/channel/UCL5rTpHIfmKzmRcp7yiKjJw","name":"Lovely Greens"},{"service_id":0,"url":"https://www.youtube.com/channel/UC4UXU2ZkeAwEFlLv1Yt1UMQ","name":"Martijn Braam"},{"service_id":0,"url":"https://www.youtube.com/channel/UCOjOqr5SIDpgBssNz1XiVJw","name":"Mary's Test Kitchen"},{"service_id":0,"url":"https://www.youtube.com/channel/UC1_uAIS3r8Vu6JjXWvastJg","name":"Mathologer"},{"service_id":0,"url":"https://www.youtube.com/channel/UCzV9N7eGedBchEQjQhPapyQ","name":"Matt_Parker_2"},{"service_id":0,"url":"https://www.youtube.com/channel/UCVGVbOl6F5rGF4wSYS6Y5yQ","name":"MIgardener"},{"service_id":0,"url":"https://www.youtube.com/channel/UCsQCbl3a9FtYvA55BxdzYiQ","name":"Miku Real Japanese"},{"service_id":0,"url":"https://www.youtube.com/channel/UCjeKoCr7YTNn0bmMO1HdyVw","name":"mimei"},{"service_id":0,"url":"https://www.youtube.com/channel/UCRutxRBx4rteBZBwtSoJATg","name":"MIMEI LAND"},{"service_id":0,"url":"https://www.youtube.com/channel/UCWQG9Bq1pFJLjwLUgorN1Qw","name":"Namiryu Dojo"},{"service_id":0,"url":"https://www.youtube.com/channel/UCcJceGUaevGlP7s2xzL9akA","name":"Next Level Gardening"},{"service_id":0,"url":"https://www.youtube.com/channel/UC1D3yD4wlPMico0dss264XA","name":"NileBlue"},{"service_id":0,"url":"https://www.youtube.com/channel/UCFhXFikryT4aFcLkLw2LBLA","name":"NileRed"},{"service_id":0,"url":"https://www.youtube.com/channel/UCoxcjq-8xIDTYp3uz647V5A","name":"Numberphile"},{"service_id":0,"url":"https://www.youtube.com/channel/UCyp1gCHZJU_fGWFf2rtMkCg","name":"Numberphile2"},{"service_id":0,"url":"https://www.youtube.com/channel/UCzR-rom72PHN9Zg7RML9EbA","name":"PBS Eons"},{"service_id":0,"url":"https://www.youtube.com/channel/UC7_gcs09iThXybpVgjHZ_7g","name":"PBS Space Time"},{"service_id":0,"url":"https://www.youtube.com/channel/UC6uo7Swuhid4KcK97n2l7_Q","name":"PDRさん"},{"service_id":0,"url":"https://www.youtube.com/channel/UCo-f3WS0648Hg8Jqxx_ui-A","name":"PDRさんのゴミ箱"},{"service_id":0,"url":"https://www.youtube.com/channel/UCtESv1e7ntJaLJYKIO1FoYw","name":"Periodic Videos"},{"service_id":0,"url":"https://www.youtube.com/channel/UCTpmmkp1E4nmZqWPS-dl5bg","name":"Quanta Magazine"},{"service_id":0,"url":"https://www.youtube.com/channel/UC176GAQozKKjhz62H8u9vQQ","name":"Real Science"},{"service_id":0,"url":"https://www.youtube.com/channel/UCFJxE0l3cVYU4kHzi4qVEkw","name":"Rebecca Watson"},{"service_id":0,"url":"https://www.youtube.com/channel/UCq9U-gJ1LtDCE4W5BhEDFSQ","name":"RED Gardens"},{"service_id":0,"url":"https://www.youtube.com/channel/UC_oqZXtcxfJTaw1j2M1H1XQ","name":"Sauce Stache"},{"service_id":0,"url":"https://www.youtube.com/channel/UCZYTClx2T1of7BRZ86-8fow","name":"SciShow"},{"service_id":0,"url":"https://www.youtube.com/channel/UCrMePiHCWG4Vwqv3t7W9EFg","name":"SciShow Space"},{"service_id":0,"url":"https://www.youtube.com/channel/UCJZTjBlrnDHYmf0F-eYXA3Q","name":"Self Sufficient Me"},{"service_id":0,"url":"https://www.youtube.com/channel/UCm8xNi3kuBHE99QH-N_VJVg","name":"Sharmeleon"},{"service_id":0,"url":"https://www.youtube.com/channel/UC6107grRI4m0o2-emgoDnAA","name":"SmarterEveryDay"},{"service_id":0,"url":"https://www.youtube.com/channel/UCMj4YareOFuEfxUBbncWF9w","name":"Sora The Troll"},{"service_id":0,"url":"https://www.youtube.com/channel/UCSju5G2aFaWMqn-_0YBtq5A","name":"Stand-up Maths"},{"service_id":0,"url":"https://www.youtube.com/channel/UCEIwxahdLz7bap-VDs9h35A","name":"Steve Mould"},{"service_id":0,"url":"https://www.youtube.com/channel/UCH4L3iapFMHQ0tfPOPNiAJA","name":"Susanne Vandraren"},{"service_id":0,"url":"https://www.youtube.com/channel/UCrW38UKhlPoApXiuKNghuig","name":"Systems with JT"},{"service_id":0,"url":"https://www.youtube.com/channel/UC4JX40jDee_tINbkjycV4Sg","name":"Tech With Tim"},{"service_id":0,"url":"https://www.youtube.com/channel/UC1VLQPn9cYSqx8plbk9RxxQ","name":"The Action Lab"},{"service_id":0,"url":"https://www.youtube.com/channel/UC3ETCazlHenpXEsrEJH-k5A","name":"The Anime Man"},{"service_id":0,"url":"https://www.youtube.com/channel/UCQ-W1KE9EYfdxhL6S4twUNw","name":"The Cherno"},{"service_id":0,"url":"https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw","name":"The Coding Train"},{"service_id":0,"url":"https://www.youtube.com/channel/UC5UAwBUum7CPN5buc-_N1Fw","name":"The Linux Experiment"},{"service_id":0,"url":"https://www.youtube.com/channel/UCF2IdSvpCUXPLQ1Q_r5JW1A","name":"The PhysicsMaths Wizard"},{"service_id":0,"url":"https://www.youtube.com/channel/UCYeF244yNGuFefuFKqxIAXw","name":"The Royal Institution"},{"service_id":0,"url":"https://www.youtube.com/channel/UCV5vCi3jPJdURZwAOO_FNfQ","name":"The Thought Emporium"},{"service_id":0,"url":"https://www.youtube.com/channel/UCgWip0vxtqu34rZrFeCpUow","name":"Tim R Morgan"},{"service_id":0,"url":"https://www.youtube.com/channel/UCZDA1kA3y3EIg25BpcHSpwQ","name":"Tinkernut"},{"service_id":0,"url":"https://www.youtube.com/channel/UC1zZE_kJ8rQHgLTVfobLi_g","name":"TKOR"},{"service_id":0,"url":"https://www.youtube.com/channel/UCAKZ2vtm_-hfqeCGTNNbZqA","name":"Tokidoki Traveller"},{"service_id":0,"url":"https://www.youtube.com/channel/UC1TmvgkTb_5jzKcvx6Pt0Dw","name":"Tokyo Creative"},{"service_id":0,"url":"https://www.youtube.com/channel/UCRfo-DAifrP3lzcxUHtGm_A","name":"Tom Rocks Maths"},{"service_id":0,"url":"https://www.youtube.com/channel/UCBa659QWEk1AI4Tg--mrJ2A","name":"Tom Scott"},{"service_id":0,"url":"https://www.youtube.com/channel/UCxr2d4As312LulcajAkKJYw","name":"Townsends"},{"service_id":0,"url":"https://www.youtube.com/channel/UCBtxQ0Oef0e2o-9iDe6Ia_w","name":"TUSENKONSTNÄRERNA"},{"service_id":0,"url":"https://www.youtube.com/channel/UCHnyfMqiRRG1u-2MsSQLbXA","name":"Veritasium"},{"service_id":0,"url":"https://www.youtube.com/channel/UCGaVdbSav8xWuFWTadK6loA","name":"vlogbrothers"},{"service_id":0,"url":"https://www.youtube.com/channel/UC6nSFpj9HTCZ5t-N3Rm3-HA","name":"Vsauce"},{"service_id":0,"url":"https://www.youtube.com/channel/UChsbD6Clp-ZPqKwXJR3V7DQ","name":"Weird Explorer"},{"service_id":0,"url":"https://www.youtube.com/channel/UCf-ruwCgdtpCzuM7ODY5c9g","name":"Yuko Sensei"},{"service_id":0,"url":"https://www.youtube.com/channel/UCYQ2j85UM2QZ_fTW5e_DddQ","name":"忍者オヤジ ポール"}]} \ No newline at end of file