class Player(xbmc.Player): last_file = None track = False def __init__(self): self.api = Api() self.developer = Developer() xbmc.Player.__init__(self) def set_last_file(self, file): self.last_file = file def get_last_file(self): return self.last_file def is_tracking(self): return self.track def disable_tracking(self): self.track = False def onPlayBackStarted(self): # Will be called when kodi starts playing a file self.track = True if utils.settings("developerMode") == "true": self.developer.developer_play_back() def onPlayBackStopped(self): # Will be called when user stops playing a file. self.last_file = None self.disable_tracking() self.api.reset_addon_data() State() # reset state
class Monitor(xbmc.Monitor): def __init__(self): self.player = Player() self.api = Api() self.playback_manager = PlaybackManager() xbmc.Monitor.__init__(self) def log(self, msg, lvl=1): class_name = self.__class__.__name__ utils.log("%s %s" % (utils.addon_name(), class_name), str(msg), int(lvl)) def run(self): while not self.abortRequested(): # check every 1 sec if self.waitForAbort(1): # Abort was requested while waiting. We should exit break if self.player.is_tracking(): try: play_time = self.player.getTime() total_time = self.player.getTotalTime() last_file = self.player.get_last_file() current_file = self.player.getPlayingFile() notification_time = self.api.notification_time() up_next_disabled = utils.settings("disableNextUp") == "true" if utils.window("PseudoTVRunning") != "True" and not up_next_disabled and total_time > 300: if (total_time - play_time <= int(notification_time) and ( last_file is None or last_file != current_file)) and total_time != 0: self.player.set_last_file(current_file) self.log("Calling autoplayback totaltime - playtime is %s" % (total_time - play_time), 2) self.playback_manager.launch_up_next() self.log("Up Next style autoplay succeeded.", 2) self.player.disable_tracking() except Exception as e: self.log("Exception in Playback Monitor Service: %s" % repr(e)) self.log("======== STOP %s ========" % utils.addon_name(), 0) def onNotification(self, sender, method, data): if method.split('.')[1].lower() != 'upnext_data': # method looks like Other.upnext_data return data = utils.decode_data(data) data['id'] = "%s_play_action" % str(sender.replace(".SIGNAL", "")) self.api.addon_data_received(data)
class PlayItem: _shared_state = {} def __init__(self): self.__dict__ = self._shared_state self.api = Api() self.player = Player() self.state = State() def log(self, msg, lvl=2): class_name = self.__class__.__name__ utils.log("%s %s" % (utils.addon_name(), class_name), msg, int(lvl)) def get_episode(self): current_file = self.player.getPlayingFile() if not self.api.has_addon_data(): # Get the active player result = self.api.get_now_playing() self.handle_now_playing_result(result) # get the next episode from kodi episode = ( self.api.handle_kodi_lookup_of_episode( self.state.tv_show_id, current_file, self.state.include_watched, self.state.current_episode_id)) else: episode = self.api.handle_addon_lookup_of_next_episode() current_episode = self.api.handle_addon_lookup_of_current_episode() self.state.current_episode_id = current_episode["episodeid"] if self.state.current_tv_show_id != current_episode["tvshowid"]: self.state.current_tv_show_id = current_episode["tvshowid"] self.state.played_in_a_row = 1 return episode def handle_now_playing_result(self, result): if 'result' in result: item_type = result["result"]["item"]["type"] current_episode_number = result["result"]["item"]["episode"] current_season_id = result["result"]["item"]["season"] current_show_title = result["result"]["item"]["showtitle"].encode('utf-8') current_show_title = utils.unicode_to_ascii(current_show_title) self.state.tv_show_id = result["result"]["item"]["tvshowid"] if item_type == "episode": if int(self.state.tv_show_id) == -1: self.state.tv_show_id = self.api.showtitle_to_id(title=current_show_title) self.log("Fetched missing tvshowid " + json.dumps(self.state.tv_show_id), 2) # Get current episodeid current_episode_id = self.api.get_episode_id( showid=str(self.state.tv_show_id), show_season=current_season_id, show_episode=current_episode_number) self.state.current_episode_id = current_episode_id if self.state.current_tv_show_id != self.state.tv_show_id: self.state.current_tv_show_id = self.state.tv_show_id self.state.played_in_a_row = 1
def __init__(self): self.player = Player() self.api = Api() self.playback_manager = PlaybackManager() xbmc.Monitor.__init__(self)
def __init__(self): self.api = Api() self.state = State() self.developer = Developer() xbmc.Player.__init__(self)
def __init__(self): self.__dict__ = self._shared_state self.api = Api() self.play_item = PlayItem() self.state = State() self.player = Player()
class PlaybackManager: _shared_state = {} def __init__(self): self.__dict__ = self._shared_state self.api = Api() self.play_item = PlayItem() self.state = State() self.player = Player() def log(self, msg, lvl=2): class_name = self.__class__.__name__ utils.log("%s %s" % (utils.addon_name(), class_name), msg, int(lvl)) def launch_up_next(self): episode = self.play_item.get_episode() if episode is None: # no episode get out of here self.log("Error: no episode could be found to play next...exiting", 1) return self.log("episode details %s" % json.dumps(episode), 2) self.launch_popup(episode) self.api.reset_addon_data() def launch_popup(self, episode): episode_id = episode["episodeid"] no_play_count = episode["playcount"] is None or episode[ "playcount"] == 0 include_play_count = True if self.state.include_watched else no_play_count if include_play_count and self.state.current_episode_id != episode_id: # we have a next up episode choose mode next_up_page, still_watching_page = pages.set_up_pages() showing_next_up_page, showing_still_watching_page, total_time = ( self.show_popup_and_wait(episode, next_up_page, still_watching_page)) should_play_default, should_play_non_default = ( self.extract_play_info(next_up_page, showing_next_up_page, showing_still_watching_page, still_watching_page, total_time)) play_item_option_1 = (should_play_default and self.state.playMode == "0") play_item_option_2 = (should_play_non_default and self.state.playMode == "1") if play_item_option_1 or play_item_option_2: self.log("playing media episode", 2) # Signal to trakt previous episode watched utils.event("NEXTUPWATCHEDSIGNAL", {'episodeid': self.state.current_episode_id}) # Play media if not self.api.has_addon_data(): self.api.play_kodi_item(episode) else: self.api.play_addon_item() def show_popup_and_wait(self, episode, next_up_page, still_watching_page): play_time = self.player.getTime() total_time = self.player.getTotalTime() progress_step_size = utils.calculate_progress_steps(total_time - play_time) next_up_page.setItem(episode) next_up_page.setProgressStepSize(progress_step_size) still_watching_page.setItem(episode) still_watching_page.setProgressStepSize(progress_step_size) played_in_a_row_number = utils.settings("playedInARow") self.log( "played in a row settings %s" % json.dumps(played_in_a_row_number), 2) self.log("played in a row %s" % json.dumps(self.state.played_in_a_row), 2) showing_next_up_page = False showing_still_watching_page = False hide_for_short_videos = ( self.state.short_play_notification == "false") and (self.state.short_play_length >= total_time) and ( self.state.short_play_mode == "true") if int(self.state.played_in_a_row) <= int( played_in_a_row_number) and not hide_for_short_videos: self.log( "showing next up page as played in a row is %s" % json.dumps(self.state.played_in_a_row), 2) next_up_page.show() utils.window('service.upnext.dialog', 'true') showing_next_up_page = True elif not hide_for_short_videos: self.log( "showing still watching page as played in a row %s" % json.dumps(self.state.played_in_a_row), 2) still_watching_page.show() utils.window('service.upnext.dialog', 'true') showing_still_watching_page = True while (self.player.isPlaying() and (total_time - play_time > 1) and not next_up_page.isCancel() and not next_up_page.isWatchNow() and not still_watching_page.isStillWatching() and not still_watching_page.isCancel()): xbmc.sleep(100) try: play_time = self.player.getTime() total_time = self.player.getTotalTime() if showing_next_up_page: next_up_page.updateProgressControl() elif showing_still_watching_page: still_watching_page.updateProgressControl() except Exception as e: self.log("error show_popup_and_wait %s" % repr(e), 1) pass return showing_next_up_page, showing_still_watching_page, total_time def extract_play_info(self, next_up_page, showing_next_up_page, showing_still_watching_page, still_watching_page, total_time): if self.state.short_play_length >= total_time and self.state.short_play_mode == "true": # play short video and don't add to playcount self.state.played_in_a_row += 0 if next_up_page.isWatchNow( ) or still_watching_page.isStillWatching(): self.state.played_in_a_row = 1 should_play_default = not next_up_page.isCancel() else: if showing_next_up_page: next_up_page.close() utils.window('service.upnext.dialog', clear=True) should_play_default = not next_up_page.isCancel() should_play_non_default = next_up_page.isWatchNow() elif showing_still_watching_page: still_watching_page.close() utils.window('service.upnext.dialog', clear=True) should_play_default = still_watching_page.isStillWatching() should_play_non_default = still_watching_page.isStillWatching() if next_up_page.isWatchNow( ) or still_watching_page.isStillWatching(): self.state.played_in_a_row = 1 else: self.state.played_in_a_row += 1 return should_play_default, should_play_non_default
class PlaybackManager: _shared_state = {} def __init__(self): self.__dict__ = self._shared_state self.api = Api() self.play_item = PlayItem() self.state = State() self.player = Player() def log(self, msg, lvl=2): class_name = self.__class__.__name__ utils.log("%s %s" % (utils.addon_name(), class_name), msg, int(lvl)) def launch_up_next(self): episode = self.play_item.get_episode() if episode is None: # no episode get out of here self.log("Error: no episode could be found to play next...exiting", 1) return self.log("episode details %s" % json.dumps(episode), 2) self.launch_popup(episode) self.api.reset_addon_data() def launch_popup(self, episode): episode_id = episode["episodeid"] no_play_count = episode["playcount"] is None or episode["playcount"] == 0 include_play_count = True if self.state.include_watched else no_play_count if include_play_count and self.state.current_episode_id != episode_id: # we have a next up episode choose mode next_up_page, still_watching_page = pages.set_up_pages() showing_next_up_page, showing_still_watching_page, total_time = ( self.show_popup_and_wait(episode, next_up_page, still_watching_page)) should_play_default, should_play_non_default = ( self.extract_play_info(next_up_page, showing_next_up_page, showing_still_watching_page, still_watching_page, total_time)) play_item_option_1 = (should_play_default and self.state.playMode == "0") play_item_option_2 = (should_play_non_default and self.state.playMode == "1") if play_item_option_1 or play_item_option_2: self.log("playing media episode", 2) # Signal to trakt previous episode watched utils.event("NEXTUPWATCHEDSIGNAL", {'episodeid': self.state.current_episode_id}) # Play media if not self.api.has_addon_data(): self.api.play_kodi_item(episode) else: self.api.play_addon_item() def show_popup_and_wait(self, episode, next_up_page, still_watching_page): play_time = self.player.getTime() total_time = self.player.getTotalTime() progress_step_size = utils.calculate_progress_steps(total_time - play_time) next_up_page.setItem(episode) next_up_page.setProgressStepSize(progress_step_size) still_watching_page.setItem(episode) still_watching_page.setProgressStepSize(progress_step_size) played_in_a_row_number = utils.settings("playedInARow") self.log("played in a row settings %s" % json.dumps(played_in_a_row_number), 2) self.log("played in a row %s" % json.dumps(self.state.played_in_a_row), 2) showing_next_up_page = False showing_still_watching_page = False hide_for_short_videos = (self.state.short_play_notification == "false") and ( self.state.short_play_length >= total_time) and ( self.state.short_play_mode == "true") if int(self.state.played_in_a_row) <= int(played_in_a_row_number) and not hide_for_short_videos: self.log( "showing next up page as played in a row is %s" % json.dumps(self.state.played_in_a_row), 2) next_up_page.show() utils.window('service.upnext.dialog', 'true') showing_next_up_page = True elif not hide_for_short_videos: self.log( "showing still watching page as played in a row %s" % json.dumps(self.state.played_in_a_row), 2) still_watching_page.show() utils.window('service.upnext.dialog', 'true') showing_still_watching_page = True while (self.player.isPlaying() and ( total_time - play_time > 1) and not next_up_page.isCancel() and not next_up_page.isWatchNow() and not still_watching_page.isStillWatching() and not still_watching_page.isCancel()): xbmc.sleep(100) try: play_time = self.player.getTime() total_time = self.player.getTotalTime() if showing_next_up_page: next_up_page.updateProgressControl() elif showing_still_watching_page: still_watching_page.updateProgressControl() except Exception as e: self.log("error show_popup_and_wait %s" % repr(e), 1) pass return showing_next_up_page, showing_still_watching_page, total_time def extract_play_info(self, next_up_page, showing_next_up_page, showing_still_watching_page, still_watching_page, total_time): if self.state.short_play_length >= total_time and self.state.short_play_mode == "true": # play short video and don't add to playcount self.state.played_in_a_row += 0 if next_up_page.isWatchNow() or still_watching_page.isStillWatching(): self.state.played_in_a_row = 1 should_play_default = not next_up_page.isCancel() else: if showing_next_up_page: next_up_page.close() utils.window('service.upnext.dialog', clear=True) should_play_default = not next_up_page.isCancel() should_play_non_default = next_up_page.isWatchNow() elif showing_still_watching_page: still_watching_page.close() utils.window('service.upnext.dialog', clear=True) should_play_default = still_watching_page.isStillWatching() should_play_non_default = still_watching_page.isStillWatching() if next_up_page.isWatchNow() or still_watching_page.isStillWatching(): self.state.played_in_a_row = 1 else: self.state.played_in_a_row += 1 return should_play_default, should_play_non_default
def __init__(self): self.api = Api() self.developer = Developer() xbmc.Player.__init__(self)
from resources.lib.kodi.search_history import SearchHistory from resources.lib.kodi.settings import Settings from resources.lib.kodi.utils import format_bold from resources.lib.kodi.vfs import VFS from resources.routes import * addon = xbmcaddon.Addon() addon_id = addon.getAddonInfo("id") addon_base = "plugin://" + addon_id addon_profile_path = xbmcvfs.translatePath(addon.getAddonInfo("profile")) vfs = VFS(addon_profile_path) vfs_cache = VFS(os.path.join(addon_profile_path, "cache/")) settings = Settings(addon) cache = Cache(settings, vfs_cache) api = Api(settings, xbmc.getLanguage(xbmc.ISO_639_1), (vfs, vfs_cache), cache) search_history = SearchHistory(settings, vfs) listItems = Items(addon, addon_base, settings, search_history, vfs) def run(): url = urllib.parse.urlparse(sys.argv[0]) path = url.path handle = int(sys.argv[1]) args = urllib.parse.parse_qs(sys.argv[2][1:]) xbmcplugin.setContent(handle, "videos") if path == PATH_ROOT: action = args.get("action", None) if action is None: items = listItems.root()
class Monitor(xbmc.Monitor): def __init__(self): self.player = Player() self.api = Api() self.playback_manager = PlaybackManager() xbmc.Monitor.__init__(self) def log(self, msg, lvl=1): class_name = self.__class__.__name__ utils.log("%s %s" % (utils.addon_name(), class_name), str(msg), int(lvl)) def run(self): while not self.abortRequested(): # check every 1 sec if self.waitForAbort(1): # Abort was requested while waiting. We should exit break if self.player.is_tracking(): try: play_time = self.player.getTime() total_time = self.player.getTotalTime() last_file = self.player.get_last_file() current_file = self.player.getPlayingFile() notification_time = self.api.notification_time() up_next_disabled = utils.settings( "disableNextUp") == "true" if utils.window("PseudoTVRunning" ) != "True" and not up_next_disabled: if (total_time - play_time <= int(notification_time) and (last_file is None or last_file != current_file)) and total_time != 0: self.player.set_last_file(current_file) self.log( "Calling autoplayback totaltime - playtime is %s" % (total_time - play_time), 2) self.playback_manager.launch_up_next() self.log("Up Next style autoplay succeeded.", 2) self.player.disable_tracking() except Exception as e: self.log("Exception in Playback Monitor Service: %s" % repr(e)) if 'not playing any media file' in str(e): self.log("No file is playing - stop up next tracking.", 2) self.player.disable_tracking() self.log("======== STOP service.upnext ========", 0) def onNotification(self, sender, method, data): if method.split('.')[1].lower( ) != 'upnext_data': # method looks like Other.upnext_data return data = utils.decode_data(data) data['id'] = "%s_play_action" % str(sender.replace(".SIGNAL", "")) self.api.addon_data_received(data)
def setUp(self): self.api = Api(Settings(MagicMock()), "en", MagicMock(), MagicMock()) self.api.api_cdn = "fastly_skyfire" xbmcMock.getUserAgent = Mock(return_value="A User-Agent String")
class ApiTestCase(TestCase): def setUp(self): self.api = Api(Settings(MagicMock()), "en", MagicMock(), MagicMock()) self.api.api_cdn = "fastly_skyfire" xbmcMock.getUserAgent = Mock(return_value="A User-Agent String") def test_search_videos(self): with open("./tests/mocks/api_videos_search.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) self.api.video_av1 = True self.api.video_stream = "HLS (Adaptive)" res = self.api.search("foo", "bar") self.assertEqual(res.items[0].label, "kodi James") self.assertEqual(res.items[0].info["user"], "Foo User") self.assertEqual(res.items[0].uri, "/videos/13101116") self.assertEqual(res.items[0].hasSubtitles, False) self.assertEqual(res.items[0].thumb, "https://i.vimeocdn.com/video/74666133_200x150.jpg?r=pad") self.assertEqual(res.items[1].label, "Kodi Sings") self.assertEqual(res.items[1].info["user"], "Bar User") self.assertEqual(res.items[1].uri, "/videos/339780805") self.assertEqual(res.items[1].hasSubtitles, False) self.assertEqual(res.items[1].thumb, "https://i.vimeocdn.com/video/787910745_200x150.jpg?r=pad") # The third item is not playable, so it should not be listed self.assertEqual(len(res.items), 2) def test_search_videos_no_media_urls(self): with open("./tests/mocks/api_videos_search_fallback.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) self.api.video_stream = "720p" res = self.api.search("foo", "videos") self.assertEqual(res.items[0].uri, "/videos/13101116") def test_search_videos_on_demand(self): with open("./tests/mocks/api_videos_search_on_demand.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) self.api.video_stream = "HLS (Adaptive)" res = self.api.search("foo", "videos") self.assertEqual(res.items[0].uri, "/videos/372251058") self.assertEqual(res.items[0].info["onDemand"], False) self.assertEqual(res.items[1].uri, "/ondemand/pages/25096/videos/97663163") self.assertEqual(res.items[1].info["onDemand"], True) self.assertEqual(res.items[2].uri, "/videos/31158028") self.assertEqual(res.items[2].info["onDemand"], False) def test_search_videos_live(self): with open("./tests/mocks/api_videos_search_live.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) self.api.video_stream = "HLS (Adaptive)" res = self.api.search("foo", "videos") self.assertEqual(res.items[0].uri, "/videos/401626792") self.assertEqual(res.items[0].info["live"], True) self.assertEqual(res.items[1].uri, "/videos/76321431") self.assertEqual(res.items[1].info["live"], False) def test_search_users(self): with open("./tests/mocks/api_users_search.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.search("foo", "bar") self.assertEqual(res.items[0].label, "Petzl-sport") self.assertEqual(res.items[0].data["location"], "France") self.assertEqual(res.items[1].label, "DSN Digital Sport Network") self.assertEqual(res.items[1].data["location"], "") def test_search_channels(self): with open("./tests/mocks/api_channels_search.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.search("foo", "bar") self.assertEqual(res.items[0].label, "Sport") self.assertEqual(res.items[0].thumb, "https://i.vimeocdn.com/video/801804973_640x360.jpg?r=pad") self.assertEqual(res.items[0].uri, "/channels/1084121/videos") self.assertEqual(res.items[1].label, "Jonica Sport") self.assertEqual(res.items[1].thumb, "https://i.vimeocdn.com/video/684400644_640x360.jpg?r=pad") self.assertEqual(res.items[1].uri, "/channels/452847/videos") def test_search_groups(self): with open("./tests/mocks/api_groups_search.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.search("foo", "bar") self.assertEqual(res.items[0].label, "Action Sport - Action Brothers") self.assertEqual(res.items[0].thumb, "https://i.vimeocdn.com/video/804634360_640x360.jpg?r=pad") self.assertEqual(res.items[0].uri, "/groups/15103/videos") self.assertEqual(res.items[1].label, "Sport") self.assertEqual(res.items[1].thumb, "https://i.vimeocdn.com/video/804929738_640x360.jpg?r=pad") self.assertEqual(res.items[1].uri, "/groups/809/videos") def test_channel_videos(self): with open("./tests/mocks/api_videos_channel.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) self.api.video_stream = "720p" res = self.api.channel("1") self.assertEqual(res.items[0].label, "The Pet Files") self.assertEqual(res.items[0].thumb, "https://i.vimeocdn.com/video/857679735_200x150.jpg?r=pad") self.assertEqual(res.items[0].uri, "/videos/392544832") def test_categories(self): with open("./tests/mocks/api_categories.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.categories() self.assertEqual(res.items[0].label, "Animation") self.assertEqual(res.items[0].thumb, "https://i.vimeocdn.com/video/858725975_640x360.jpg?r=pad") self.assertEqual(res.items[0].uri, "/categories/animation/videos") self.assertEqual(res.items[1].label, "Travel") self.assertEqual(res.items[1].thumb, "https://i.vimeocdn.com/video/649307891_640x360.jpg?r=pad") self.assertEqual(res.items[1].uri, "/categories/travel/videos") def test_trending(self): with open("./tests/mocks/api_videos_trending.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.categories() self.assertEqual(res.items[0].label, "Feeling Love for Filmfest Dresden") self.assertEqual(res.items[1].label, "Lecture: The Meeting with Nadav Kander") self.assertEqual(res.items[2].label, "Stay Home") def test_resolve_id(self): with open("./tests/mocks/api_videos_detail.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_id("352494023") self.assertEqual(res.items[0].label, "Beautiful Chaos") self.assertEqual(res.items[0].thumb, "https://i.vimeocdn.com/video/804395055_200x150.jpg?r=pad") self.assertEqual(res.items[0].uri, "/videos/352494023") with open("./tests/mocks/api_videos_detail_live.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_id("401749070") self.assertEqual(res.items[0].label, "Vespers & Benediction: 6PM (CT)") self.assertEqual(res.items[0].thumb, "https://i.vimeocdn.com/video/default-live_200x150?r=pad") self.assertEqual(res.items[0].uri, "/videos/401749070") with open("./tests/mocks/api_videos_detail_unlisted.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_id("355062058:5293454954") self.assertEqual(res.items[0].label, "An unlisted Vimeo Video") self.assertEqual(res.items[0].uri, "/videos/355062058:5293454954") def test_resolve_id_password_protected(self): with open("./tests/mocks/api_videos_detail_invalid_params.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) self.assertRaises(PasswordRequiredException, self.api.resolve_id, "216913310") def test_resolve_id_texttracks(self): with open("./tests/mocks/api_videos_detail_texttracks.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_id("140786188") self.assertEqual(res.items[0].label, "Impardonnable (English subtitle)") self.assertEqual(res.items[0].uri, "/videos/140786188") self.assertEqual(res.items[0].hasSubtitles, True) def test_resolve_media_url(self): with open("./tests/mocks/api_videos_detail.json") as f: mock_data = f.read() # Progressive self.api.video_av1 = False self.api.video_stream = "360p" self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/352494023") self.assertEqual(res, "https://vimeo-prod-skyfire-std-us.storage.googleapis.com/01/498/14/352494023/1430794413.mp4") # Progressive (AV1) self.api.video_av1 = True self.api.video_stream = "1080p" self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/352494023") self.assertEqual(res, "https://vimeo-prod-skyfire-std-us.storage.googleapis.com/01/498/14/352494023/1446202906.mp4") # Progressive (fallback) self.api.video_av1 = False self.api.video_stream = "720p" # Resolution does not exist in API response self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/352494023") self.assertEqual(res, "https://vimeo-prod-skyfire-std-us.storage.googleapis.com/01/498/14/352494023/1430794570.mp4") # HLS stream self.api.video_stream = "HLS (Adaptive)" self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/352494023") self.assertEqual(res, "https://player.vimeo.com/play/1446216704/hls") with open("./tests/mocks/api_videos_detail_live.json") as f: mock_data = f.read() # Live stream (HLS) self.api.video_stream = "HLS (Adaptive)" self.api.video_av1 = True # Avoids HTTP request in `api._hls_playlist_without_av1_streams` self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/401749070") self.assertEqual(res, "https://player.vimeo.com/live/7e80cc02-afdd-48fc-9a49-95078c7fbcd3/playlist/hls") # Live stream (HLS fallback) self.api.video_stream = "720p" self.api.video_av1 = True # Avoids HTTP request in `api._hls_playlist_without_av1_streams` self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/401749070") self.assertEqual(res, "https://player.vimeo.com/live/7e80cc02-afdd-48fc-9a49-95078c7fbcd3/playlist/hls") def test_resolve_media_url_on_demand(self): with open("./tests/mocks/api_ondemand_video.json") as f: mock_data = f.read() self.api._do_api_request = Mock(return_value=json.loads(mock_data)) self.api.video_stream = "HLS (Adaptive)" res = self.api.resolve_media_url("/ondemand/pages/25096") self.assertEqual(res, "https://player.vimeo.com/play/260864877/hls") def test_resolve_media_url_fallback(self): with open("./tests/mocks/player_video_config.json") as f: mock_data = f.read() self.api.api_fallback = True self.api.video_av1 = False # Progressive self.api.video_stream = "360p" self.api._do_player_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/13101116") self.assertEqual(res, "https://gcs-vimeo.akamaized.net/exp=1570994045~acl=%2A%2F1363060449.mp4%shortened") # Progressive (fallback) self.api.video_stream = "720p" self.api._do_player_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/13101116") self.assertEqual(res, "https://gcs-vimeo.akamaized.net/exp=1570994045~acl=%2A%2F1363060455.mp4%shortened") with open("./tests/mocks/player_video_config_av1.json") as f: mock_data = f.read() self.api.video_av1 = False # HLS stream self.api.video_stream = "HLS (Adaptive)" self.api._do_player_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/13101116") self.assertEqual(res, "https://skyfire.vimeocdn.com/avc") self.api.video_av1 = True # HLS stream (AV1) self.api.video_stream = "HLS (Adaptive)" self.api._do_player_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/13101116") self.assertEqual(res, "https://skyfire.vimeocdn.com/av1") def test_resolve_media_url_macos(self): with open("./tests/mocks/api_videos_detail.json") as f: mock_data = f.read() xbmcMock.getUserAgent = Mock(return_value="A Mac OS X User Agent") self.api.video_stream = "1080p" self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_media_url("/videos/123") self.assertEqual(res, "http://a.b/c.mp4|User-Agent=pyvimeo%201.2.0%3B%20%28http%3A//developer.vimeo.com/api/docs%29") @skip("Can't easily mock the Vimeo client") def test_authorize(self): with open("./tests/mocks/api_authorize.json") as f: mock_data = json.loads(f.read()) # self.api.api_client.device_code_authorize = Mock(return_value=(mock_data["access_token"], mock_data["user"], mock_data["scope"])) # res = self.api.oauth_device_authorize("72QX6Y", "d1d80ccf128de92517eaac61aad2b539ce2d5af9") # self.assertEqual(res, "Philip Gray") def test_text_tracks(self): with open("./tests/mocks/web.vtt") as f: mock_data_webvtt = f.read() with open("./tests/mocks/api_videos_texttracks.json") as f: mock_data = f.read() self.api._do_request = Mock(return_value=mock_data_webvtt) self.api._do_api_request = Mock(return_value=json.loads(mock_data)) res = self.api.resolve_texttracks("/videos/12345/texttracks") self.assertEqual(res[0]["uri"], "/videos/503121159/texttracks/11718998") self.assertEqual(res[0]["language"], "en-US") self.assertTrue(res[0]["srt"].startswith("1\n00:00:08,850 --> 00:00:10,350\nMy love.\n\n")) self.assertEqual(res[1]["uri"], "/videos/503121159/texttracks/11719013") self.assertEqual(res[1]["language"], "fr")