[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex.changes
|
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex.spec
^
|
|
[-]
[+]
|
Changed |
_service
^
|
@@ -2,7 +2,7 @@
<service name="tar_git">
<param name="url">https://github.com/poetaster/musicex.git</param>
<param name="branch">main</param>
- <param name="revision">0.44</param>
+ <param name="revision">0.45</param>
<param name="debian">N</param>
<param name="dumb">N</param>
</service>
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/harbour-musicex.desktop
^
|
@@ -5,12 +5,12 @@
X-Nemo-Application-Type=generic
Name=Music Explorer
Icon=harbour-musicex
-Exec=sailjail -p harbour-musicex.desktop /usr/bin/sailfish-qml harbour-musicex
+Exec=sailfish-qml harbour-musicex
X-Desktop-File-Install-Version=0.26
[X-Sailjail]
-Permissions=Internet;Audio;Bluetooth;Downloads;MediaIndexing;Microphone;Music;NFC;PublicDir;RemovableMedia;UserDirs;Videos;WebView
+Permissions=Internet;Audio;Bluetooth;Downloads;MediaIndexing;Music;PublicDir;RemovableMedia;UserDirs;Videos;WebView
OrganizationName=app.qml
ApplicationName=musicex
|
[-]
[+]
|
Added |
_service:tar_git:harbour-musicex-0.45.tar.bz2/qml/pages/WebViewPage_4.qml
^
|
@@ -0,0 +1,19 @@
+import QtQuick 2.2
+import Sailfish.Silica 1.0
+import Sailfish.WebView 1.0
+import Sailfish.WebEngine 1.0
+
+Page {
+ property alias url: web_view.url
+
+ WebView {
+ id: web_view
+
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ }
+}
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/__main__.py
^
|
@@ -465,3 +465,15 @@
"""
self.stream_monostate.on_complete = func
+
+ @staticmethod
+ def from_id(video_id: str) -> "YouTube":
+ """Construct a :class:`YouTube <YouTube>` object from a video id.
+
+ :param str video_id:
+ The video id of the YouTube video.
+
+ :rtype: :class:`YouTube <YouTube>`
+
+ """
+ return YouTube(f"https://www.youtube.com/watch?v={video_id}")
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/captions.py
^
|
@@ -1,6 +1,7 @@
import math
import os
import time
+import json
import xml.etree.ElementTree as ElementTree
from html import unescape
from typing import Dict, Optional
@@ -42,6 +43,15 @@
"""Download the xml caption tracks."""
return request.get(self.url)
+ @property
+ def json_captions(self) -> dict:
+ """Download and parse the json caption tracks."""
+ json_captions_url = self.url.replace('fmt=srv3','fmt=json3')
+ text = request.get(json_captions_url)
+ parsed = json.loads(text)
+ assert parsed['wireMagic'] == 'pb3', 'Unexpected captions format'
+ return parsed
+
def generate_srt_captions(self) -> str:
"""Generate "SubRip Subtitle" captions.
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/cli.py
^
|
@@ -161,7 +161,7 @@
const="mp4",
nargs="?",
help=(
- "Download the audio for a given URL at the highest bitrate available"
+ "Download the audio for a given URL at the highest bitrate available. "
"Defaults to mp4 format if none is specified"
),
)
@@ -171,8 +171,8 @@
const="best",
nargs="?",
help=(
- "Downloads the audio and video stream for resolution provided"
- "If no resolution is provided, downloads the best resolution"
+ "Downloads the audio and video stream for resolution provided. "
+ "If no resolution is provided, downloads the best resolution. "
"Runs the command line program ffmpeg to combine the audio and video"
),
)
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/contrib/search.py
^
|
@@ -150,10 +150,10 @@
continue
if 'videoRenderer' not in video_details:
- logger.warn('Unexpected renderer encountered.')
- logger.warn(f'Renderer name: {video_details.keys()}')
- logger.warn(f'Search term: {self.query}')
- logger.warn(
+ logger.warning('Unexpected renderer encountered.')
+ logger.warning(f'Renderer name: {video_details.keys()}')
+ logger.warning(f'Search term: {self.query}')
+ logger.warning(
'Please open an issue at '
'https://github.com/pytube/pytube/issues '
'and provide this log output.'
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/innertube.py
^
|
@@ -36,37 +36,182 @@
'clientVersion': '2.20200720.00.02'
}
},
+ 'header': {
+ 'User-Agent': 'Mozilla/5.0'
+ },
'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
},
'ANDROID': {
'context': {
'client': {
'clientName': 'ANDROID',
- 'clientVersion': '16.20'
+ 'clientVersion': '17.31.35',
+ 'androidSdkVersion': 30
}
},
+ 'header': {
+ 'User-Agent': 'com.google.android.youtube/',
+ },
'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
},
+ 'IOS': {
+ 'context': {
+ 'client': {
+ 'clientName': 'IOS',
+ 'clientVersion': '17.33.2',
+ 'deviceModel': 'iPhone14,3'
+ }
+ },
+ 'header': {
+ 'User-Agent': 'com.google.ios.youtube/'
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
+
'WEB_EMBED': {
'context': {
'client': {
- 'clientName': 'WEB',
+ 'clientName': 'WEB_EMBEDDED_PLAYER',
'clientVersion': '2.20210721.00.00',
'clientScreen': 'EMBED'
}
},
+ 'header': {
+ 'User-Agent': 'Mozilla/5.0'
+ },
'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
},
'ANDROID_EMBED': {
'context': {
'client': {
- 'clientName': 'ANDROID',
- 'clientVersion': '16.20',
- 'clientScreen': 'EMBED'
+ 'clientName': 'ANDROID_EMBEDDED_PLAYER',
+ 'clientVersion': '17.31.35',
+ 'clientScreen': 'EMBED',
+ 'androidSdkVersion': 30,
+ }
+ },
+ 'header': {
+ 'User-Agent': 'com.google.android.youtube/'
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
+ 'IOS_EMBED': {
+ 'context': {
+ 'client': {
+ 'clientName': 'IOS_MESSAGES_EXTENSION',
+ 'clientVersion': '17.33.2',
+ 'deviceModel': 'iPhone14,3'
+ }
+ },
+ 'header': {
+ 'User-Agent': 'com.google.ios.youtube/'
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
+
+ 'WEB_MUSIC': {
+ 'context': {
+ 'client': {
+ 'clientName': 'WEB_REMIX',
+ 'clientVersion': '1.20220727.01.00',
}
},
+ 'header': {
+ 'User-Agent': 'Mozilla/5.0'
+ },
'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
- }
+ },
+ 'ANDROID_MUSIC': {
+ 'context': {
+ 'client': {
+ 'clientName': 'ANDROID_MUSIC',
+ 'clientVersion': '5.16.51',
+ 'androidSdkVersion': 30
+ }
+ },
+ 'header': {
+ 'User-Agent': 'com.google.android.apps.youtube.music/'
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
+ 'IOS_MUSIC': {
+ 'context': {
+ 'client': {
+ 'clientName': 'IOS_MUSIC',
+ 'clientVersion': '5.21',
+ 'deviceModel': 'iPhone14,3'
+ }
+ },
+ 'header': {
+ 'User-Agent': 'com.google.ios.youtubemusic/'
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
+
+ 'WEB_CREATOR': {
+ 'context': {
+ 'client': {
+ 'clientName': 'WEB_CREATOR',
+ 'clientVersion': '1.20220726.00.00',
+ }
+ },
+ 'header': {
+ 'User-Agent': 'Mozilla/5.0'
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
+ 'ANDROID_CREATOR': {
+ 'context': {
+ 'client': {
+ 'clientName': 'ANDROID_CREATOR',
+ 'clientVersion': '22.30.100',
+ 'androidSdkVersion': 30,
+ }
+ },
+ 'header': {
+ 'User-Agent': 'com.google.android.apps.youtube.creator/',
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
+ 'IOS_CREATOR': {
+ 'context': {
+ 'client': {
+ 'clientName': 'IOS_CREATOR',
+ 'clientVersion': '22.33.101',
+ 'deviceModel': 'iPhone14,3',
+ }
+ },
+ 'header': {
+ 'User-Agent': 'com.google.ios.ytcreator/'
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
+
+ 'MWEB': {
+ 'context': {
+ 'client': {
+ 'clientName': 'MWEB',
+ 'clientVersion': '2.20220801.00.00',
+ }
+ },
+ 'header': {
+ 'User-Agent': 'Mozilla/5.0'
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
+
+ 'TV_EMBED': {
+ 'context': {
+ 'client': {
+ 'clientName': 'TVHTML5_SIMPLY_EMBEDDED_PLAYER',
+ 'clientVersion': '2.0',
+ }
+ },
+ 'header': {
+ 'User-Agent': 'Mozilla/5.0'
+ },
+ 'api_key': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ },
}
_token_timeout = 1800
_cache_dir = pathlib.Path(__file__).parent.resolve() / '__cache__'
@@ -75,7 +220,7 @@
class InnerTube:
"""Object for interacting with the innertube API."""
- def __init__(self, client='ANDROID', use_oauth=False, allow_cache=True):
+ def __init__(self, client='ANDROID_MUSIC', use_oauth=False, allow_cache=True):
"""Initialize an InnerTube object.
:param str client:
@@ -87,6 +232,7 @@
Allows caching of oauth tokens on the machine.
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/itags.py
^
|
@@ -91,6 +91,15 @@
401: ("2160p", None), # MP4
402: ("4320p", None), # MP4
571: ("4320p", None), # MP4
+ 694: ("144p", None), # MP4
+ 695: ("240p", None), # MP4
+ 696: ("360p", None), # MP4
+ 697: ("480p", None), # MP4
+ 698: ("720p", None), # MP4
+ 699: ("1080p", None), # MP4
+ 700: ("1440p", None), # MP4
+ 701: ("2160p", None), # MP4
+ 702: ("4320p", None), # MP4
}
DASH_AUDIO = {
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/parser.py
^
|
@@ -74,18 +74,23 @@
# First letter MUST be a open brace, so we put that in the stack,
# and skip the first character.
+ last_char = '{'
+ curr_char = None
stack = [html[0]]
i = 1
context_closers = {
'{': '}',
'[': ']',
- '"': '"'
+ '"': '"',
+ '/': '/' # javascript regex
}
while i < len(html):
if len(stack) == 0:
break
+ if curr_char not in [' ', '\n']:
+ last_char = curr_char
curr_char = html[i]
curr_context = stack[-1]
@@ -95,17 +100,19 @@
i += 1
continue
- # Strings require special context handling because they can contain
+ # Strings and regex expressions require special context handling because they can contain
# context openers *and* closers
- if curr_context == '"':
- # If there's a backslash in a string, we skip a character
+ if curr_context in ['"', '/']:
+ # If there's a backslash in a string or regex expression, we skip a character
if curr_char == '\\':
i += 2
continue
else:
# Non-string contexts are when we need to look for context openers.
if curr_char in context_closers.keys():
- stack.append(curr_char)
+ # Slash starts a regular expression depending on context
+ if not (curr_char == '/' and last_char not in ['(', ',', '=', ':', '[', '!', '&', '|', '?', '{', '}', ';']):
+ stack.append(curr_char)
i += 1
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/query.py
^
|
@@ -123,7 +123,10 @@
"""
filters = []
if res or resolution:
- filters.append(lambda s: s.resolution == (res or resolution))
+ if isinstance(res, str) or isinstance(resolution, str):
+ filters.append(lambda s: s.resolution == (res or resolution))
+ elif isinstance(res, list) or isinstance(resolution, list):
+ filters.append(lambda s: s.resolution in (res or resolution))
if fps:
filters.append(lambda s: s.fps == fps)
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/request.py
^
|
@@ -155,9 +155,8 @@
# Try to execute the request, ignoring socket timeouts
try:
response = _execute_request(
- url,
+ url + f"&range={downloaded}-{stop_pos}",
method="GET",
- headers={"Range": range_header},
timeout=timeout
)
except URLError as e:
@@ -177,8 +176,13 @@
if file_size == default_range_size:
try:
- content_range = response.info()["Content-Range"]
- file_size = int(content_range.split("/")[1])
+ resp = _execute_request(
+ url + f"&range={0}-{99999999999}",
+ method="GET",
+ timeout=timeout
+ )
+ content_range = resp.info()["Content-Length"]
+ file_size = int(content_range)
except (KeyError, IndexError, ValueError) as e:
logger.error(e)
while True:
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/streams.py
^
|
@@ -8,6 +8,8 @@
"""
import logging
import os
+from math import ceil
+
from datetime import datetime
from typing import BinaryIO, Dict, Optional, Tuple
from urllib.error import HTTPError
@@ -61,6 +63,15 @@
# filesize in bytes
self._filesize: Optional[int] = int(stream.get('contentLength', 0))
+
+ # filesize in kilobytes
+ self._filesize_kb: Optional[float] = float(ceil(float(stream.get('contentLength', 0)) / 1024 * 1000) / 1000)
+
+ # filesize in megabytes
+ self._filesize_mb: Optional[float] = float(ceil(float(stream.get('contentLength', 0)) / 1024 / 1024 * 1000) / 1000)
+
+ # filesize in gigabytes(fingers crossed we don't need terabytes going forward though)
+ self._filesize_gb: Optional[float] = float(ceil(float(stream.get('contentLength', 0)) / 1024 / 1024 / 1024 * 1000) / 1000)
# Additional information about the stream format, such as resolution,
# frame rate, and whether the stream is live (HLS) or 3D.
@@ -149,7 +160,58 @@
raise
self._filesize = request.seq_filesize(self.url)
return self._filesize
+
+ @property
+ def filesize_kb(self) -> float:
+ """File size of the media stream in kilobytes.
+ :rtype: float
+ :returns:
+ Rounded filesize (in kilobytes) of the stream.
+ """
+ if self._filesize_kb == 0:
+ try:
+ self._filesize_kb = float(ceil(request.filesize(self.url)/1024 * 1000) / 1000)
+ except HTTPError as e:
+ if e.code != 404:
+ raise
+ self._filesize_kb = float(ceil(request.seq_filesize(self.url)/1024 * 1000) / 1000)
+ return self._filesize_kb
+
+ @property
+ def filesize_mb(self) -> float:
+ """File size of the media stream in megabytes.
+
+ :rtype: float
+ :returns:
+ Rounded filesize (in megabytes) of the stream.
+ """
+ if self._filesize_mb == 0:
+ try:
+ self._filesize_mb = float(ceil(request.filesize(self.url)/1024/1024 * 1000) / 1000)
+ except HTTPError as e:
+ if e.code != 404:
+ raise
+ self._filesize_mb = float(ceil(request.seq_filesize(self.url)/1024/1024 * 1000) / 1000)
+ return self._filesize_mb
+
+ @property
+ def filesize_gb(self) -> float:
+ """File size of the media stream in gigabytes.
+
+ :rtype: float
+ :returns:
+ Rounded filesize (in gigabytes) of the stream.
+ """
+ if self._filesize_gb == 0:
+ try:
+ self._filesize_gb = float(ceil(request.filesize(self.url)/1024/1024/1024 * 1000) / 1000)
+ except HTTPError as e:
+ if e.code != 404:
+ raise
+ self._filesize_gb = float(ceil(request.seq_filesize(self.url)/1024/1024/1024 * 1000) / 1000)
+ return self._filesize_gb
+
@property
def title(self) -> str:
"""Get title of video
|
[-]
[+]
|
Changed |
_service:tar_git:harbour-musicex-0.45.tar.bz2/src/pytube/version.py
^
|
@@ -1,4 +1,4 @@
-__version__ = "12.1.0"
+__version__ = "15.0.0"
if __name__ == "__main__":
print(__version__)
|