diff --git a/CHANGELOG.md b/CHANGELOG.md index 7367a5e..9d7d564 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,16 @@ Changelog ===== -## v1.1.1.0 (04-15-2015) +## v1.1.X.X (XX-XX-2016) + * Altered the popup's uploads search to include video titles + * Implemented watch later feature + +## v1.1.1.0 (04-15-2016) * Reverted to old video uploads method due to quota exhaustion * Added four additional API keys * Implemented search functionality * Added subscription importing -## v1.1.0.5 (02-02-2015) +## v1.1.0.5 (02-02-2016) * Changed channel updater to use the search API instead of the channel's playlist API (This allows more up-to-date updating) * Fixed bug where channels would stop updating * Added locales/languages diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 13ebb40..4471233 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -249,12 +249,16 @@ }, "background_notificationWatch": { "description": "", - "message": "Watch Video" + "message": "Play Video" }, "background_notificationClose": { "description": "", "message": "Dismiss" }, + "background_notificationWatchLater": { + "description": "", + "message": "Watch Later" + }, "background_notificationLogCheck": { "description": "", "message": "Checking YouTube User: " diff --git a/_locales/es/messages.json b/_locales/es/messages.json index 07ccf5b..87010c5 100644 --- a/_locales/es/messages.json +++ b/_locales/es/messages.json @@ -255,6 +255,10 @@ "description": "", "message": "Cerca" }, + "background_notificationWatchLater": { + "description": "", + "message": "Ver más tarde" + }, "background_notificationLogCheck": { "description": "", "message": "Comprobación de YouTube del usuario: " diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index 4e291e8..ee12da7 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -255,6 +255,10 @@ "description": "", "message": "Près" }, + "background_notificationWatchLater": { + "description": "", + "message": "À regarder plus tard" + }, "background_notificationLogCheck": { "description": "", "message": "Vérification YouTube utilisateur: " diff --git a/img/ic_watchlater.png b/img/ic_watchlater.png new file mode 100644 index 0000000..e26ffb2 Binary files /dev/null and b/img/ic_watchlater.png differ diff --git a/img/ic_watchlater.psd b/img/ic_watchlater.psd new file mode 100644 index 0000000..eb11265 Binary files /dev/null and b/img/ic_watchlater.psd differ diff --git a/js/background.js b/js/background.js index b7f27e9..50a9967 100644 --- a/js/background.js +++ b/js/background.js @@ -12,6 +12,8 @@ var wyn = {}; "notification_watch_icon": "img/ic_play.png", "notification_close": getString("notificationClose"), "notification_close_icon": "img/ic_close.png", + "notification_watchlater": getString("notificationWatchLater"), + "notification_watchlater_icon": "img/ic_watchlater.png", "notification_main_icon": "img/ic_youtube.png", "notification_log_check": getString("notificationLogCheck"), "notification_log_new": getString("notificationLogNew"), @@ -64,24 +66,47 @@ if(localStorage.getItem("settings") == null) }, updated: { enabled: false + }, + watchlater: { + id: "" } })); fixItems(); function fixItems(){ var settings = JSON.parse(localStorage.getItem("settings")); - if(typeof settings.addBtn === "undefined"){ + if(typeof settings.notifications === "undefined" || settings.notifications.enabled === "undefined" || settings.notifications.volume === "undefined"){ + settings.notifications = { + enabled: true, + volume: 100 + }; + localStorage.setItem("settings", JSON.stringify(settings)); + } + if(typeof settings.tts === "undefined" || settings.tts.enabled === "undefined" || settings.tts.type === "undefined"){ + settings.notifications = { + enabled: false, + type: 1 + }; + localStorage.setItem("settings", JSON.stringify(settings)); + } + if(typeof settings.addBtn === "undefined" || settings.addBtn.enabled === "undefined"){ settings.addBtn = { enabled: true }; localStorage.setItem("settings", JSON.stringify(settings)); } - if(typeof settings.updated === "undefined"){ + if(typeof settings.updated === "undefined" || settings.updated.enabled === "undefined"){ settings.updated = { enabled: false }; localStorage.setItem("settings", JSON.stringify(settings)); } + if(typeof settings.watchlater === "undefined" || settings.watchlater.id === "undefined"){ + settings.watchlater = { + id: "" + }; + localStorage.setItem("settings", JSON.stringify(settings)); + } } chrome.notifications.onClicked.addListener(onNotificationClick); @@ -147,7 +172,7 @@ $(function(){ sendResponse(settings.addBtn.enabled); break; case "receivedToken": - sendResponse(onReceivedToken()); + sendResponse(onReceiveImportToken()); break; case "importApproved": sendResponse(onImportApproved()); @@ -421,7 +446,7 @@ function removeYoutube(type, name, refresh, fromContentScript){ * @param {string} id The name of the channel's ID * @param {number} index The index for an element (for injected.js) */ - function doesYoutubeExist(id, index){ +function doesYoutubeExist(id, index){ var channels = JSON.parse(localStorage.getItem("channels")); for(var i = 0; i < channels.length; i++){ if(channels[i].id == id) @@ -562,6 +587,9 @@ function checkYoutube(num, refresh, batch) { buttons: [{ title: wyn.strings.notification_watch, iconUrl: wyn.strings.notification_watch_icon + }, { + title: wyn.strings.notification_watchlater, + iconUrl: wyn.strings.notification_watchlater_icon }, { title: wyn.strings.notification_close, iconUrl: wyn.strings.notification_close_icon @@ -758,13 +786,24 @@ function onNotificationClick(ntID){ */ function onNotificationButtonClick(ntID, btnID){ if(typeof ntID.split("-")[4] !== "undefined") { - if(btnID == 0){ - var channels = JSON.parse(localStorage.getItem("channels")); - createTab("https://www.youtube.com/watch?v=" + channels[ntID.split("-")[4]].latestVideo.id); - console.log("User clicked on \"" + wyn.strings.notification_watch + "\" button; NTID: " + ntID); - console.log("Sending user to https://www.youtube.com/watch?v=" + channels[ntID.split("-")[4]].latestVideo.id); - }else if(btnID == 1){ - console.log("User clicked on \"" + wyn.strings.notification_close + "\" button; NTID: " + ntID); + switch(btnID) { + case 0: + var channels = JSON.parse(localStorage.getItem("channels")); + createTab("https://www.youtube.com/watch?v=" + channels[ntID.split("-")[4]].latestVideo.id); + console.log("User clicked on \"" + wyn.strings.notification_watch + "\" button; NTID: " + ntID); + console.log("Sending user to https://www.youtube.com/watch?v=" + channels[ntID.split("-")[4]].latestVideo.id); + break; + case 1: + var channels = JSON.parse(localStorage.getItem("channels")), + data = channels[ntID.split("-")[4]].latestVideo; + data.index = ntID.split("-")[4]; + + requestExtendedToken(data); + console.log("User clicked on \"" + wyn.strings.notification_watchlater + "\" button; NTID: " + ntID); + break; + case 2: + console.log("User clicked on \"" + wyn.strings.notification_close + "\" button; NTID: " + ntID); + break; } } chrome.notifications.clear(ntID); @@ -814,6 +853,9 @@ wyn.testNotify = function(){ buttons: [{ title: wyn.strings.notification_watch, iconUrl: wyn.strings.notification_watch_icon + }, { + title: wyn.strings.notification_watchlater, + iconUrl: wyn.strings.notification_watchlater_icon }, { title: wyn.strings.notification_close, iconUrl: wyn.strings.notification_close_icon @@ -869,6 +911,9 @@ wyn.forceNotification = function(id) { buttons: [{ title: wyn.strings.notification_watch, iconUrl: wyn.strings.notification_watch_icon + }, { + title: wyn.strings.notification_watchlater, + iconUrl: wyn.strings.notification_watchlater_icon }, { title: wyn.strings.notification_close, iconUrl: wyn.strings.notification_close_icon @@ -890,7 +935,7 @@ wyn.resetVideos = function(){ /** * Ran when the subscription import has been approved */ -function onReceivedToken() { +function onReceiveImportToken() { chrome.identity.getAuthToken({ interactive: false }, function(access_token) { if(chrome.runtime.lastError) return; @@ -991,4 +1036,112 @@ function importChannelsPost(){ setTimeout(function(){ chrome.extension.sendMessage({type: "refreshPage"}); }, 10*1000); +} + +/** + * Requests the user to approve the extended OAuth request + */ +function requestExtendedToken(videoInfo) { + chrome.identity.getAuthToken({ + interactive: true, + scopes: [ + "https://www.googleapis.com/auth/youtube.force-ssl" + ] + }, + function(token){ + if(!chrome.runtime.lastError){ + onReceiveExtendedToken(videoInfo); + } + } + ); +} + +/** + * Ran when the extended token is approved + */ +function onReceiveExtendedToken(videoInfo) { + chrome.identity.getAuthToken({ + interactive: false, + scopes: [ + "https://www.googleapis.com/auth/youtube.force-ssl" + ] + }, function(access_token) { + if(chrome.runtime.lastError) + return; + + var settings = JSON.parse(localStorage.getItem("settings")); + + if(settings.watchlater.id == "") { + var xhr = new XMLHttpRequest(); + xhr.open("GET", "https://www.googleapis.com/youtube/v3/channels?part=contentDetails&mine=true"); + xhr.setRequestHeader("Authorization", "Bearer " + access_token); + xhr.onload = function(){ + var data = JSON.parse(this.response), + watchLaterPlaylist = data.items[0].contentDetails.relatedPlaylists.watchLater; + + settings.watchlater.id = watchLaterPlaylist; + localStorage.setItem("settings", JSON.stringify(settings)); + + onReceiveExtendedTokenPost(access_token, videoInfo); + }; + xhr.send(); + }else + onReceiveExtendedTokenPost(access_token, videoInfo); + }); +} + +/** + * Ran after we receive the access token and watch later playlist + */ +function onReceiveExtendedTokenPost(access_token, videoInfo) { + var settings = JSON.parse(localStorage.getItem("settings")), + xhr = new XMLHttpRequest(), + requestData = { + snippet: { + playlistId: settings.watchlater.id, + resourceId: { + kind: "youtube#video", + videoId: videoInfo.id + } + } + }; + + xhr.open("POST", "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet"); + xhr.setRequestHeader("Authorization", "Bearer " + access_token); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onload = function(){ + var data = JSON.parse(this.response), + title = "Video added to Watch Later", + message = "\"" + videoInfo.title + "\" has been added to the Watch Later playlist."; + + if(data.error) { + if(data.error.code == 409) { + title = "Video already exists in Watch Later"; + message = "\"" + videoInfo.title + "\" already exists in the Watch Later playlist."; + }else{ + title = "Error Code: " + data.error.code; + message = data.error.message + } + } + + var ntID = rndStr(10) + "-" + rndStr(5) + "-" + rndStr(5) + "-" + rndStr(5) + "-" + videoInfo.index; + var options = { + type: "basic", + priority: 0, + title: title, + message: message, + iconUrl: videoInfo.thumbnail, + buttons: [{ + title: wyn.strings.notification_watch, + iconUrl: wyn.strings.notification_watch_icon + }] + }; + + chrome.notifications.create(ntID, options, function(){ + wyn.notificationSound.volume = parseInt(JSON.parse(localStorage.getItem("settings"))["notifications"]["volume"])/100; + wyn.notificationSound.play() + notifyTTS(options); + }); + }; + xhr.send(JSON.stringify(requestData)); } \ No newline at end of file diff --git a/js/settings.js b/js/settings.js index 55138ec..db9162b 100644 --- a/js/settings.js +++ b/js/settings.js @@ -86,16 +86,21 @@ $(function(){ configureSettings(); }, 500); - checkToken(); + checkImportToken(); }); var timesFailed = 0; -function checkToken(){ - chrome.identity.getAuthToken({ interactive: false }, +function checkImportToken(){ + chrome.identity.getAuthToken({ + interactive: true, + scopes: [ + "https://www.googleapis.com/auth/youtube.readonly" + ] + }, function(current_token) { var error = chrome.runtime.lastError; if(error && error.message == "OAuth2 not granted or revoked." && timesFailed < 4){ - setTimeout(function(){checkToken()}, 100); + setTimeout(function(){checkImportToken()}, 100); timesFailed++; return; } @@ -341,17 +346,17 @@ function registerListeners(){ chrome.tabs.create({url: "https://www.youtube.com/subscription_manager"}); }); $("#settings_import").on("click", function(){ - requestToken(); + requestImportToken(); }); $("#settings_import_changeUser").on("click", function(){ - changeOAuthToken(); + changeImportOAuthToken(); }); $("#emptyChannelsList_subheader").on("click", function(){ $("a.mdl-layout__tab").removeClass("is-active"); $("a[href='#tab-edit_settings']").addClass("is-active"); $(".mdl-layout__tab-panel").removeClass("is-active"); $("#tab-edit_settings").addClass("is-active"); - changeOAuthToken(); + changeImportOAuthToken(); }); } @@ -681,12 +686,12 @@ function updateSearch() { return; var val = $("#search-input").val().toLowerCase().trim(); - var max = 0; $(".channelRow:not(#masterChannelRow)").show().filter(function() { - var text = $(this).find(".channel_author").text().toLowerCase(); - if(text.indexOf(val) < 0) max++; - return !~text.indexOf(val); + var text = $(this).find(".channel_author").text().toLowerCase(), + text2 = $(this).find(".channel_video_title").text().toLowerCase(); + + return text.indexOf(val) < 0 ? text2.indexOf(val) < 0 : false; }).hide(); if(typeof wyns.previousLastListItem !== "undefined") @@ -715,16 +720,23 @@ function updateSearchExact() { /** * Requests the user to approve the OAuth request */ -function requestToken() { +function requestImportToken() { if($(".paper-snackbar").text() != wyns.strings.please_wait) createSnackbar(wyns.strings.please_wait); - chrome.identity.getAuthToken({ interactive: true }, function(token){ - if(chrome.runtime.lastError){ - createSnackbar("Error: " + chrome.runtime.lastError.message); - }else{ - chrome.extension.sendMessage({type: "receivedToken"}); + chrome.identity.getAuthToken({ + interactive: true, + scopes: [ + "https://www.googleapis.com/auth/youtube.readonly" + ] + }, + function(token){ + if(chrome.runtime.lastError){ + createSnackbar("Error: " + chrome.runtime.lastError.message); + }else{ + chrome.extension.sendMessage({type: "receivedToken"}); + } } - }); + ); } /** @@ -755,9 +767,14 @@ function showImportPopup(channelsNum) { /** * Change user token */ -function changeOAuthToken() { +function changeImportOAuthToken() { createSnackbar(wyns.strings.please_wait); - chrome.identity.getAuthToken({ interactive: false }, + chrome.identity.getAuthToken({ + interactive: true, + scopes: [ + "https://www.googleapis.com/auth/youtube.readonly" + ] + }, function(current_token) { if (!chrome.runtime.lastError) { chrome.identity.removeCachedAuthToken({ token: current_token }); @@ -765,7 +782,7 @@ function changeOAuthToken() { xhr.open("GET", "https://accounts.google.com/o/oauth2/revoke?token=" + current_token); xhr.send(); } - requestToken(); + requestImportToken(); } ); } diff --git a/manifest.json b/manifest.json index 6225f4d..9b82dff 100644 --- a/manifest.json +++ b/manifest.json @@ -32,9 +32,10 @@ "128": "img/128.png" }, "oauth2": { - "client_id": "836337899077-k426d78agmgff3l32a29c4lalg0tb0q1.apps.googleusercontent.com", + "client_id": "836337899077-9v0uf16i106obm32lkhc85ubr90h685u.apps.googleusercontent.com", "scopes": [ - "https://www.googleapis.com/auth/youtube.readonly" + "https://www.googleapis.com/auth/youtube.readonly", + "https://www.googleapis.com/auth/youtube.force-ssl" ] } } \ No newline at end of file