aboutsummaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
authorRblSb <msrblsb@gmail.com>2024-04-28 07:23:25 +0300
committerRblSb <msrblsb@gmail.com>2024-04-28 21:51:50 +0300
commit9d844bbf3ac6be327325b13a91a6b33f73c49c1d (patch)
tree52108f2300ca84decf33a1e7b3552e81166ba5ac /src/client
parent8679f8edcb6d2f3142db30848c640aed6fe883b8 (diff)
Raw youtube fallback for unavailable videos
Also: - fix `tryLocalIp` replacement (NAT workaround) - improve proxy headers a bit - use json2object fork for better generated diffs
Diffstat (limited to 'src/client')
-rw-r--r--src/client/Main.hx12
-rw-r--r--src/client/Player.hx4
-rw-r--r--src/client/players/Youtube.hx69
3 files changed, 62 insertions, 23 deletions
diff --git a/src/client/Main.hx b/src/client/Main.hx
index b1e0eb6..85db713 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -18,6 +18,7 @@ import js.html.Event;
import js.html.InputElement;
import js.html.KeyboardEvent;
import js.html.MouseEvent;
+import js.html.URL;
import js.html.VideoElement;
import js.html.WebSocket;
@@ -387,7 +388,13 @@ class Main {
public function tryLocalIp(url:String):String {
if (host == globalIp) return url;
- return url.replace(globalIp, host);
+ try {
+ final url = new URL(url);
+ url.hostname = url.hostname.replace(globalIp, host);
+ return '$url';
+ } catch (e) {
+ return url;
+ }
}
function onMessage(e):Void {
@@ -557,6 +564,9 @@ class Main {
case Dump:
Utils.saveFile("dump.json", ApplicationJson, data.dump.data);
+
+ case GetYoutubeVideoInfo:
+ // handled by event listeners like `JsApi.once`
}
}
diff --git a/src/client/Player.hx b/src/client/Player.hx
index 75b44b5..06a8f3e 100644
--- a/src/client/Player.hx
+++ b/src/client/Player.hx
@@ -89,6 +89,10 @@ class Player {
setItemElementType(el, videoList.getItem(pos).isTemp);
}
+ public function getCurrentItem():VideoItem {
+ return videoList.currentItem;
+ }
+
function setPlayer(newPlayer:IPlayer):Void {
if (player != newPlayer) {
if (player != null) {
diff --git a/src/client/players/Youtube.hx b/src/client/players/Youtube.hx
index a728012..5308b63 100644
--- a/src/client/players/Youtube.hx
+++ b/src/client/players/Youtube.hx
@@ -10,15 +10,11 @@ import js.Browser.document;
import js.html.Element;
import js.youtube.Youtube as YtInit;
import js.youtube.YoutubePlayer;
+import utils.YoutubeUtils;
using StringTools;
class Youtube implements IPlayer {
- final matchId = ~/youtube\.com.*v=([A-z0-9_-]+)/;
- final matchShort = ~/youtu\.be\/([A-z0-9_-]+)/;
- final matchShorts = ~/youtube\.com\/shorts\/([A-z0-9_-]+)/;
- final matchEmbed = ~/youtube\.com\/embed\/([A-z0-9_-]+)/;
- final matchPlaylist = ~/youtube\.com.*list=([A-z0-9_-]+)/;
final videosUrl = "https://www.googleapis.com/youtube/v3/videos";
final playlistUrl = "https://www.googleapis.com/youtube/v3/playlistItems";
final urlTitleDuration = "?part=snippet,contentDetails&fields=items(snippet/title,contentDetails/duration)";
@@ -41,25 +37,12 @@ class Youtube implements IPlayer {
return extractVideoId(url) != "" || extractPlaylistId(url) != "";
}
- public function extractVideoId(url:String):String {
- if (matchId.match(url)) {
- return matchId.matched(1);
- }
- if (matchShort.match(url)) {
- return matchShort.matched(1);
- }
- if (matchShorts.match(url)) {
- return matchShorts.matched(1);
- }
- if (matchEmbed.match(url)) {
- return matchEmbed.matched(1);
- }
- return "";
+ public function extractVideoId(url:String) {
+ return YoutubeUtils.extractVideoId(url);
}
- function extractPlaylistId(url:String):String {
- if (!matchPlaylist.match(url)) return "";
- return matchPlaylist.matched(1);
+ public function extractPlaylistId(url:String) {
+ return YoutubeUtils.extractPlaylistId(url);
}
final matchHours = ~/([0-9]+)H/;
@@ -256,11 +239,53 @@ class Youtube implements IPlayer {
},
onPlaybackRateChange: e -> {
player.onRateChange();
+ },
+ onError: e -> {
+ // TODO message error codes
+ trace('Error ${e.data}');
+ rawSourceFallback(item.url);
}
}
});
}
+ function rawSourceFallback(url:String):Void {
+ JsApi.once(GetYoutubeVideoInfo, event -> {
+ final data = event.getYoutubeVideoInfo;
+ final info = data.response;
+ final format = getBestStreamFormat(info) ?? {
+ trace("format not found in response info:");
+ trace(info);
+ return;
+ };
+ final item = player.getCurrentItem();
+ item.url = format.url;
+ player.refresh();
+ });
+ main.send({
+ type: GetYoutubeVideoInfo,
+ getYoutubeVideoInfo: {
+ url: url
+ }
+ });
+ }
+
+ function getBestStreamFormat(info:YouTubeVideoInfo):Null<YoutubeVideoFormat> {
+ info.formats ??= [];
+ info.adaptiveFormats ??= [];
+ final formats = info.adaptiveFormats.concat(info.formats);
+ final qPriority = [1080, 720, 480, 360, 240];
+ for (q in qPriority) {
+ final quality = '${q}p';
+ for (format in formats) {
+ if (format.audioQuality == null) continue; // no sound
+ if (format.width == null) continue; // no video
+ if (format.qualityLabel == quality) return format;
+ }
+ }
+ return null;
+ }
+
public function removeVideo():Void {
if (video == null) return;
isLoaded = false;
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage