Skip to content

Instantly share code, notes, and snippets.

@tangyl
Last active November 1, 2020 19:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save tangyl/e95d92a017813f5176404dab4c2238fa to your computer and use it in GitHub Desktop.
Save tangyl/e95d92a017813f5176404dab4c2238fa to your computer and use it in GitHub Desktop.
instaclip.user.js
// ==UserScript==
// @name InstaClip Poster
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Create InstaClip post from current video page
// @author Yilong Tang yilongt@opera.com
// @match https://www.facebook.com/*
// @match https://www.instagram.com/*
// @match https://www.youtube.com/*
// @match http://road-to-champion-2018.op-mobile.opera.com/assets/view/instaclip.html
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_openInTab
// @grant GM_log
// @grant window.close
// @grant unsafeWindow
// ==/UserScript==
(function() {
'use strict';
if (window.location.href == "http://road-to-champion-2018.op-mobile.opera.com/assets/view/instaclip.html"){
unsafeWindow.__auth_callback__ = function(auth){
GM_setValue("instaclip_auth", auth);
}
return;
}
GM_addStyle(`
.instaclip-modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 10000; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content */
.instaclip-modal-content {
position: relative;
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 30%;
max-height: 80%;
overflow-y: auto;
font-size: 24px;
line-height: 28px;
}
.instaclip-user-bar{
position: relative;
width: 100%;
height: 60px;
margin-top: 5px;
margin-bottom: 5px;
}
.instaclip-avatar {
position: absolute;
top: 0;
left: 0;
width: 60px;
height: 60px;
}
.instaclip-user {
position: absolute;
top: 0;
left: 60px;
font-size: 28px;
line-height: 60px;
}
.instaclip-board {
margin-top: 5px;
margin-bottom: 5px;
}
.instaclip-modal-content h1 {
font-size: 36px;
line-height: 40px;
}
.instaclip-modal-content textarea {
margin: 4px;
resize: vertical;
width: 100%;
font-size: 18px;
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
}
.instaclip-modal-content select {
font-size: 18px;
}
.instaclip-thumb {
border: none;
border-radius: 8px;
}
.instaclip-btn {
padding: .5rem;
background: #86d1f0;
border: none;
border-radius: 4px;
margin: 4px;
font-size: 18px;
}
/* The Close Button */
.instaclip-close {
position: absolute;
color: #aaaaaa;
font-size: 28px;
font-weight: bold;
right: 28px;
top: 28px;
}
.instaclip-close:hover,
.instaclip-close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
.instaclip-progress-current {
padding: .5rem;
background: #86d1f0;
border: none;
border-radius: 4px;
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
font-size: 18px;
position: absolute;
top: 0;
left: 0;
height: 100%;
opacity: 0.4;
}
.instaclip-progress-bar {
position: relative;
background: #eee;
border: none;
text-align: center;
margin: 4px;
padding: 0;
width: 100%;
font-size: 18px;
height: 40px;
line-height: 40px;
}
`);
function uuidv4() {
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
)
}
var deviceID = GM_getValue("instaclip_device_id", uuidv4());
GM_setValue("instaclip_device_id", deviceID);
var app = {
headers: null,
auth: null,
deviceID: null,
boards: null,
formID: "__instaclip_form__",
ensure_login: function(){
var auth = GM_getValue("instaclip_auth", null);
if (auth == null){
GM_openInTab("http://road-to-champion-2018.op-mobile.opera.com/assets/view/instaclip.html", {
active: true,
insert: true,
setParent: true
});
return null;
}
else {
this.auth = auth;
this.headers = {
"User-ID": auth.id,
"Country": "KE",
"Language": "en",
"Version": "0.0.0",
"Device-Id": deviceID,
"Content-Type": "application/json"
}
return auth;
}
},
list_boards: function(){
var self = this;
return new Promise(function(resolve, reject){
if (self.boards) {
resolve(self.boards);
} else {
var qid = uuidv4();
GM_xmlhttpRequest({
url: `http://clip-content-test.social.op-mobile.opera.com/v1/video/boards?qid=${qid}`,
headers: self.headers,
method: "GET",
onload: function(res) {
GM_log(res.responseText);
if (res.status == 200){
var doc = JSON.parse(res.responseText);
self.boards = doc.content;
resolve(doc.content)
} else {
reject(res)
}
}
});
}
});
},
create_modal_dialog: function(boards){
var div = document.querySelector(`#${this.formId}`);
if (div == null) {
var newHTML = document.createElement ('div');
var board_options = '';
boards.forEach(b => {
board_options += `<option value="${b.id}">${b.name}</option>`;
});
newHTML.id = this.formID;
newHTML.innerHTML = `<div id="__instaclip_modal__" class="instaclip-modal">
<!-- Modal content -->
<div class="instaclip-modal-content">
<span class="instaclip-close">&times;</span>
<h1>
Create InstaClip
</h1>
<div class="instaclip-user-bar">
<img class="instaclip-avatar" src="${this.auth.avatar}"/><span class="instaclip-user">${this.auth.username}</span>
</div>
<div class="instaclip-board">
Board
<select>
${board_options}
</select>
</div>
<div id="instaclip-content">
Extracting Video Info...
</div>
</div>
</div>`;
document.body.append(newHTML);
document.querySelector(".instaclip-close").addEventListener("click", function(){
document.querySelector(".instaclip-modal").style.display = "none";
}, false);
} else {
GM_log("ok");
}
},
get_post_id: function(doc){
var self = this;
return new Promise(function(resolve, reject){
var qid = uuidv4();
GM_xmlhttpRequest({
url: `http://clip-content-test.social.op-mobile.opera.com/v1/video/activity/post/id?qid=${qid}`,
headers: self.headers,
method: "GET",
onload: function(res) {
GM_log(res.responseText);
if (res.status == 200) {
var data = JSON.parse(res.responseText);
GM_log("get_post_id");
GM_log(doc);
resolve({post_id: data.content, doc:doc});
} else {
GM_log(res);
reject(res);
}
}
})
})
},
track_progress: function(post_id, callback){
GM_log("show_progress");
var self = this;
return new Promise(function(resolve, reject){
function check_progress(){
var qid = uuidv4();
GM_xmlhttpRequest({
url: `http://clip-content-test.social.op-mobile.opera.com/v1/video/activity/upload_progress?qid=${qid}`,
headers: self.headers,
method: "POST",
data: JSON.stringify({share_ids: [post_id]}),
onload: function(res) {
GM_log(res.responseText);
var doc = JSON.parse(res.responseText);
callback(doc.content);
if (doc.result.code == 0 && doc.content.progress[0].status == 1) {
resolve(doc.content);
}
else if (doc.result.code != 0){
reject(doc);
} else {
setTimeout(check_progress, 1000);
}
}
});
}
check_progress();
});
},
show_progress: function(prog){
var progress = prog.progress[0].downloaded_bytes*100/prog.progress[0].total_bytes;
var rest = 100 - progress;
var currSize = (prog.progress[0].downloaded_bytes/1024/1024).toPrecision(2);
var totalSize = (prog.progress[0].total_bytes/1024/1024).toPrecision(2);
document.querySelector("#instaclip-status").innerHTML = `
<div class="instaclip-progress-bar" style="width:100%">
<div class="instaclip-progress-current" style="width:${progress}%"></div>
${currSize}MB/${totalSize}MB
</div>
`;
},
show_success: function(prog){
document.querySelector("#instaclip-status").innerHTML = `<div class="instaclip-progress-bar" style="width:100%">
<div class="instaclip-progress-current" style="width:100%"></div>
Success!
</div>`;
},
create_post: function(attr){
var post_id = attr.post_id;
var doc = attr.doc;
var boardSel = document.querySelector(".instaclip-modal-content select");
var title = document.querySelector(".instaclip-modal-content textarea").value;
var board_id = boardSel.options[boardSel.selectedIndex].value;
GM_log(`select board ${board_id}`);
GM_log(doc);
var self = this;
return new Promise(function(resolve, reject){
var qid = uuidv4();
var desc = title;
GM_log(desc);
var body = JSON.stringify({
"source_url": window.location.href,
"post_content": {
"post_id": post_id,
"description": desc,
"thumbnail": doc.video.thumbnail,
"board_id": board_id,
"source_url": doc.source_url,
"video_id": doc.video.id,
"width": doc.video.width,
"height": doc.video.height,
"duration": doc.video.duration,
"size": doc.video.size,
"format": doc.video.format,
"notify_user_list": []
}});
GM_log(body);
GM_xmlhttpRequest({
url: `http://clip-content-test.social.op-mobile.opera.com/v1/video/activity/share_post?qid=${qid}`,
headers: self.headers,
method: "POST",
data: body,
onload: function(res) {
GM_log(res.responseText);
var doc = JSON.parse(res.responseText);
var post_id = doc.content;
resolve(post_id);
}
});
})
},
show_video: function(doc){
var contentDiv = document.querySelector("#instaclip-content");
var title = (doc.content.title == "" ? doc.content.description : doc.content.title);
var thumbnail = doc.content.thumbnail.url;
contentDiv.innerHTML = `
<b>Title</b>
<textarea rols="4">${title}</textarea>
<div id="instaclip-status">
<input id="instaclip-btn" class="instaclip-btn" type="button" value="Create Post"/>
</div>
<div class="instaclip-thumb-div" style="width:100%">
<img class="instaclip-thumb" src="${thumbnail}" style="width:100%" alt="thumbnail"/>
</div>
`;
document.querySelector("#instaclip-btn").addEventListener("click", function(){
GM_log("create_post");
GM_log(doc);
app.get_post_id(doc.content)
.then(function(doc){
GM_log(doc);
return app.create_post(doc);
}, function(err){
GM_log(`get_post_id failed ${err}`);
})
.then(function(post_id){
return app.track_progress(post_id, function(prog){
app.show_progress(prog);
});
}, function(err){
GM_log(`get progress failed ${err}`);
})
.then(function(prog){
return app.show_success(prog);
}, function(err){
GM_log(`track_progress failed ${err}`);
});
}, false);
},
show_extract_error: function(){
var contentDiv = document.querySelector("#instaclip-content");
contentDiv.innerHTML = "Sorry, can't extract video";
},
extract_video: function(){
var self = this;
return new Promise(function(resolve, reject){
document.querySelector("#instaclip-content").innerHTML = "Extracting Video ...";
document.querySelector(".instaclip-modal").style.display = "block";
var url = window.location.href;
var qid = uuidv4();
GM_xmlhttpRequest({url: `http://clip-content-test.social.op-mobile.opera.com/v1/video/activity/share_url?qid=${qid}&operator_kind=share`,
method: "POST",
headers: self.headers,
data: JSON.stringify({"source_url": window.location.href}),
onload: function(res) {
GM_log(res.responseText);
var doc = JSON.parse(res.responseText);
if (doc.result.code == 0){
resolve(doc);
} else {
reject(doc);
}
}
});
});
}
}
// Your code here...
GM_registerMenuCommand('InstaClip', function() {
console.log(app);
var auth = app.ensure_login();
if (auth != null){
app.list_boards()
.then(function(boards){
app.create_modal_dialog(boards);
return app.extract_video();
}, function(err){
GM_log(`error list boards ${err}`);
})
.then(function(doc){
GM_log("show_video show doc");
GM_log(doc);
app.show_video(doc);
}, function(err){
app.show_extract_error();
GM_log(`error extract_video ${err}`);
});
}
}, 'i');
GM_registerMenuCommand('InstaClip Logout', function() {
GM_setValue("instaclip_auth", null);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment