Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-casttube for openSUSE:Factory checked in at 2022-01-15 20:05:16 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-casttube (Old) and /work/SRC/openSUSE:Factory/.python-casttube.new.1892 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-casttube" Sat Jan 15 20:05:16 2022 rev:2 rq:946642 version:0.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-casttube/python-casttube.changes 2019-01-03 18:08:40.628031147 +0100 +++ /work/SRC/openSUSE:Factory/.python-casttube.new.1892/python-casttube.changes 2022-01-15 20:05:28.433771828 +0100 @@ -1,0 +2,7 @@ +Sat Jan 15 15:29:36 UTC 2022 - Dirk M??ller <dmuel...@suse.com> + +- update to 0.2.1: + * You can now set a starting time for a video + * Session data and queued videos info + +------------------------------------------------------------------- Old: ---- LICENSE casttube-0.2.0.tar.gz New: ---- casttube-0.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-casttube.spec ++++++ --- /var/tmp/diff_new_pack.GsEuzQ/_old 2022-01-15 20:05:28.861772162 +0100 +++ /var/tmp/diff_new_pack.GsEuzQ/_new 2022-01-15 20:05:28.865772165 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-casttube # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,14 +18,13 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-casttube -Version: 0.2.0 +Version: 0.2.1 Release: 0 Summary: YouTube chromecast api License: MIT Group: Development/Languages/Python -Url: http://github.com/ur1katz/casttube +URL: http://github.com/ur1katz/casttube Source: https://files.pythonhosted.org/packages/source/c/casttube/casttube-%{version}.tar.gz -Source1: https://raw.githubusercontent.com/ur1katz/casttube/master/LICENSE BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -38,7 +37,6 @@ %prep %setup -q -n casttube-%{version} -cp %{SOURCE1} . %build %python_build @@ -46,6 +44,7 @@ %install %python_install %python_expand %fdupes %{buildroot}%{$python_sitelib} +rm -v %{buildroot}%{_prefix}/LICENSE %files %{python_files} %doc README.md ++++++ casttube-0.2.0.tar.gz -> casttube-0.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/casttube-0.2.0/LICENSE new/casttube-0.2.1/LICENSE --- old/casttube-0.2.0/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/casttube-0.2.1/LICENSE 2018-06-01 09:23:27.000000000 +0200 @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Uri Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/casttube-0.2.0/PKG-INFO new/casttube-0.2.1/PKG-INFO --- old/casttube-0.2.0/PKG-INFO 2018-09-21 07:48:58.000000000 +0200 +++ new/casttube-0.2.1/PKG-INFO 2020-04-08 14:46:03.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: casttube -Version: 0.2.0 +Version: 0.2.1 Summary: YouTube chromecast api Home-page: http://github.com/ur1katz/casttube Author: Uri Katz diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/casttube-0.2.0/casttube/YouTubeSession.py new/casttube-0.2.1/casttube/YouTubeSession.py --- old/casttube-0.2.0/casttube/YouTubeSession.py 2018-06-16 09:47:44.000000000 +0200 +++ new/casttube-0.2.1/casttube/YouTubeSession.py 2020-04-08 14:30:51.000000000 +0200 @@ -1,15 +1,19 @@ import re +import json +from html.parser import HTMLParser import requests YOUTUBE_BASE_URL = "https://www.youtube.com/" BIND_URL = YOUTUBE_BASE_URL + "api/lounge/bc/bind" LOUNGE_TOKEN_URL = YOUTUBE_BASE_URL + "api/lounge/pairing/get_lounge_token_batch" +QUEUE_AJAX_URL = YOUTUBE_BASE_URL + "watch_queue_ajax" HEADERS = {"Origin": YOUTUBE_BASE_URL, "Content-Type": "application/x-www-form-urlencoded"} LOUNGE_ID_HEADER = "X-YouTube-LoungeId-Token" REQ_PREFIX = "req{req_id}" +WATCH_QUEUE_ITEM_CLASS = 'yt-uix-scroller-scroll-unit watch-queue-item' GSESSION_ID_REGEX = '"S","(.*?)"]' SID_REGEX = '"c","(.*?)",\"' @@ -26,18 +30,35 @@ ACTION_REMOVE = "removeVideo" ACTION_INSERT = "insertVideo" ACTION_ADD = "addVideo" - +ACTION_GET_QUEUE_ITEMS = "action_get_watch_queue_items" GSESSIONID = "gsessionid" +LOUNGEIDTOKEN = "loungeIdToken" CVER = "CVER" +TYPE = "TYPE" RID = "RID" SID = "SID" VER = "VER" +AID = "AID" +CI = "CI" BIND_DATA = {"device": "REMOTE_CONTROL", "id": "aaaaaaaaaaaaaaaaaaaaaaaaaa", "name": "Python", "mdx-version": 3, "pairing_type": "cast", "app": "android-phone-13.14.55"} +class QueueHTMLParser(HTMLParser): + def __init__(self): + self.queue_items = [] + super().__init__() + + def handle_starttag(self, tag, attrs): + if tag == "li": + attributes = dict((x, y) for x, y in attrs) + if 'class' in attributes.keys(): + if attributes['class'] == WATCH_QUEUE_ITEM_CLASS: + self.queue_items.append(attributes) + + class YouTubeSession(object): """ The main logic to interact with YouTube cast api.""" @@ -57,15 +78,16 @@ else: return False - def play_video(self, video_id, list_id=""): + def play_video(self, video_id, list_id="", start_time="0"): """ Play video(video_id) now. This ignores the current play queue order. - :param list_id: list id for playing playlist ...youtube.com/watch?v=VIDEO_ID&list=LIST_ID :param video_id: YouTube video id(http://youtube.com/watch?v=video_id) + :param list_id: list id for playing playlist ...youtube.com/watch?v=VIDEO_ID&list=LIST_ID + :param start_time: starting time of the video in seconds """ # We always want to start a new session here to ensure an empty queue. self._start_session() - self._initialize_queue(video_id, list_id) + self._initialize_queue(video_id, list_id, start_time) def add_to_queue(self, video_id): """ @@ -91,6 +113,49 @@ def clear_playlist(self): self._queue_action('', ACTION_CLEAR) + def get_session_data(self): + """ + Get data about the current active session using an xmlhttp request. + :return: List of session attributes + """ + url_params = {LOUNGEIDTOKEN: self._lounge_token, VER: 8, "v": 2, RID: "rpc", SID: self._sid, + GSESSIONID: self._gsession_id, TYPE: "xmlhttp", "t": 1, AID: 5, CI: 1} + url_params.update(BIND_DATA) + response = self._do_post(BIND_URL, headers={LOUNGE_ID_HEADER: self._lounge_token}, + session_request=True, params=url_params) + response_text = response.text + response_text = response_text.replace("\n", "") + response_list = json.loads(response_text[response_text.find("["):]) + response_list = [v for k, v in response_list] + return response_list + + def get_queue_playlist_id(self): + """ + Get the current queue playlist id. + :return: queue playlist id or None + """ + session_data = self.get_session_data() + for v in session_data: + if v[0] == "nowPlaying": + if v[1]["listId"]: + return v[1]["listId"] + return None + + def get_queue_videos(self): + """ + Get the video id, video title and uploader username for videos currently in the queue. + :return: index, video id, title, username or {} if no active playlist id is found for the session + """ + queue_playlist_id = self.get_queue_playlist_id() + if not queue_playlist_id: + return {} + url_params = {ACTION_GET_QUEUE_ITEMS: 1, "list": queue_playlist_id} + response = self._do_post(QUEUE_AJAX_URL, headers={LOUNGE_ID_HEADER: self._lounge_token}, + session_request=False, params=url_params) + parser = QueueHTMLParser() + parser.feed(response.json()['html']) + return parser.queue_items + def _start_session(self): self._get_lounge_id() self._bind() @@ -126,13 +191,13 @@ self._sid = sid.group(1) self._gsession_id = gsessionid.group(1) - def _initialize_queue(self, video_id, list_id=""): + def _initialize_queue(self, video_id, list_id="", start_time="0"): """ Initialize a queue with a video and start playing that video. """ request_data = {LIST_ID: list_id, ACTION: ACTION_SET_PLAYLIST, - CURRENT_TIME: "0", + CURRENT_TIME: start_time, CURRENT_INDEX: -1, AUDIO_ONLY: "false", VIDEO_ID: video_id, @@ -172,7 +237,7 @@ req_count = REQ_PREFIX.format(req_id=self._req_count) return {req_count + k if k.startswith("_") else k: v for k, v in param_dict.items()} - def _do_post(self, url, data, params=None, headers=None, session_request=False): + def _do_post(self, url, data=None, params=None, headers=None, session_request=False): """ Calls requests.post with custom headers, increments RID(request id) on every post. @@ -190,7 +255,6 @@ else: headers = HEADERS response = requests.post(url, headers=headers, data=data, params=params) - # 404 resets the sid, session counters # 400 in session probably means bad sid # If user did a bad request (eg. remove an non-existing video from queue) bind restores the session. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/casttube-0.2.0/casttube.egg-info/PKG-INFO new/casttube-0.2.1/casttube.egg-info/PKG-INFO --- old/casttube-0.2.0/casttube.egg-info/PKG-INFO 2018-09-21 07:48:58.000000000 +0200 +++ new/casttube-0.2.1/casttube.egg-info/PKG-INFO 2020-04-08 14:46:03.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: casttube -Version: 0.2.0 +Version: 0.2.1 Summary: YouTube chromecast api Home-page: http://github.com/ur1katz/casttube Author: Uri Katz diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/casttube-0.2.0/casttube.egg-info/SOURCES.txt new/casttube-0.2.1/casttube.egg-info/SOURCES.txt --- old/casttube-0.2.0/casttube.egg-info/SOURCES.txt 2018-09-21 07:48:58.000000000 +0200 +++ new/casttube-0.2.1/casttube.egg-info/SOURCES.txt 2020-04-08 14:46:03.000000000 +0200 @@ -1,3 +1,4 @@ +LICENSE README.md setup.cfg setup.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/casttube-0.2.0/setup.py new/casttube-0.2.1/setup.py --- old/casttube-0.2.0/setup.py 2018-09-21 07:34:25.000000000 +0200 +++ new/casttube-0.2.1/setup.py 2020-04-08 14:40:53.000000000 +0200 @@ -4,7 +4,7 @@ long_description = fh.read() setup(name='casttube', - version='0.2.0', + version='0.2.1', description='YouTube chromecast api', long_description=long_description, long_description_content_type="text/markdown", @@ -12,6 +12,7 @@ author='Uri Katz', author_email='4urik...@gmail.com', license='MIT', + data_files = [("", ["LICENSE"])], packages=['casttube'], zip_safe=False, keywords = ['youtube', 'chromecast', 'youtube-api'],