aboutsummaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/Buttons.hx4
-rw-r--r--src/client/ClientSettings.hx1
-rw-r--r--src/client/Main.hx24
-rw-r--r--src/client/players/Raw.hx42
-rw-r--r--src/client/players/RawSubs.hx139
5 files changed, 179 insertions, 31 deletions
diff --git a/src/client/Buttons.hx b/src/client/Buttons.hx
index ee42f44..7df6486 100644
--- a/src/client/Buttons.hx
+++ b/src/client/Buttons.hx
@@ -158,9 +158,7 @@ class Buttons {
final isRawSingleVideo = value != "" && main.isRawPlayerLink(value)
&& main.isSingleVideoLink(value);
ge("#mediatitleblock").style.display = isRawSingleVideo ? "" : "none";
- if (JsApi.hasSubtitleSupport()) {
- ge("#subsurlblock").style.display = isRawSingleVideo ? "" : "none";
- }
+ ge("#subsurlblock").style.display = isRawSingleVideo ? "" : "none";
}
mediaUrl.onfocus = mediaUrl.oninput;
diff --git a/src/client/ClientSettings.hx b/src/client/ClientSettings.hx
index 03f1d23..c787824 100644
--- a/src/client/ClientSettings.hx
+++ b/src/client/ClientSettings.hx
@@ -11,5 +11,6 @@ typedef ClientSettings = {
isSwapped:Bool,
isUserListHidden:Bool,
latestLinks:Array<String>,
+ latestSubs:Array<String>,
hotkeysEnabled:Bool
}
diff --git a/src/client/Main.hx b/src/client/Main.hx
index e9cda39..c5dca67 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -26,7 +26,7 @@ using ClientTools;
using StringTools;
class Main {
- static inline var SETTINGS_VERSION = 2;
+ static inline var SETTINGS_VERSION = 3;
public final settings:ClientSettings;
public var isSyncActive = true;
@@ -65,6 +65,7 @@ class Main {
isSwapped: false,
isUserListHidden: true,
latestLinks: [],
+ latestSubs: [],
hotkeysEnabled: true
}
Settings.init(defaults, settingsPatcher);
@@ -93,6 +94,9 @@ class Main {
case 1:
final data:ClientSettings = data;
data.hotkeysEnabled = true;
+ case 2:
+ final data:ClientSettings = data;
+ data.latestSubs = [];
case SETTINGS_VERSION, _:
throw 'skipped version $version';
}
@@ -154,9 +158,10 @@ class Main {
ge("#mediatitle").onkeydown = (e:KeyboardEvent) -> {
if (e.keyCode == KeyCode.Return) addVideoUrl(true);
}
- ge("#subsurl").onkeydown = (e:KeyboardEvent) -> {
- if (e.keyCode == KeyCode.Return) addVideoUrl(true);
- }
+ new InputWithHistory(cast ge("#subsurl"), settings.latestSubs, 10, value -> {
+ addVideoUrl(true);
+ return false;
+ });
ge("#ce_queue_next").onclick = e -> addIframe(false);
ge("#ce_queue_end").onclick = e -> addIframe(true);
@@ -205,12 +210,17 @@ class Main {
function addVideoUrl(atEnd:Bool):Void {
final mediaUrl:InputElement = cast ge("#mediaurl");
+ final subsUrl:InputElement = cast ge("#subsurl");
final checkbox:InputElement = cast ge("#addfromurl").querySelector(".add-temp");
final isTemp = checkbox.checked;
final url = mediaUrl.value;
+ final subs = subsUrl.value;
if (url.length == 0) return;
mediaUrl.value = "";
InputWithHistory.pushIfNotLast(settings.latestLinks, url);
+ if (subs.length != 0) {
+ InputWithHistory.pushIfNotLast(settings.latestSubs, subs);
+ }
Settings.write(settings);
final url = ~/, ?(https?)/g.replace(url, "|$1");
final links = url.split("|");
@@ -351,8 +361,7 @@ class Main {
public function getPlaylistLinks():Array<String> {
final items = player.getItems();
return [
- for (item in items)
- item.url
+ for (item in items) item.url
];
}
@@ -801,7 +810,8 @@ class Main {
function onChatVideoLoaded(e:Event):Void {
final el:VideoElement = cast e.target;
if (emoteMaxSize == null) {
- emoteMaxSize = Std.parseInt(window.getComputedStyle(el).getPropertyValue("max-width"));
+ emoteMaxSize = Std.parseInt(window.getComputedStyle(el)
+ .getPropertyValue("max-width"));
}
// fixes default video tag size in chat when tab unloads videos in background
// (some browsers optimization i guess)
diff --git a/src/client/players/Raw.hx b/src/client/players/Raw.hx
index ea51e97..5889732 100644
--- a/src/client/players/Raw.hx
+++ b/src/client/players/Raw.hx
@@ -54,11 +54,8 @@ class Raw implements IPlayer {
}
titleInput.value = "";
- var subs = "";
- if (JsApi.hasSubtitleSupport()) {
- subs = subsInput.value.trim();
- subsInput.value = "";
- }
+ final subs = subsInput.value.trim();
+ subsInput.value = "";
final video = document.createVideoElement();
video.src = url;
video.onerror = e -> {
@@ -101,24 +98,27 @@ class Raw implements IPlayer {
}
if (video != null) {
video.src = url;
- if (isHls) initHlsSource(video, url);
- restartControlsHider();
- return;
- }
- video = document.createVideoElement();
- video.id = "videoplayer";
- video.src = url;
- restartControlsHider();
- video.oncanplaythrough = player.onCanBePlayed;
- video.onseeking = player.onSetTime;
- video.onplay = e -> {
- playAllowed = true;
- player.onPlay();
+ for (element in video.children) {
+ if (element.nodeName != "TRACK") continue;
+ element.remove();
+ }
+ } else {
+ video = document.createVideoElement();
+ video.id = "videoplayer";
+ video.src = url;
+ video.oncanplaythrough = player.onCanBePlayed;
+ video.onseeking = player.onSetTime;
+ video.onplay = e -> {
+ playAllowed = true;
+ player.onPlay();
+ }
+ video.onpause = player.onPause;
+ video.onratechange = player.onRateChange;
+ playerEl.appendChild(video);
}
- video.onpause = player.onPause;
- video.onratechange = player.onRateChange;
- playerEl.appendChild(video);
if (isHls) initHlsSource(video, url);
+ restartControlsHider();
+ RawSubs.loadSubs(item, video);
}
function restartControlsHider():Void {
diff --git a/src/client/players/RawSubs.hx b/src/client/players/RawSubs.hx
new file mode 100644
index 0000000..4fcf3b6
--- /dev/null
+++ b/src/client/players/RawSubs.hx
@@ -0,0 +1,139 @@
+package client.players;
+
+import Types.VideoItem;
+import haxe.crypto.Base64;
+import haxe.io.Bytes;
+import js.Browser.document;
+import js.Browser.window;
+import js.html.VideoElement;
+
+using StringTools;
+
+private typedef Duration = {
+ h:Int,
+ m:Int,
+ s:Int,
+ ms:Int
+}
+
+class RawSubs {
+ public static function loadSubs(item:VideoItem, video:VideoElement):Void {
+ final ext = PathTools.urlExtension(item.subs);
+ // do not load subs if there is custom plugin
+ if (JsApi.hasSubtitleSupport(ext)) return;
+ final url = '/proxy?url=${encodeURI(item.subs)}';
+
+ switch ext {
+ case "ass":
+ case "srt":
+ parseSrt(video, url);
+ case "vtt":
+ onParsed(video, "VTT subtitles", url);
+ // parseVtt(video, url);
+ }
+ }
+
+ static function parseSrt(video:VideoElement, url:String):Void {
+ window.fetch(url).then(response -> {
+ return response.text();
+ }).then(text -> {
+ final subs:Array<{
+ counter:String,
+ time:String,
+ text:String
+ }> = [];
+ final blocks = text.replace("\r\n", "\n").split("\n\n");
+ for (block in blocks) {
+ final lines = block.split("\n");
+ if (lines.length < 3) continue;
+ final textLines = [
+ for (i in 2...lines.length) lines[i]
+ ];
+ subs.push({
+ counter: lines[0],
+ time: lines[1].replace(",", "."),
+ text: textLines.join("\n")
+ });
+ }
+ var data = "WEBVTT\n\n";
+ for (sub in subs) {
+ data += '${sub.counter}\n';
+ data += '${sub.time}\n';
+ data += '${sub.text}\n\n';
+ }
+ trace(data);
+ final textBase64 = "data:text/plain;base64,";
+ final url = textBase64 + Base64.encode(Bytes.ofString(data));
+ onParsed(video, "SRT subtitles", url);
+ });
+ }
+
+ // function saveSubs() {
+ // final options = [
+ // for (i in 0...subsSelect.options.length)
+ // subsSelect.item(i)
+ // ];
+ // var inputId = (cast el("#id-offset") : InputElement).value;
+ // var id = Std.parseInt(inputId);
+ // if (id == null) id = 1;
+ // final data:Array<String> = options.map(element -> {
+ // final value = element.textContent;
+ // final firstTime = Std.parseFloat(value.split("|")[0]);
+ // final secondTime = Std.parseFloat(value.split("|")[1]);
+ // // 00:00:00,498 --> 00:00:02,827
+ // var time = '$id\n';
+ // time += srvTimeFormat(stringDuration(firstTime));
+ // time += " --> ";
+ // time += srvTimeFormat(stringDuration(secondTime));
+ // time += '\ntext$id\n';
+ // id++;
+ // return time;
+ // });
+ // final data = data.join("\n");
+ // Utils.saveFile("subs.srv", TextPlain, data);
+ // }
+
+ function stringDuration(seconds:Float):Duration {
+ final h = Std.int(seconds / 60 / 60);
+ final m = Std.int(seconds / 60) - h * 60;
+ final s = Std.int(seconds % 60);
+ final ms = Std.int((seconds - Std.int(seconds)) * 1000);
+ return {
+ h: h,
+ m: m,
+ s: s,
+ ms: ms
+ }
+ }
+
+ function srvTimeFormat(time:Duration):String {
+ final h = '${time.h}'.lpad("0", 2);
+ final m = '${time.m}'.lpad("0", 2);
+ final s = '${time.s}'.lpad("0", 2);
+ final ms = '${time.ms}'.rpad("0", 3);
+ return '$h:$m:$s,$ms';
+ }
+
+ static function parseVtt(video:VideoElement, url:String):Void {
+ window.fetch(url).then(response -> response.text()).then(data -> {
+ final textBase64 = "data:text/plain;base64,";
+ final url = textBase64 + Base64.encode(Bytes.ofString(data));
+ onParsed(video, "VTT subtitles", url);
+ });
+ }
+
+ static function onParsed(video:VideoElement, name:String, dataUrl:String) {
+ final trackEl = document.createTrackElement();
+ trackEl.label = name;
+ trackEl.kind = "subtitles";
+ trackEl.src = dataUrl;
+ trackEl.default_ = true;
+ final track = trackEl.track;
+ track.mode = SHOWING;
+ video.appendChild(trackEl);
+ }
+
+ static function encodeURI(data:String):String {
+ return js.Syntax.code("encodeURI({0})", data);
+ }
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage