From 3fbaff704571293be83e2b56d36b761f42cce1ec Mon Sep 17 00:00:00 2001 From: Biswa Kalyan Bhuyan Date: Sat, 29 Mar 2025 12:35:50 +0530 Subject: Update version v0.3.2 --- youtube/__init__.py | 6 +- youtube/channel.py | 2 +- youtube/comments.py | 8 +- youtube/get_app_version/get_app_version.py | 48 ++++---- youtube/opensearch.xml | 2 +- youtube/playlist.py | 2 +- youtube/static/js/av-merge.js | 30 ++++- youtube/static/js/plyr-start.js | 17 ++- youtube/static/js/watch.js | 3 +- youtube/static/modules/plyr/custom_plyr.css | 38 +++++++ youtube/static/watch.css | 26 +++++ youtube/templates/base.html | 6 +- youtube/templates/error.html | 2 +- youtube/util.py | 171 ++++++++++++++++++---------- youtube/version.py | 2 +- youtube/watch.py | 134 +++++++++++----------- 16 files changed, 320 insertions(+), 177 deletions(-) (limited to 'youtube') diff --git a/youtube/__init__.py b/youtube/__init__.py index 64aed56..f122704 100644 --- a/youtube/__init__.py +++ b/youtube/__init__.py @@ -19,14 +19,14 @@ yt_app.add_url_rule('/settings', 'settings_page', settings.settings_page, method @yt_app.route('/') def homepage(): - return flask.render_template('home.html', title="YT Local") + return flask.render_template('home.html', title="Biswa's Youtube") @yt_app.route('/licenses') def licensepage(): return flask.render_template( 'licenses.html', - title="Licenses - YT Local" + title="Licenses - Biswa's Youtube" ) @@ -121,7 +121,7 @@ def error_page(e): elif (exc_info()[0] == util.FetchError and exc_info()[1].code == '404' ): - error_message = ('Error: The page you are looking for isn\'t here. ¯\_(ツ)_/¯') + error_message = ('Error: The page you are looking for isn\'t here.') return flask.render_template('error.html', error_code=exc_info()[1].code, error_message=error_message, diff --git a/youtube/channel.py b/youtube/channel.py index b520121..81881eb 100644 --- a/youtube/channel.py +++ b/youtube/channel.py @@ -292,7 +292,7 @@ def get_number_of_videos_channel(channel_id): try: response = util.fetch_url(url, headers_mobile, debug_name='number_of_videos', report_text='Got number of videos') - except urllib.error.HTTPError as e: + except (urllib.error.HTTPError, util.FetchError) as e: traceback.print_exc() print("Couldn't retrieve number of videos") return 1000 diff --git a/youtube/comments.py b/youtube/comments.py index 92a89e1..1ff1a21 100644 --- a/youtube/comments.py +++ b/youtube/comments.py @@ -53,7 +53,7 @@ def request_comments(ctoken, replies=False): 'hl': 'en', 'gl': 'US', 'clientName': 'MWEB', - 'clientVersion': '2.20240328.08.00', + 'clientVersion': '2.20210804.02.00', }, }, 'continuation': ctoken.replace('=', '%3D'), @@ -78,7 +78,7 @@ def single_comment_ctoken(video_id, comment_id): def post_process_comments_info(comments_info): for comment in comments_info['comments']: - comment['author'] = strip_non_ascii(comment['author']) + comment['author'] = strip_non_ascii(comment['author']) if comment.get('author') else "" comment['author_url'] = concat_or_none( '/', comment['author_url']) comment['author_avatar'] = concat_or_none( @@ -189,10 +189,10 @@ def video_comments(video_id, sort=0, offset=0, lc='', secret_key=''): comments_info['error'] += '\n\n' + e.error_message comments_info['error'] += '\n\nExit node IP address: %s' % e.ip else: - comments_info['error'] = 'YouTube blocked the request. IP address: %s' % e.ip + comments_info['error'] = 'YouTube blocked the request. Error: %s' % str(e) except Exception as e: - comments_info['error'] = 'YouTube blocked the request. IP address: %s' % e.ip + comments_info['error'] = 'YouTube blocked the request. Error: %s' % str(e) if comments_info.get('error'): print('Error retrieving comments for ' + str(video_id) + ':\n' + diff --git a/youtube/get_app_version/get_app_version.py b/youtube/get_app_version/get_app_version.py index 9852359..4995bb7 100644 --- a/youtube/get_app_version/get_app_version.py +++ b/youtube/get_app_version/get_app_version.py @@ -11,17 +11,10 @@ import subprocess def app_version(): def minimal_env_cmd(cmd): # make minimal environment - env = {} - for k in ['SYSTEMROOT', 'PATH']: - v = os.environ.get(k) - if v is not None: - env[k] = v - - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - out = subprocess.Popen( - cmd, stdout=subprocess.PIPE, env=env).communicate()[0] + env = {k: os.environ[k] for k in ['SYSTEMROOT', 'PATH'] if k in os.environ} + env.update({'LANGUAGE': 'C', 'LANG': 'C', 'LC_ALL': 'C'}) + + out = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env).communicate()[0] return out subst_list = { @@ -31,24 +24,21 @@ def app_version(): } if os.system("command -v git > /dev/null 2>&1") != 0: - subst_list - else: - if call(["git", "branch"], stderr=STDOUT, - stdout=open(os.devnull, 'w')) != 0: - subst_list - else: - # version - describe = minimal_env_cmd(["git", "describe", "--always"]) - git_revision = describe.strip().decode('ascii') - # branch - branch = minimal_env_cmd(["git", "branch"]) - git_branch = branch.strip().decode('ascii').replace('* ', '') - - subst_list = { - "version": __version__, - "branch": git_branch, - "commit": git_revision - } + return subst_list + + if call(["git", "branch"], stderr=STDOUT, stdout=open(os.devnull, 'w')) != 0: + return subst_list + + describe = minimal_env_cmd(["git", "describe", "--tags", "--always"]) + git_revision = describe.strip().decode('ascii') + + branch = minimal_env_cmd(["git", "branch"]) + git_branch = branch.strip().decode('ascii').replace('* ', '') + + subst_list.update({ + "branch": git_branch, + "commit": git_revision + }) return subst_list diff --git a/youtube/opensearch.xml b/youtube/opensearch.xml index 09d1cb7..cde9beb 100644 --- a/youtube/opensearch.xml +++ b/youtube/opensearch.xml @@ -1,5 +1,5 @@ -YT Local +Biswa's Youtube no CIA shit in the background UTF-8 data:image/x-icon;base64,AAABAAEAEBAAAAEACAAlAgAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAexJREFUOI2lkzFPmlEUhp/73fshtCUCRtvQkJoKMrDQJvoHnBzUhc3EH0DUQf+As6tujo4M6mTiIDp0kGiMTRojTRNSW6o12iD4YYXv3g7Qr4O0ScM7npz7vOe+J0fk83lDF7K6eQygwkdHhI+P0bYNxmBXq5RmZui5vGQgn0f7fKi7O4oLC1gPD48BP9JpnpRKJFZXcQMB3m1u4vr9NHp76d/bo39/n4/z84ROThBa4/r91OJxMKb9BSn5mskAIOt1eq6uEFpjVyrEcjk+T0+TXlzkbTZLuFDAur9/nIFRipuREQCe7+zgBgK8mZvj/fIylVTKa/6UzXKbSnnuHkA0GnwbH/cA0a0takND3IyOEiwWAXBiMYTWjzLwtvB9bAyAwMUF8ZUVPiwtYTWbHqA6PIxoNv8OMLbN3eBga9TZWYQxaKX+AJJJhOv+AyAlT0slAG6TSX5n8+zszJugkzxA4PzcK9YSCQCk42DXaq1aGwqgfT5ebG9jpMQyUjKwu8vrtbWWqxC83NjAd31NsO2uleJnX58HCJ6eEjk8BGNQAA+RCOXJScpTU2AMwnUxlkXk4ACA+2iUSKGArNeRjkMsl6M8MYHQGtHpmIxSvFpfRzoORinQGqvZBCEwQoAxfMlkaIRCnQH/o66v8Re19MavaDNLfgAAAABJRU5ErkJggg== diff --git a/youtube/playlist.py b/youtube/playlist.py index 83d530c..28b8149 100644 --- a/youtube/playlist.py +++ b/youtube/playlist.py @@ -115,7 +115,7 @@ def get_playlist_page(): video_count = yt_data_extract.deep_get(info, 'metadata', 'video_count') if video_count is None: - video_count = 40 + video_count = 1000 return flask.render_template( 'playlist.html', diff --git a/youtube/static/js/av-merge.js b/youtube/static/js/av-merge.js index e00f440..cfe9574 100644 --- a/youtube/static/js/av-merge.js +++ b/youtube/static/js/av-merge.js @@ -20,6 +20,29 @@ // TODO: Call abort to cancel in-progress appends? +// Buffer sizes for different systems +const BUFFER_CONFIG = { + default: 50 * 10**6, // 50 megabytes + webOS: 20 * 10**6, // 20 megabytes WebOS (LG) + samsungTizen: 20 * 10**6, // 20 megabytes Samsung Tizen OS + androidTV: 30 * 10**6, // 30 megabytes Android TV + desktop: 50 * 10**6, // 50 megabytes PC/Mac +}; + +function detectSystem() { + const userAgent = navigator.userAgent.toLowerCase(); + if (/webos|lg browser/i.test(userAgent)) { + return "webOS"; + } else if (/tizen/i.test(userAgent)) { + return "samsungTizen"; + } else if (/android tv|smart-tv/i.test(userAgent)) { + return "androidTV"; + } else if (/firefox|chrome|safari|edge/i.test(userAgent)) { + return "desktop"; + } else { + return "default"; + } +} function AVMerge(video, srcInfo, startTime){ this.audioSource = null; @@ -164,6 +187,8 @@ AVMerge.prototype.printDebuggingInfo = function() { } function Stream(avMerge, source, startTime, avRatio) { + const selectedSystem = detectSystem(); + let baseBufferTarget = BUFFER_CONFIG[selectedSystem] || BUFFER_CONFIG.default; this.avMerge = avMerge; this.video = avMerge.video; this.url = source['url']; @@ -173,10 +198,11 @@ function Stream(avMerge, source, startTime, avRatio) { this.mimeCodec = source['mime_codec'] this.streamType = source['acodec'] ? 'audio' : 'video'; if (this.streamType == 'audio') { - this.bufferTarget = avRatio*50*10**6; + this.bufferTarget = avRatio * baseBufferTarget; } else { - this.bufferTarget = 50*10**6; // 50 megabytes + this.bufferTarget = baseBufferTarget; } + console.info(`Detected system: ${selectedSystem}. Applying bufferTarget of ${this.bufferTarget} bytes to ${this.streamType}.`); this.initRange = source['init_range']; this.indexRange = source['index_range']; diff --git a/youtube/static/js/plyr-start.js b/youtube/static/js/plyr-start.js index 56068f0..3838acc 100644 --- a/youtube/static/js/plyr-start.js +++ b/youtube/static/js/plyr-start.js @@ -58,7 +58,7 @@ }, }); - const player = new Plyr(document.getElementById('js-video-player'), { + const playerOptions = { // Learning about autoplay permission https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy/autoplay#syntax autoplay: autoplayActive, disableContextMenu: false, @@ -117,5 +117,20 @@ tooltips: { controls: true, }, + } + + const player = new Plyr(document.getElementById('js-video-player'), playerOptions); + + // disable double click to fullscreen + // https://github.com/sampotts/plyr/issues/1370#issuecomment-528966795 + player.eventListeners.forEach(function(eventListener) { + if(eventListener.type === 'dblclick') { + eventListener.element.removeEventListener(eventListener.type, eventListener.callback, eventListener.options); + } }); + + // Add .started property, true after the playback has been started + // Needed so controls won't be hidden before playback has started + player.started = false; + player.once('playing', function(){this.started = true}); })(); diff --git a/youtube/static/js/watch.js b/youtube/static/js/watch.js index 95d9fa7..00803cf 100644 --- a/youtube/static/js/watch.js +++ b/youtube/static/js/watch.js @@ -5,8 +5,9 @@ function changeQuality(selection) { let videoPaused = video.paused; let videoSpeed = video.playbackRate; let srcInfo; - if (avMerge) + if (avMerge && typeof avMerge.close === 'function') { avMerge.close(); + } if (selection.type == 'uni'){ srcInfo = data['uni_sources'][selection.index]; video.src = srcInfo.url; diff --git a/youtube/static/modules/plyr/custom_plyr.css b/youtube/static/modules/plyr/custom_plyr.css index 0fd3c52..7a9f0f3 100644 --- a/youtube/static/modules/plyr/custom_plyr.css +++ b/youtube/static/modules/plyr/custom_plyr.css @@ -37,3 +37,41 @@ e.g. Firefox playback speed options */ max-height: 320px; overflow-y: auto; } + +/* +* Custom styles similar to youtube +*/ +.plyr__controls { + display: flex; + justify-content: center; +} + +.plyr__progress__container { + position: absolute; + bottom: 0; + width: 100%; + margin-bottom: -10px; +} + +.plyr__controls .plyr__controls__item:first-child { + margin-left: 0; + margin-right: 0; + z-index: 5; +} + +.plyr__controls .plyr__controls__item.plyr__volume { + margin-left: auto; +} + +.plyr__controls .plyr__controls__item.plyr__progress__container { + padding-left: 10px; + padding-right: 10px; +} + +.plyr__progress input[type="range"] { + margin-bottom: 50px; +} + +/* +* End custom styles +*/ diff --git a/youtube/static/watch.css b/youtube/static/watch.css index 460bba3..c0bdec6 100644 --- a/youtube/static/watch.css +++ b/youtube/static/watch.css @@ -128,6 +128,29 @@ header { background-color: var(--buttom-hover); } +.live-url-choices { + background-color: var(--thumb-background); + margin: 1rem 0; + padding: 1rem; +} + +.playability-error { + position: relative; + box-sizing: border-box; + height: 30vh; + margin: 1rem 0; +} + +.playability-error > span { + display: flex; + background-color: var(--thumb-background); + height: 100%; + object-fit: cover; + justify-content: center; + align-items: center; + text-align: center; +} + .playlist { display: grid; grid-gap: 4px; @@ -622,6 +645,9 @@ figure.sc-video { max-height: 80vh; overflow-y: scroll; } + .playability-error { + height: 60vh; + } .playlist { display: grid; grid-gap: 1px; diff --git a/youtube/templates/base.html b/youtube/templates/base.html index 393cc52..d90c61f 100644 --- a/youtube/templates/base.html +++ b/youtube/templates/base.html @@ -10,7 +10,7 @@ {{ page_title }} - + @@ -31,7 +31,7 @@