diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Types.hx | 4 | ||||
| -rw-r--r-- | src/client/Main.hx | 12 | ||||
| -rw-r--r-- | src/client/players/Youtube.hx | 83 |
3 files changed, 80 insertions, 19 deletions
diff --git a/src/Types.hx b/src/Types.hx index a98c7a1..2501cc4 100644 --- a/src/Types.hx +++ b/src/Types.hx @@ -4,7 +4,8 @@ import Client.ClientData; typedef VideoData = { duration:Float, - ?title:String + ?title:String, + ?url:String } typedef Config = { @@ -16,6 +17,7 @@ typedef Config = { totalVideoLimit:Int, userVideoLimit:Int, templateUrl:String, + youtubeApiKey:String, permissions:{ guest:Array<Permission>, user:Array<Permission>, diff --git a/src/client/Main.hx b/src/client/Main.hx index 1552d38..918a81a 100644 --- a/src/client/Main.hx +++ b/src/client/Main.hx @@ -182,10 +182,11 @@ class Main { return; } if (data.title == null) data.title = Lang.get("rawVideo"); + if (data.url == null) data.url = url; send({ type: AddVideo, addVideo: { item: { - url: url, + url: data.url, title: data.title, author: personal.name, duration: data.duration, @@ -502,7 +503,7 @@ class Main { ws.send(Json.stringify(data)); } - function serverMessage(type:Int, ?text:String):Void { + public function serverMessage(type:Int, ?text:String, isText = true):Void { final msgBuf = ge("#messagebuffer"); final div = document.createDivElement(); final time = "[" + new Date().toTimeString().split(" ")[0] + "] "; @@ -518,7 +519,8 @@ class Main { div.textContent = time + text + " " + Lang.get("entered"); case 4: div.className = "server-whisper"; - div.textContent = time + text; + if (isText) div.textContent = time + text; + else div.innerHTML = time + text; default: } msgBuf.appendChild(div); @@ -650,6 +652,10 @@ class Main { return config.templateUrl; } + public function getYoutubeApiKey():String { + return config.youtubeApiKey; + } + function escapeRegExp(regex:String):String { return ~/([.*+?^${}()|[\]\\])/g.replace(regex, "\\$1"); } diff --git a/src/client/players/Youtube.hx b/src/client/players/Youtube.hx index 15eb014..f582ba7 100644 --- a/src/client/players/Youtube.hx +++ b/src/client/players/Youtube.hx @@ -16,9 +16,12 @@ class Youtube implements IPlayer { static final matchId = ~/v=([A-z0-9_-]+)/; static final matchShort = ~/youtu.be\/([A-z0-9_-]+)/; static final matchEmbed = ~/embed\/([A-z0-9_-]+)/; - static final apiUrl = "https://www.googleapis.com/youtube/v3/videos"; + static final matchPlaylist = ~/youtube\.com.*list=([A-z0-9_-]+)/; + static final videosUrl = "https://www.googleapis.com/youtube/v3/videos"; + static final playlistUrl = "https://www.googleapis.com/youtube/v3/playlistItems"; static final urlTitleDuration = "?part=snippet,contentDetails&fields=items(snippet/title,contentDetails/duration)"; - static final apiKey = "AIzaSyDTk1OPRI9cDkAK_BKsBcv10DQCHse-QaA"; + static final urlVideoId = "?part=snippet&fields=items(snippet/resourceId/videoId)"; + static var apiKey:String; final main:Main; final player:Player; final playerEl:Element = ge("#ytapiplayer"); @@ -29,10 +32,11 @@ class Youtube implements IPlayer { public function new(main:Main, player:Player) { this.main = main; this.player = player; + apiKey = main.getYoutubeApiKey(); } public static function isYoutube(url:String):Bool { - return extractVideoId(url) != ""; + return extractVideoId(url) != "" || extractPlaylistId(url) != ""; } static function extractVideoId(url:String):String { @@ -48,6 +52,11 @@ class Youtube implements IPlayer { return matchId.matched(1); } + static function extractPlaylistId(url:String):String { + if (!matchPlaylist.match(url)) return ""; + return matchPlaylist.matched(1); + } + final matchHours = ~/([0-9]+)H/; final matchMinutes = ~/([0-9]+)M/; final matchSeconds = ~/([0-9]+)S/; @@ -64,32 +73,76 @@ class Youtube implements IPlayer { } public function getVideoData(url:String, callback:(data:VideoData)->Void):Void { - final id = extractVideoId(url); - final url = '$apiUrl$urlTitleDuration&id=$id&key=$apiKey'; - final http = new Http(url); + var id = extractVideoId(url); + if (id == "") { + getPlaylistVideoData(url, callback); + return; + } + final dataUrl = '$videosUrl$urlTitleDuration&id=$id&key=$apiKey'; + final http = new Http(dataUrl); http.onData = data -> { final json = Json.parse(data); if (json.error != null) { + youtubeApiError(json.error); getRemoteDataFallback(url, callback); return; } - final item = json.items[0]; - if (item == null) { + final items:Array<Dynamic> = json.items; + if (items == null || items.length == 0) { callback({duration: 0}); return; } - final title:String = item.snippet.title; - final duration:String = item.contentDetails.duration; - // TODO duration is PT0S for streams - callback({ - duration: convertTime(duration), - title: title - }); + for (item in items) { + final title:String = item.snippet.title; + final duration:String = item.contentDetails.duration; + // TODO duration is PT0S for streams + callback({ + duration: convertTime(duration), + title: title, + url: url + }); + } } http.onError = msg -> getRemoteDataFallback(url, callback); http.request(); } + function getPlaylistVideoData(url:String, callback:(data:VideoData)->Void):Void { + final id = extractPlaylistId(url); + final dataUrl = '$playlistUrl$urlVideoId&maxResults=50&playlistId=$id&key=$apiKey'; + final http = new Http(dataUrl); + http.onData = data -> { + final json = Json.parse(data); + if (json.error != null) { + youtubeApiError(json.error); + callback({duration: 0}); + return; + } + final items:Array<Dynamic> = json.items; + if (items == null || items.length == 0) { + callback({duration: 0}); + return; + } + function loadNextItem():Void { + final item = items.shift(); + final id:String = item.snippet.resourceId.videoId; + getVideoData('youtu.be/$id', data -> { + callback(data); + if (items.length > 0) loadNextItem(); + }); + } + loadNextItem(); + } + http.onError = msg -> callback({duration: 0}); + http.request(); + } + + function youtubeApiError(error:Dynamic):Void { + final code:Int = error.code; + final msg:String = error.message; + main.serverMessage(4, 'Error $code: $msg', false); + } + function getRemoteDataFallback(url:String, callback:(data:VideoData)->Void):Void { if (!YtInit.isLoadedAPI) { YtInit.init(() -> getRemoteDataFallback(url, callback)); |
