#!/usr/bin/python # encoding=utf8 """ Copyright (C) 2018 MuadDib ---------------------------------------------------------------------------- "THE BEER-WARE LICENSE" (Revision 42): @tantrumdev wrote this file. As long as you retain this notice you can do whatever you want with this stuff. Just Ask first when not released through the tools and parser GIT. If we meet some day, and you think this stuff is worth it, you can buy him a beer in return. - Muad'Dib ---------------------------------------------------------------------------- Changelog: 2019.4.16: - Updated website url change 2018.7.2: - Added Clear Cache function - Minor update on fetch cache returns 2018.6.20: - Added caching to primary menus (Cache time is 3 hours) Usage Examples: 50 Latest Releases topfifty/last-50-recent-release Most Popular Series main/sidebar-titles/0 Latest Series Added main/recent-release-main/1 Dubbed Anime category/dubbed-anime-list Subbed Anime category/subbed-anime-list Cartoons category/cartoon-list Movies category/movie-list Ova Series category/ova-list Search Site wcsearch Everything 101 Dalmatians wcsearch/101 dalmatians Action Genre wcgenre/14 """ import __builtin__ import base64,time import json,re,requests,os,traceback,urlparse import koding import xbmc,xbmcaddon,xbmcgui from koding import route from resources.lib.plugin import Plugin from resources.lib.util import dom_parser from resources.lib.util.context import get_context_items from resources.lib.util.xml import JenItem, JenList, display_list from unidecode import unidecode CACHE_TIME = 10800 # change to wanted cache time in seconds addon_fanart = xbmcaddon.Addon().getAddonInfo('fanart') addon_icon = xbmcaddon.Addon().getAddonInfo('icon') User_Agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36' class WatchCartoon(Plugin): name = "wctoon" def process_item(self, item_xml): if "" in item_xml: item = JenItem(item_xml) if "wcepisode/" in item.get("wctoon", ""): result_item = { 'label': item["title"], 'icon': item.get("thumbnail", addon_icon), 'fanart': item.get("fanart", addon_fanart), 'mode': "WCEpisodes", 'url': item.get("wctoon", ""), 'folder': True, 'imdb': "0", 'content': "files", 'season': "0", 'episode': "0", 'info': {}, 'year': "0", 'context': get_context_items(item), "summary": item.get("summary", None) } elif "wcsearch" in item.get("wctoon", ""): result_item = { 'label': item["title"], 'icon': item.get("thumbnail", addon_icon), 'fanart': item.get("fanart", addon_fanart), 'mode': "WCSearch", 'url': item.get("wctoon", ""), 'folder': True, 'imdb': "0", 'content': "files", 'season': "0", 'episode': "0", 'info': {}, 'year': "0", 'context': get_context_items(item), "summary": item.get("summary", None) } elif "wcgenre" in item.get("wctoon", ""): result_item = { 'label': item["title"], 'icon': item.get("thumbnail", addon_icon), 'fanart': item.get("fanart", addon_fanart), 'mode': "WCGenre", 'url': item.get("wctoon", ""), 'folder': True, 'imdb': "0", 'content': "files", 'season': "0", 'episode': "0", 'info': {}, 'year': "0", 'context': get_context_items(item), "summary": item.get("summary", None) } elif "direct/" in item.get("wctoon", ""): result_item = { 'label': item["title"], 'icon': item.get("thumbnail", addon_icon), 'fanart': item.get("fanart", addon_fanart), 'mode': "WCPlayVideo", 'url': item.get("wctoon", ""), 'folder': False, 'imdb': "0", 'content': "files", 'season': "0", 'episode': "0", 'info': {}, 'year': "0", 'context': get_context_items(item), "summary": item.get("summary", None) } elif "topfifty/" in item.get("wctoon", ""): result_item = { 'label': item["title"], 'icon': item.get("thumbnail", addon_icon), 'fanart': item.get("fanart", addon_fanart), 'mode': "TopFifty", 'url': item.get("wctoon", ""), 'folder': True, 'imdb': "0", 'content': "files", 'season': "0", 'episode': "0", 'info': {}, 'year': "0", 'context': get_context_items(item), "summary": item.get("summary", None) } elif "main/" in item.get("wctoon", ""): result_item = { 'label': item["title"], 'icon': item.get("thumbnail", addon_icon), 'fanart': item.get("fanart", addon_fanart), 'mode': "WCMain", 'url': item.get("wctoon", ""), 'folder': True, 'imdb': "0", 'content': "files", 'season': "0", 'episode': "0", 'info': {}, 'year': "0", 'context': get_context_items(item), "summary": item.get("summary", None) } elif "category/" in item.get("wctoon", ""): result_item = { 'label': item["title"], 'icon': item.get("thumbnail", addon_icon), 'fanart': item.get("fanart", addon_fanart), 'mode': "WatchCartoon", 'url': item.get("wctoon", ""), 'folder': True, 'imdb': "0", 'content': "files", 'season': "0", 'episode': "0", 'info': {}, 'year': "0", 'context': get_context_items(item), "summary": item.get("summary", None) } result_item["properties"] = { 'fanart_image': result_item["fanart"] } result_item['fanart_small'] = result_item["fanart"] return result_item def clear_cache(self): skip_prompt = xbmcaddon.Addon().getSetting("quiet_cache") dialog = xbmcgui.Dialog() if skip_prompt == 'false': if dialog.yesno(xbmcaddon.Addon().getAddonInfo('name'), "Clear WatchCartoon.com Plugin Cache?"): koding.Remove_Table("wctoon_com_plugin") else: koding.Remove_Table("wctoon_com_plugin") @route(mode='WatchCartoon', args=["url"]) def get_wcstream(url): url = url.replace('category/', '') # Strip our category tag off. url = urlparse.urljoin('https://www.thewatchcartoononline.tv/', url) #url = urlparse.urljoin('https://www.watchcartoononline.com', url) pins = "" xml = fetch_from_db(url) if not xml: xml = "" try: html = requests.get(url).content ddmcc = dom_parser.parseDOM(html, 'div', attrs={'class':'ddmcc'})[0] # pull root List, as all the minor lists are contained within it lists = dom_parser.parseDOM(ddmcc, 'li') for entry in lists: try: movie_style = 0 try: # if this fails, means it is a movie/ova series entry as they use different html for those categories show_url, title = re.compile('(.+?)',re.DOTALL).findall(entry)[0] except: show_url, title = re.compile('(.+?)',re.DOTALL).findall(entry)[0] movie_style = 1 title = refreshtitle(title) title = remove_non_ascii(title) if movie_style == 1: xml += ""\ " %s"\ " direct/%s"\ " %s"\ " %s"\ "" % (title,show_url,addon_icon,title) else: xml += ""\ " %s"\ " wcepisode/%s"\ " %s"\ " %s"\ "" % (title,show_url,addon_icon,title) except: continue save_to_db(xml, url) except: pass jenlist = JenList(xml) display_list(jenlist.get_list(), jenlist.get_content_type(), pins) @route(mode='TopFifty', args=["url"]) def get_wctopfiftystream(url): url = url.replace('topfifty/', '') # Strip our category tag off. url = urlparse.urljoin('https://www.watchcartoononline.io', url) pins = "" xml = fetch_from_db(url) if not xml: xml = "" try: html = requests.get(url).content thediv = dom_parser.parseDOM(html, 'div', attrs={'class':'menulaststyle'})[0] lists = dom_parser.parseDOM(thediv, 'li') for entry in lists: try: show_url, title = re.compile('(.+?)',re.DOTALL).findall(entry)[0] title = refreshtitle(title) title = remove_non_ascii(title) xml += ""\ " %s"\ " direct/%s"\ " %s"\ " %s"\ "" % (title,show_url,addon_icon,title) except: continue save_to_db(xml, url) except: pass jenlist = JenList(xml) display_list(jenlist.get_list(), jenlist.get_content_type(), pins) @route(mode='WCMain', args=["url"]) def get_wcmainstream(subid): xml = "" subid = subid.replace('main/', '', 1) # Strip our category tag off. subid = subid.split('/') pins = "" try: html = requests.get('https://www.watchcartoononline.com').content thedivs = dom_parser.parseDOM(html, 'div', attrs={'class':subid[0]})[int(subid[1])] list_items = dom_parser.parseDOM(thedivs, 'li') for content in list_items: try: if subid[0] == 'sidebar-titles': show_url, title = re.compile('(.+?)',re.DOTALL).findall(content)[0] title = refreshtitle(title).replace('Episode ', 'EP:') title = remove_non_ascii(title) xml += ""\ " %s"\ " wcepisode/%s"\ " %s"\ " %s"\ "" % (title,show_url,addon_icon,title) elif subid[0] == 'recent-release-main': show_url, title = re.compile('(.+?)',re.DOTALL).findall(content)[1] title = refreshtitle(title).replace('Episode ', 'EP:') title = remove_non_ascii(title) show_icon = re.compile('src="(.+?)"',re.DOTALL).findall(content)[0] xml += ""\ " %s"\ " wcepisode/%s"\ " %s"\ " %s"\ "" % (title,show_url,show_icon,title) else: continue except: continue except: pass jenlist = JenList(xml) display_list(jenlist.get_list(), jenlist.get_content_type(), pins) @route(mode='WCEpisodes', args=["url"]) def get_wcepisodes(url): url = url.replace('wcepisode/', '') # Strip our episode tag off. url = urlparse.urljoin('https://www.watchcartoononline.com', url) pins = "" xml = fetch_from_db(url) if not xml: xml = "" try: html = requests.get(url).content thediv = dom_parser.parseDOM(html, 'div', attrs={'id':'sidebar_right3'})[0] lists = dom_parser.parseDOM(thediv, 'div', attrs={'class':'cat-eps'}) for entry in lists: show_url, title = re.compile('(.+?)',re.DOTALL).findall(entry)[0] title = refreshtitle(title).replace('Episode ', 'EP:') title = remove_non_ascii(title) show_icon = dom_parser.parseDOM(html, 'meta', attrs={'property':'og:image'}, ret='content')[0] xml += ""\ " %s"\ " direct/%s"\ " %s"\ " %s"\ "" % (title,show_url,show_icon,title) save_to_db(xml, url) except: pass jenlist = JenList(xml) display_list(jenlist.get_list(), jenlist.get_content_type(), pins) @route(mode='WCGenre', args=["url"]) def get_wcgenre(url): pins = "" if 'all' in url: get_wcgenrelist() return else: url = url.replace('wcgenre/', '') # Strip our genre tag off. url = urlparse.urljoin('https://www.watchcartoononline.com/search-by-genre/', url) xml = fetch_from_db(url) if not xml: xml = "" try: html = requests.get(url).content ddmcc = dom_parser.parseDOM(html, 'div', attrs={'class':'ddmcc'})[0] # pull root List, as all the minor lists are contained within it lists = dom_parser.parseDOM(ddmcc, 'li') for entry in lists: show_url, title = re.compile('href="(.+?)">(.+?)',re.DOTALL).findall(entry)[0] title = refreshtitle(title) title = remove_non_ascii(title) xml += ""\ " %s"\ " wcepisode/%s"\ " %s"\ " %s"\ "" % (title,show_url,addon_icon,title) save_to_db(xml, url) except: pass jenlist = JenList(xml) display_list(jenlist.get_list(), jenlist.get_content_type(), pins) def get_wcgenrelist(): url = 'https://www.watchcartoononline.com/search-by-genre/' pins = "" xml = fetch_from_db(url) if not xml: xml = "" try: html = requests.get(url).content ddmcc = dom_parser.parseDOM(html, 'div', attrs={'class':'ddmcc'})[0] # pull root List, as all the minor lists are contained within it lists = dom_parser.parseDOM(ddmcc, 'li') for entry in lists: show_url, title = re.compile('href="(.+?)">(.+?)',re.DOTALL).findall(entry)[0] # convert show_url to get last tag in the url for the xml creation show_url = show_url.split('/')[-1] title = refreshtitle(title) title = remove_non_ascii(title) xml += ""\ " %s"\ " wcgenre/%s"\ " %s"\ " %s"\ "" % (title,show_url,addon_icon,title) save_to_db(xml, url) except: pass jenlist = JenList(xml) display_list(jenlist.get_list(), jenlist.get_content_type(), pins) @route(mode='WCSearch', args=["url"]) def get_wcsearch(url): pins = "" xml = "" url = url.replace('wcsearch/', '') # Strip our search tag off when used with keywords in the xml url = url.replace('wcsearch', '') # Catch plain case, for when overall search is used. if url != None and url != "": search = url else: keyboard = xbmc.Keyboard('', 'Search for') keyboard.doModal() if keyboard.isConfirmed() != None and keyboard.isConfirmed() != "": search = keyboard.getText() else: return if search == None or search == "": xml += ""\ " Search Cancelled"\ " plugin://plugin.video.squadcontrol/?mode=section_item"\ " %s"\ "" % (addon_icon) jenlist = JenList(xml) display_list(jenlist.get_list(), jenlist.get_content_type(), pins) return total = 0 try: search_url = 'https://www.watchcartoononline.com/wp-json/wp/v2/posts?per_page=100&search=%s' % search.replace(' ', '%20') html = requests.get(search_url).content results = re.compile('"post","link":"(.+?)","title".+?"rendered":"(.+?)"',re.DOTALL).findall(html) if len(results) == 0: dialog = xbmcgui.Dialog() dialog.ok('Search Results', 'Search Results are empty') return for link,name in results: link = link.replace('\\','') name = refreshtitle(name).replace('Episode ', 'EP:') name = remove_non_ascii(name) if search.lower() in name.lower() or search.lower() in link.lower(): xml += ""\ " %s"\ " direct/%s"\ " %s"\ "" % (name,link,addon_icon) total += 1 except: pass if total > 0: jenlist = JenList(xml) display_list(jenlist.get_list(), jenlist.get_content_type(), pins) @route(mode='WCPlayVideo', args=["url"]) def get_wcplayvideo(url): url = url.replace('direct/', '') # Strip our episode tag off. html = requests.get(url) url = '' try: match = re.findall('''var\s*[a-zA-Z]{3}\s*\=\s*\[([^\]]+)''', html.text)[0] spread = re.findall('''-\s*(\d+)\)\;\s*\}''', html.text)[0] match = re.findall('''['"]([^'"]+)['"]''', match) for i in match: i = base64.b64decode(i) i = re.findall(r'(\d+)',i)[0] i = chr(int(i) - int(spread)) url += i url = re.findall(r'src="(.*?)"', url)[0] url = urlparse.urljoin('https://www.watchcartoononline.com', url) url = requests.get(url) oldurl = url url = re.findall(r'''file:\s*['\"]([^'\"]+)['\"](?:\,\s*label:\s*|)(?:['\"]|)([\d]+|)''', url.text) if len(url) == 0: url = re.findall(r'''src:\s*['\"]([^'\"]+)['\"](?:\,\s*src:\s*|)(?:['\"]|)([\d]+|)''', oldurl.text) url = [(i[0],'0' if i[1] == '' else i[1]) for i in url] url = sorted(url, key=lambda x: int(x[1]),reverse=True) xbmc.executebuiltin("PlayMedia(%s)" % (url[0][0])) quit() return except: pass def save_to_db(item, url): if not item or not url: return False try: koding.reset_db() koding.Remove_From_Table( "wctoon_com_plugin", { "url": url }) koding.Add_To_Table("wctoon_com_plugin", { "url": url, "item": base64.b64encode(item), "created": time.time() }) except: return False def fetch_from_db(url): koding.reset_db() wctoon_plugin_spec = { "columns": { "url": "TEXT", "item": "TEXT", "created": "TEXT" }, "constraints": { "unique": "url" } } koding.Create_Table("wctoon_com_plugin", wctoon_plugin_spec) match = koding.Get_From_Table( "wctoon_com_plugin", {"url": url}) if match: match = match[0] if not match["item"]: return None created_time = match["created"] if created_time and float(created_time) + CACHE_TIME >= time.time(): match_item = match["item"] try: result = base64.b64decode(match_item) except: return None return result else: return None else: return None def refreshtitle(title): title = replaceEscapeCodes(title) title = replaceHTMLCodes(title).replace('English Dubbed','[COLOR yellow](English Dubbed)[/COLOR]').replace('English Subbed','[COLOR orange](English Subbed)[/COLOR]') return title def replaceHTMLCodes(txt): txt = re.sub("(&#[0-9]+)([^;^0-9]+)", "\\1;\\2", txt) try: import html.parser as html_parser except: import HTMLParser as html_parser txt = html_parser.HTMLParser().unescape(txt) txt = html_parser.HTMLParser().unescape(txt) txt = txt.replace(""", "\"") txt = txt.replace("&", "&") txt = txt.strip() return txt def replaceEscapeCodes(txt): try: import html.parser as html_parser except: import HTMLParser as html_parser txt = html_parser.HTMLParser().unescape(txt) return txt def remove_non_ascii(text): try: text = text.decode('utf-8').replace(u'\xc2', u'A').replace(u'\xc3', u'A').replace(u'\xc4', u'A') except: pass return unidecode(text)