').click(function () {
$(this).toggleClass("bigThumbnail");
$(this).next().children().first().toggleClass("hidename");
});
var messageTime = '
').click(switchSection.bind(null, 'User', event.user_id));
var username = $('
').click(function () { // insert username to text field
textBox.val(textBox.val() + '@' + $(this).text().substr(1, $(this).text().length - 2) + ' ');
textBox.focus();
});
var html = $('
');
messageBox.append(profImage,html);
if (!event.body) // for debug
console.log('empty body!', event);
container.append(messageBox);
}
break;
case 2: // heart
/*moderationReportType: 0
moderationType: 0*/
break;
case 3: // "joined"
if (event.displayName && !$.isArray(container))
userlistAdd(event);
break;
case 4: // broadcaster moved to new place
if ($('#debug')[0].checked && !$.isArray(container))
console.log('new location: ' + event.lat + ', ' + event.lng + ', ' + event.heading);
break;
case 5: // broadcast ended
if (!$.isArray(container))
container.append('
*** ' + (event.displayName || 'Broadcaster') + (event.username ? ' (@' + event.username + ')' : '') + ' ended the broadcast
');
break;
case 6: // followers invited
if (!$.isArray(container))
container.append('
*** ' + (event.displayName || '') + ' (@' + event.username + '): ' + event.body.replace('*%s*', event.invited_count) + '
');
break;
case 7: // BROADCAST_STARTED_LOCALLY (?)
if (!$.isArray(container)) {
container.append('
');
console.log('BROADCAST_STARTED_LOCALLY', event);
}
break;
case 8: // replay available
break;
case 9: // Broadcaster starts streaming. uuid=SE-0. timestampPlaybackOffset
break;
case 10: //LOCAL_PROMPT_TO_FOLLOW_BROADCASTER (?)
if (!$.isArray(container)) {
container.append('
');
console.log('LOCAL_PROMPT_TO_FOLLOW_BROADCASTER', event);
}
break;
case 11: //LOCAL_PROMPT_TO_SHARE_BROADCAST (?)
if (!$.isArray(container)) {
container.append('
');
console.log('LOCAL_PROMPT_TO_SHARE_BROADCAST', event);
}
break;
case 12: // Ban
case 14: //SUBSCRIBER_BLOCKED_VIEWER
if ($.isArray(container))
container.push({
date: date,
user: '',
text: '@' + event.broadcasterBlockedUsername + ' has been blocked for message: "' + event.broadcasterBlockedMessageBody +'"'
});
else
container.append('
*** @' + event.broadcasterBlockedUsername + ' has been blocked for message: "' + emoji_to_img(event.broadcasterBlockedMessageBody) + '"
');
break;
case 13: //SUBSCRIBER_SHARED_ON_TWITTER
if (!$.isArray(container))
container.append('
*** ' + (event.displayName || '') + ' (@' + event.username + ') shared on twitter
');
break;
case 15: //SUBSCRIBER_SHARED_ON_FACEBOOK
if (!$.isArray(container))
container.append('
*** ' + (event.displayName || '') + ' (@' + event.username + ') shared on facebook
');
break;
case 16: //SCREENSHOT
if (!$.isArray(container))
container.append('
*** ' + (event.displayName || '') + (event.username ? ' (@' + event.username + ')':'')+' has made the screenshot
');
break;
default: // service messages (event.action = join, leave, timeout, state_changed)
if ($('#debug')[0].checked)
console.log('renderMessages default!', event);
/*event.occupancy && event.total_participants*/
break;
}
}
function processWSmessage (message, div) {
message.payload = JSON.parse(message.payload);
message.body = $.extend(JSON.parse(message.payload.body), message.payload.sender);
if ($('#autoscroll')[0].checked)
chat[0].scrollTop = chat[0].scrollHeight;
switch (message.kind) {
case MESSAGE_KIND.CHAT:
renderMessages(message.body, div);
break;
case MESSAGE_KIND.CONTROL:
switch (message.payload.kind) {
case MESSAGE_KIND.PRESENCE:
$('#presence').text(message.body.occupancy + '/' + message.body.total_participants);
break;
case MESSAGE_KIND.CHAT: // smb joined
userlistAdd(message.body); // message.payload.cap
break;
case MESSAGE_KIND.CONTROL: // smb left
userlistRemove(message.body); //message.payload.of
break;
default:
console.log(message);
}
break;
default:
console.log('default!', message);
}
}
var playButton = $('
').click(function () {
clearInterval(chat_interval);
if (NODEJS && ws && ws.readyState == ws.OPEN)
ws.close();
chat.empty();
userlist.empty();
title.empty();
historyDiv.empty();
//Load user list
PeriscopeWrapper.V2_POST_Api('getBroadcastViewers', {
broadcast_id: broadcast_id.val().trim()
}, function(viewers){
userlist.empty();
var user;
for (var j in viewers.live)
if ((user = viewers.live[j]) && user.display_name) {
userlistAdd(user);
}
});
PeriscopeWrapper.V2_POST_Api('accessChannel', {
broadcast_id: broadcast_id.val().trim()
}, function (broadcast) {
var userLink = $('
').click(switchSection.bind(null, 'User', broadcast.broadcast.user_id));
var srtLink = $('
').click(function () {
Progress.start();
var data = [];
historyLoad('', data, function(){
data.sort(function (a, b) { return a.date - b.date; });
var start = new Date(broadcast.broadcast.start);
var srt = '';
for (var i = 0; i < data.length; i++) {
var date0 = new Date(data[i].date - start); // date of the current message
var date1 = new Date((i < data.length - 1 ? data[i + 1].date : new Date(broadcast.broadcast.end || new Date())) - start); // date of the next message
srt += (i + 1) + '\n' +
zeros(date0.getUTCHours()) + ':' + zeros(date0.getMinutes()) + ':' + zeros(date0.getSeconds()) + ','+date0.getMilliseconds()+' --> ' +
zeros(date1.getUTCHours()) + ':' + zeros(date1.getMinutes()) + ':' + zeros(date1.getSeconds()) + ','+date1.getMilliseconds()+'\n' +
(i > 3 ? '
: ' + data[i].text + '\n\n';
}
var filename = (broadcast.broadcast.status || 'Untitled') + '.srt';
srtLink.unbind('click')
.click(saveAs.bind(null, srt, filename))
.attr('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(srt))
.attr('download', filename)
.get(0).click();
});
});
title.html((broadcast.read_only?'Read-only | ':'') + '
| '
+ emoji_to_img(broadcast.broadcast.user_display_name) + ' ')
.append(userLink,
broadcast.hls_url ? ' |
' : '',
' | ', srtLink
);
// Load history
function historyLoad(start, container, callback) {
GM_xmlhttpRequest({
method: 'POST',
url: broadcast.endpoint + '/chatapi/v1/history',
data: JSON.stringify({
access_token: broadcast.access_token,
cursor: start,
duration: 100 // actually 40 is maximum
}),
onload: function (history) {
if (history.status == 200) {
history = JSON.parse(history.responseText);
for (var i in history.messages)
processWSmessage(history.messages[i], container || historyDiv);
if (history.cursor != '')
historyLoad(history.cursor, container, callback);
else {
Progress.stop();
if (Object.prototype.toString.call(callback) === '[object Function]')
callback();
}
} else {
Progress.stop();
if (Object.prototype.toString.call(callback) === '[object Function]')
callback();
}
}
});
}
chat.append(historyDiv, $('
').click(function () {
Progress.start();
historyLoad('');
$(this).remove();
}));
if (broadcast.read_only)
switch (broadcast.type) {
case "StreamTypeOnlyFriends":
chat.append('
');
break;
default:
chat.append('
');
}
// Chat reading & posting
if (NODEJS) {
var openSocket = function (failures) {
ws = new WebSocket(broadcast.endpoint.replace('https:', 'wss:').replace('http:', 'ws:') + '/chatapi/v1/chatnow');
ws.on('open', function open() {
// AUTH
ws.send(JSON.stringify({
payload: JSON.stringify({access_token: broadcast.access_token}),
kind: MESSAGE_KIND.AUTH
}));
// JOIN
ws.send(JSON.stringify({
payload: JSON.stringify({
body: JSON.stringify({
room: broadcast.room_id
}),
kind: MESSAGE_KIND.CHAT
}),
kind: MESSAGE_KIND.CONTROL
}));
});
ws.on('ping', function (data) {
ws.pong(data, {masked: false, binary: true});
});
ws.on('message', function (data) {
processWSmessage(JSON.parse(data), chat);
});
ws.on('close', function (code) {
ws.close();
switch (code) {
case 403: // 403=forbidden
console.log('Forbidden');
break;
case 1006: // 1006=timeout
if (failures < 4) {
setTimeout(openSocket.bind(null, failures + 1), 100);
console.log('reconnect ' + failures);
}
break;
case 1000: // 1000=broadcast ended
break;
default:
console.log('websocket closed, code: ', code);
}
});
ws.on('error', function () {});
};
function uuid() {//function from stackoverflow
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
var sendMessage = function (customtype) {
var timestamp = Math.floor(Date.now() / 1000);
var ntpstamp = parseInt((timestamp + 2208988800).toString(16) + '00000000', 16); // timestamp in NTP format
var message = {
body: textBox.val(),
//display_name: 'OpenPeriscope',
//initials: '',
//"moderationReportType": 0,
//"moderationType": 0,
//v: 2
profileImageURL: loginTwitter.user.profile_image_urls[0].url,
timestamp: timestamp,
remoteID: loginTwitter.user.id,
username: loginTwitter.user.username,
uuid: uuid(),// no longer identifie yourself as open periscope user on comment or heart.
signer_token: broadcast.signer_token,
participant_index: broadcast.participant_index,
type: customtype || 1, // "text message"
ntpForBroadcasterFrame: ntpstamp,
ntpForLiveFrame: ntpstamp
};
ws.send(JSON.stringify({
payload: JSON.stringify({
body: JSON.stringify(message),
room: broadcast.room_id,
timestamp: timestamp
//sender
}),
kind: MESSAGE_KIND.CHAT
}), function (error) {
textBox.val('');
if (error)
console.log('message not sent', error);
else
renderMessages(message, chat);
});
};
if (broadcast.endpoint)
openSocket(0);
} else {
if (broadcast.endpoint) {
var cursor = null;
clearInterval(chat_interval);
var prevMessages = [];
chat_interval = setInterval(function () {
GM_xmlhttpRequest({
method: 'POST',
url: broadcast.endpoint + '/chatapi/v1/history',
data: JSON.stringify({
access_token: broadcast.access_token,
cursor: cursor || (Date.now() - 30000) * 1000000 + '',
limit: 1000
}),
onload: function (history) {
if (history.status == 200) {
history = JSON.parse(history.responseText);
for (var i in history.messages) {
var contains = false;
for (var j = 0; j < prevMessages.length && !contains; j++) // if prevMessages contains meesage
if (prevMessages[j].signature == history.messages[i].signature)
contains = true;
if (!contains)
processWSmessage(history.messages[i], chat);
}
prevMessages = history.messages;
cursor = history.cursor;
}
}
});
}, 1000);
}
var sendMessage = function () {
alert('Sending messages available only in standalone version');
};
}
$('#sendMessage').off().click(sendMessage.bind(null, null));
$('#sendLike').off().click(sendMessage.bind(null, 2));
textBox.off().keypress(function (e) {
if (e.which == 13) {
sendMessage();
return false;
}
});
}, function (error) {
title.append('
').append(
'
Autoscroll',
'
\u2665 Send ', $('
').append(textBox)
)
)
);
},
User: function () {
var resultUser = $('
');
var showButton = $('
OK ').click(function () {
resultUser.empty();
var id = $('#user_id').val().trim();
var name = $('#user_name').val().trim();
name.startsWith('@') ? (name = name.slice('1',name.length)) : '';
var param = {user_id : id};
name ? param = {username : name} : '' ;
PeriscopeWrapper.V2_POST_Api('user', param, function (response) {
id = response.user.id;
$('#user_id').val(id);
resultUser.prepend(getUserDescription(response.user));
FollowersSpoiler.append(' (' + response.user.n_followers + ')');
FollowingSpoiler.append(' (' + response.user.n_following + ')');
PeriscopeWrapper.V2_POST_Api('userBroadcasts', {
user_id: id,
all: true
}, function (broadcasts) {
refreshList($('#userBroadcasts'), null, "userBroadcasts")(broadcasts);
BroadcastsSpoiler.append(' (' + broadcasts.length + ')').click();
});
},function(response){resultUser.prepend(response)});
var BroadcastsSpoiler = $('');
var FollowersSpoiler = $('').on("jq-spoiler-visible", function() {
var followersDiv = $('#userFollowers');
if (!followersDiv.html())
PeriscopeWrapper.V2_POST_Api('followers', {
user_id: id
}, function (followers) {
if (followers.length){
FollowersSpoiler.append(' (' + followers.length + ')');
for (var i in followers)
followersDiv.append($('
').append(getUserDescription(followers[i])));
}
else
followersDiv.html('No results');
});
});
var FollowingSpoiler = $('').on("jq-spoiler-visible", function() {
var followingDiv = $('#userFollowing');
if (!followingDiv.html())
PeriscopeWrapper.V2_POST_Api('following', {
user_id: id
}, function (following) {
if (following.length){
FollowingSpoiler.append(' (' + following.length + ')');
for (var i in following)
followingDiv.append($('
').append(getUserDescription(following[i])));
}
else
followingDiv.html('No results');
});
});
resultUser.append(BroadcastsSpoiler, '
',
FollowersSpoiler, '
',
FollowingSpoiler, '
');
if (id == loginTwitter.user.id) { // Blocked list
var BlockedSpoiler = $('').on("jq-spoiler-visible", function() {
var blockedDiv = $('#userBlocked');
if (!blockedDiv.html())
PeriscopeWrapper.V2_POST_Api('block/users', {}, function (blocked) {
if (blocked.length)
for (var i in blocked) {
blocked[i].is_blocked = true;
blockedDiv.append($('
').append(getUserDescription(blocked[i])));
}
else
blockedDiv.html('No results');
});
});
resultUser.append(BlockedSpoiler, '
');
}
$(".spoiler").off("click").spoiler({ triggerEvents: true });
});
var idInput = $('
id:
');
$('#right').append(idInput.append(showButton, '
', resultUser));
},
People: function () {
var refreshButton = $('
Refresh ').click(function () {
PeriscopeWrapper.V2_POST_Api('suggestedPeople', {
languages: [$('#People .lang').val()]
}, function (response) {
var result = $('#resultPeople');
result.empty();
if (response.featured && response.featured.length) {
result.append('
Featured ');
for (var i in response.featured)
result.append($('
').append(getUserDescription(response.featured[i])));
}
result.append('
Popular ');
for (i in response.popular)
result.append($('
').append(getUserDescription(response.popular[i])));
PeriscopeWrapper.V2_POST_Api('suggestedPeople', {}, function (response) {
if (response.hearted && response.hearted.length) {
result.append('
Hearted ');
for (var i in response.hearted)
result.append($('
').append(getUserDescription(response.hearted[i])));
}
});
});
});
var searchPeople = function () {
PeriscopeWrapper.V2_POST_Api('userSearch', {
search: $('#search').val()
}, function (response) {
var result = $('#resultPeople');
result.html('
Search results ');
var found_exact = false;
for (var i in response) {
result.append($('
').append(getUserDescription(response[i])));
if (!found_exact && response[i].username.toUpperCase() == $('#search').val().toUpperCase())
found_exact=true;
}
if (!found_exact)
PeriscopeWrapper.V2_POST_Api('user', {
username: $('#search').val()
}, function (user) {
result.prepend($('
').append(getUserDescription(user.user)));
});
});
};
var searchButton = $('
Search ').click(searchPeople);
$('#right').append($('
').append(languageSelect, refreshButton, $('
').keypress(function (e) {
if (e.which == 13) {
searchPeople();
return false;
}
}), searchButton, '
'));
$("#People .lang").find(":contains(" + (navigator.language || navigator.userLanguage || "en").substr(0, 2) + ")").attr("selected", "selected");
refreshButton.click();
},
Edit: function () {
var button = $('
Save ').click(function () {
var uname = $('#uname').val();
if (uname != loginTwitter.user.username) {
PeriscopeWrapper.V2_POST_Api('verifyUsername', {
username: uname,
display_name: loginTwitter.user.display_name
}, function () {
loginTwitter.user.username = uname;
localStorage.setItem('loginTwitter', JSON.stringify(loginTwitter));
});
}
var description = $('#description').val();
if (description != loginTwitter.user.description)
PeriscopeWrapper.V2_POST_Api('updateDescription', {
description: description
}, function () {
loginTwitter.user.description = description;
localStorage.setItem('loginTwitter', JSON.stringify(loginTwitter));
});
var dname = $('#dname').val();
if (dname != loginTwitter.user.display_name) {
PeriscopeWrapper.V2_POST_Api('updateDisplayName', {
display_name: dname
}, function () {
loginTwitter.user.display_name = dname;
localStorage.setItem('loginTwitter', JSON.stringify(loginTwitter));
});
}
if ($('input[name="image"]').val())
form.submit();
});
var form = $('
');
var hiddenIframe = $('
').on('load',refreshProfile);
var settingsContainer = $('
');
var tempSettings;
PeriscopeWrapper.V2_POST_Api('getSettings', {}, function (settingsResponse) {
loginTwitter.settings = settingsResponse;
localStorage.setItem('loginTwitter', JSON.stringify(loginTwitter));
tempSettings = settingsResponse;
for (var setting in loginTwitter.settings) {
settingsContainer.append($('
' + setting + '').click(function (setting) {
return function (e) {
tempSettings[setting] = e.target.checked;
}
}(setting)));
}
});
var buttonSettings = $('
Save ').click(function () {
PeriscopeWrapper.V2_POST_Api('setSettings', {
settings: tempSettings
}, function (r) {
if (r.success){
loginTwitter.settings = tempSettings;
localStorage.setItem('loginTwitter', JSON.stringify(loginTwitter));
} else
alert('Settings not saved!');
});
});
var notifications = $('
Enable notifications').click(function (e) {
setSet('followingNotifications', e.target.checked);
if (e.target.checked)
Notifications.start();
else
Notifications.stop();
});
var notifications_interval = $('
').change(function () {
setSet('followingInterval', this.value);
Notifications.stop();
Notifications.start();
});
if (NODEJS) {
var autoDownload = $('
Enable automatic downloading of the following items').click(function (e) {
setSet('automaticDownload', e.target.checked);
if (e.target.checked)
Notifications.start();
else
Notifications.stop();
});
var download_private = $('
Private broadcasts').click(function (e) {
setSet('privateDownload', e.target.checked);
});
var download_following = $('
Following broadcasts').click(function (e) {
setSet('followingDownload', e.target.checked);
});
var download_shared = $('
Shared broadcasts').click(function (e) {
setSet('sharedDownload', e.target.checked);
});
var download_Selected = $('
Selected users broadcasts').click(function (e) {
setSet('selectedDownload', e.target.checked);
});
var current_download_path = $('
' + settings.downloadPath + ' ');
var download_path = $('
').append($('
').change(function () {
setSet('downloadPath', $(this).val());
current_download_path.text($(this).val());
}));
// var download_format = $('
').append($('
' +
// 'MP4 ' +
// 'TS ' +
// ' ').change(function () {
// setSet('downloadFormat', $(this).val());
// }));
var log_broadcasts_to_file = $('
Log broadcasts to a file').click(function (e) {
setSet('logToFile', e.target.checked);
});
var replay_time_limit = $('
').change(function () {
setSet('replayTimeLimit', this.value);
});
}
if (!NODEJS) {
var show_nodeDown_links = $('
Show node periscopeDownloader links').click(function (e) {
setSet('showNodeDownLinks', e.target.checked);
});
var show_nodeDown_linksPrv = $('
Private broadcasts only').click(function (e) {
setSet('showNodeDownLinksPrv', e.target.checked);
});
}
var show_m3u_links = $('
Show M3U links').click(function (e) {
setSet('showM3Ulinks', e.target.checked);
});
var show_partial_links = $('
Show partial replay(PR) links').click(function (e) {
setSet('showPRlinks', e.target.checked);
});
var update_thumbnails = $('
Auto update thumbnails').click(function (e) {
setSet('updateThumbnails', e.target.checked);
});
var open_preview_in_separate_windows = $('
Open previews in separate windows').click(function (e) {
setSet('previewSeparateWindows', e.target.checked);
});
var fileNameButton = $('
Save ').click(function () {
setSet('userPartialShort', $('#partialShort').val());
setSet('userReplayShort', $('#replayShort').val());
setSet('userPrivateShort', $('#privateShort').val());
setSet('userProducerShort', $('#producerShort').val());
setSet('userFolderName', $('#folderName').val());
setSet('userFileName', $('#fileName').val());
});
var resetToDefault = $('
Default ').click(function () {
$('#partialShort').val(DefaultFolderFileNames.partialShort);
$('#replayShort').val(DefaultFolderFileNames.replayShort);
$('#privateShort').val(DefaultFolderFileNames.privateShort);
$('#producerShort').val(DefaultFolderFileNames.producerShort);
$('#folderName').val(DefaultFolderFileNames.folderName);
$('#fileName').val(DefaultFolderFileNames.fileName);
});
var ProfileEditSpoiler = $('');
var ProfileEdit = $('
')
.append('
Display name: ' +
'
Username: ' +
'
Description: ' +
'
Avatar: ', hiddenIframe, form, '
', button
);
var MyOpSettingsSpoiler = $('');
var MyOpSettings = $('
')
.append(notifications , '
',
autoDownload, '
',
download_private, '
',
download_following, '
',
download_shared, '
',
download_Selected, '
',
'Notifications refresh interval: ', notifications_interval ,' seconds','
',
'Limit replay for auto-download: ', replay_time_limit,' seconds','
',
(NODEJS ? ['
Downloads path: ', current_download_path, download_path, '
'] : ''),
'
', log_broadcasts_to_file,
'
', update_thumbnails,
'
', open_preview_in_separate_windows,
'
', show_m3u_links,
'
', show_partial_links,
'
', show_nodeDown_links,
'
', show_nodeDown_linksPrv
);
var NamesEditorSpoiler = $('');
var NamesEditor = $('
')
.append(
'
#{id}, #{language}, #{status}, #{user_display_name}, #{user_id}, #{username}, #{year}, #{month}, #{day}, #{hour}, #{minute}, #{second}
' +
'
#{partial}: ' +
'
#{replay}: ' +
'
#{private}: ' +
'
#{producer}: ' +
'
Folder name: ' +
'
File name: ',
fileNameButton , resetToDefault
);
var PeriSettingsSpoiler = $('');
var PeriSettings = $('
').append(settingsContainer, "
", buttonSettings);
$('#right').append($('
').append(
ProfileEditSpoiler, ProfileEdit,
MyOpSettingsSpoiler, MyOpSettings,
NamesEditorSpoiler, NamesEditor,
PeriSettingsSpoiler, PeriSettings
));
$(".spoiler").off("click").spoiler({ triggerEvents: true });
MyOpSettingsSpoiler.click();
},
Dmanager: function (go_to_bid) {
var result = $('
');
var refreshButton = $('
Refresh ').click(function () {dManagerDescription(result)});
var removefinished = $('
Remove Finished ').click(function () {
if (childProcesses && childProcesses.length){
childProcesses=childProcesses.filter(function(proc){
return proc.exitCode === null;
})
refreshButton.click();
}
});
var goButton = $('
Go ').click(function () {
var dowLink = $('#broadcastLink').val().trim();
var validLink = (dowLink.startsWith('https://www.periscope.tv/') || dowLink.startsWith('https://www.pscp.tv/'));
if(validLink){
var broadcast_id = dowLink.split('/')[4];
var urlCallback = function(live, replay, cookies, _name, _folder_name, _broadcast_info) {
var live_url = $('#right > div:visible >div').find('#templiveUrl');
if(live){
live_url.val(live);
getURL(broadcast_id, urlCallback, true);
}else if(replay){
download(_folder_name, _name, live_url.val(), replay, cookies, _broadcast_info);
live_url.val(null);
}
}
getURL(broadcast_id, urlCallback);
setTimeout(function(){refreshButton.click()},5000);
}
});
var linkInput = $('
' + '
').append(goButton);
$('#right').append($('
').append(refreshButton, removefinished,'', linkInput, result));
refreshButton.click();
if(go_to_bid){
var dowCards = $('.downloadCard.' + go_to_bid );
setTimeout(function(){
document.documentElement.scrollTop = 0;
dowCards[0].scrollIntoView({behavior: 'smooth'});
dowCards.addClass('focusedDownloadCard');
},0);
}
},
Console: function () {
var resultConsole = $('
');
var downloadButton = $('
Download ').click(function () {
resultConsole.empty();
var dl = download($('#download_folder_name').val().trim(), $('#download_name').val().trim(), $('#download_url').val().trim(), $('#download_replay_url').val().trim(), $('#download_cookies').val().trim(), JSON.parse($('#download_response').val().trim() || '""'), resultConsole);
var gui = require('nw.gui');
gui.Window.get().removeAllListeners('close').on('close', function(){
try {
dl.stdin.end('q', dl.kill);
} finally {
gui.App.quit();
}
});
});
$('#right').append($('
').append('
URL: ' +
'
R_URL: ' +
'
Cookies: ' +
'
Name: ' +
'
Folder: ' +
'
Key: ' +
'
',
downloadButton, '
', resultConsole));
}
};
var chat_interval;
var ws; // websocket
var MESSAGE_KIND = {
CHAT: 1,
CONTROL: 2,
AUTH: 3,
PRESENCE: 4
};
/* LEVEL 1 */
function cleanFilename(filename){
var tmp = filename.replace(/[<>+\\/:"|?*]/g, '');
if (tmp.length > 100)
tmp = tmp.substring(0, 98);
if (tmp.endsWith('.'))
tmp = tmp.replace(/\.$/, '_')
return tmp;
}
function zeros(number) {
return (100 + number + '').substr(1);
}
var broadcastsWithLinks = {
idsQueue:[],
addToBroadcastsLinks(id,params){
if(this[id]){
$.extend(true, this[id], params);
}else{
this[id] = params;
}
}
};
function addUserContextMenu(node, id, username) {
node.contextmenu(function (ev) {
ev.preventDefault();
var contextmenu = $('')
.append($('
Follow
').click(function () {
PeriscopeWrapper.V2_POST_Api('follow', {
user_id: id
});
}))
.append($('
Unfollow
').click(function () {
PeriscopeWrapper.V2_POST_Api('unfollow', {
user_id: id
});
}))
.append('
Copy profile URL
' +
'
Copy username
' +
'
Copy user ID
')
.append($('
Block user
').click(function () {
PeriscopeWrapper.V2_POST_Api('block/add', {
to: id
});
}))
.append($('
Unlock user
').click(function () {
PeriscopeWrapper.V2_POST_Api('block/remove', {
to: id
});
}))
.append($('
Profile on Periscope
').click(function(){
var win = window.open(
"https://periscope.tv/" + username,
"PeriscopeProfile" + (settings.previewSeparateWindows?username:''),
"toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=800,height=550,top=100,left="+(screen.width/2));
}))
.click(function (event) {
$(this).remove();
})
.mousedown(function (event) {
event.stopPropagation();
});
$(document.body).append(contextmenu).off('mousedown').mousedown(function () {
contextmenu.remove();
});
new ClipboardJS('.contextmenu div');
})
};
broadcastsCache = {
idsQueue: [],
oldBroadcastsList: [],
interestingList: [],
autoGettinList: [],
filters:{
hideProducer: false,
hideEnded: false,
languages:{
Arabic: 'ar',
Armenian: 'hy',
Chinese: 'zh',
Danish: 'da',
English: ['en','us'],
Finnish: 'fi',
French: 'fr',
German: 'de',
Hebrew: 'he',
Indonesian: 'id',
Italian: 'it',
Japanese: 'ja',
Kazakh: 'kk',
Korean: 'ko',
Norwegian: 'nb',
Polish: 'pl',
Portuguese: 'pt',
Romanian: 'ro',
Russian: 'ru',
Spanish: 'es',
Swedish: 'sv',
Turkish: 'tr',
Ukrainian: 'uk',
other: 'other'
},
languagesToHide: []
}
}
function refreshList(jcontainer, title, refreshFrom) { // use it as callback arg
return function (response) {
jcontainer.html(title || '
');
if (response.length) {
var ids = [];
var createCard = function (index) {
var resp;
if (refreshFrom === 'following' && !settings.classic_cards_order){
var deleted = existingBroadcasts.indexOf(broadcastsCache.idsQueue[index]) < 0;
resp = broadcastsCache[broadcastsCache.idsQueue[index]];
} else {
resp = response[index];
}
deleted ? resp.state = 'ENDED' : ''; //prevent some cached broadcasts from showing as running.
var isPrivate = resp.is_locked;
var producer = (resp.broadcast_source === 'producer' || resp.broadcast_source === 'livecms');
var newHighlight = (broadcastsCache.oldBroadcastsList.indexOf(resp.id) < 0 && refreshFrom === 'following' && broadcastsCache.oldBroadcastsList.length !== 0) ? ' newHighlight' : '';
var stream = $('
').append(getDescription(resp));
if (refreshFrom != "userBroadcasts")
addUserContextMenu(stream, resp.user_id, resp.username);
var link = $('
Get stream link ');
link.click(getM3U.bind(null, resp.id, stream));
let recLink = $('
').append(downloadStatus(resp.id, true));
var downloadWhole = $('
Download ').click(getBothURLs.bind(null, resp.id));
if (refreshFrom === 'following' ){
var repeat_getTheLink = (settings.showPRlinks && resp.state === 'RUNNING')? ($('
= 0) ? 'checked' : '') + '/> repeat').click({param1: resp.id},function (e) {
if(e.target.checked) {
broadcastsCache.autoGettinList.push(e.data.param1);
}else{
broadcastsCache.autoGettinList.splice(broadcastsCache.autoGettinList.indexOf(e.data.param1),1);
}
broadcastsCache.interestingList.indexOf(e.data.param1) < 0 ? broadcastsCache.interestingList.push(e.data.param1) : '';
if(broadcastsCache.interestingList.length > 100)
broadcastsCache.interestingList.shift();
})) : '';
broadcastsCache.filters.hideEnded && !newHighlight && (resp.state != 'RUNNING') ? stream.hide() : '';
}
broadcastsCache.filters.hideProducer && !newHighlight && producer ? stream.hide() : '';
broadcastsCache.filters.languagesToHide.indexOf(resp.language) >= 0 ? stream.hide() : '';
ids.push(resp.id);
var replayLinkExists = false;
if (broadcastsWithLinks.hasOwnProperty(resp.id) ){
var brwlID = broadcastsWithLinks[resp.id];
var rep = brwlID.RclipboardLink;
var repM3U = brwlID.Rm3uLink;
var liv = brwlID.clipboardLink;
var showDowLink = !NODEJS && (settings.showNodeDownLinks || (settings.showNodeDownLinksPrv && isPrivate)) && settings.showPRlinks;
rep ? replayLinkExists = brwlID.RdownloadLink.hasClass('linkReplay') : '';
if(liv && !replayLinkExists){
var clipboardLink = brwlID.clipboardLink.clone();
new ClipboardJS(clipboardLink.get(0));
var clipboardDowLink = brwlID.clipboardDowLink.clone();
new ClipboardJS(clipboardDowLink.get(0));
stream.find('.responseLinks').append(
(settings.showM3Ulinks && brwlID.m3uLink) ? [brwlID.m3uLink.clone(), ' | '] : '',
NODEJS ? [brwlID.downloadLink.clone(true,true), ' | '] : '',
clipboardLink,
showDowLink ? [' | ', clipboardDowLink] : '', '
'
);
}
if(rep){
var RclipboardLink = brwlID.RclipboardLink.clone();
new ClipboardJS(RclipboardLink.get(0));
var RclipboardDowLink = brwlID.RclipboardDowLink.clone();
new ClipboardJS(RclipboardDowLink.get(0));
stream.find('.responseLinksReplay').append(
(settings.showM3Ulinks && settings.showPRlinks && repM3U) ? [repM3U.clone(true,true), ' | '] : '',
(settings.showPRlinks && NODEJS ? [brwlID.RdownloadLink.clone(true,true), ' | '] : ''),
settings.showPRlinks ? RclipboardLink : '',
showDowLink ? [' | ', RclipboardDowLink] : '', '
'
);
}
}
var addMethod = '';
refreshFrom === 'following' && !settings.classic_cards_order ? addMethod = 'prepend' : '';
refreshFrom !== 'following' || settings.classic_cards_order ? addMethod = 'append' : '';
jcontainer[addMethod](stream.append(recLink, ((NODEJS && !replayLinkExists)? [downloadWhole, ' | '] : ''), link).append((refreshFrom === 'following') ? repeat_getTheLink : ''));
}
if (refreshFrom === 'following'){
var existingBroadcasts = [];
var interestingToggle = false;
var interestingList = [];
for (var o in response) {
existingBroadcasts.push(response[o].id);
}
for (var i = response.length - 1; i >= 0; i--) {
broadcastsCache[response[i].id] = response[i];
limitAddIDs(broadcastsCache, response[i].id, 100, existingBroadcasts);
}
if (settings.classic_cards_order){
for (var i in response){
createCard(i)
}
} else {
for (var i = broadcastsCache.idsQueue.length - 1; i >= 0; i--)
createCard(i)
}
broadcastsCache.oldBroadcastsList = [];
for (var x in broadcastsCache.idsQueue) {
broadcastsCache.oldBroadcastsList.push(broadcastsCache.idsQueue[x]);
}
jcontainer.prepend($('
Show interesting only ').click(function () {
var cards = jcontainer.find('.card');
$.each(cards, function(i){
for(var a in broadcastsCache.interestingList){
if($(cards[i]).hasClass(broadcastsCache.interestingList[a])){
$(cards[i]).addClass('interesting');
break;
}
}
})
if(!interestingToggle){
interestingToggle = true;
cards.filter(":visible").each(function(index, card){
interestingList.push(card.getAttribute('nr'))
})
cards.not(".interesting").hide();
$(".interesting").show();
}else{
interestingToggle = false;
$(".interesting").hide();
cards.filter(function(i, card){
return (interestingList.indexOf(card.getAttribute('nr')) >= 0)
}).show()
interestingList = [];
}
$(window).trigger('scroll'); // for lazy load
}));
} else { //if not following tab
for (var i in response)
createCard(i)
}
var sortedAlready = false;
jcontainer.prepend($('
Sort by watching ').click(function () { // sort cards in given jquery-container
var cards = jcontainer.find('.card');
if(!sortedAlready){
var sorted = cards.sort(function (a, b) {
return $(b).find('.watching').text() - $(a).find('.watching').text();
});
sortedAlready = true;
}else{
var sorted = cards.sort(function (a, b) {
return $(a)[0].getAttribute('nr') - $(b)[0].getAttribute('nr');
});
sortedAlready = false;
}
jcontainer.append(sorted);
$(window).trigger('scroll'); // for lazy load
}));
if (typeof response[0].n_watching == 'undefined')
PeriscopeWrapper.V2_POST_Api('getBroadcasts', {
broadcast_ids: ids,
only_public_publish: true
}, function (info) {
for (var i in info)
$('.card.' + info[i].id + ' .watching').text(info[i].n_watching);
});
} else
jcontainer.append('No results');
// if jcontainer isn't visible, scroll to it
var top = jcontainer.offset().top;
if ($(window).scrollTop() + $(window).height() - 100 < top) {
$(window).scrollTop(top);
}
};
}
function limitAddIDs(toObject, whatID, howMany, inResponse){
if(toObject.idsQueue.indexOf(whatID) === -1)
toObject.idsQueue.unshift(whatID);
var len = toObject.idsQueue.length;
if(len > howMany){ //to keep catched broadcasts under (howMany) remove first card from bottom that holds deleted broadcast
for(var i = len - 1; i >= 0; i--){
if(inResponse.indexOf(toObject.idsQueue[i]) < 0){//if broadcast(id) is on oryginal following list then don't delete it.
delete toObject[toObject.idsQueue[i]]
toObject.idsQueue.splice(i,1)
return;
}//TODO, list can get bigger than 'howMany'/low priority fix
}
}
}
function linkRedirection301(replay_url, callback){
var downloader_cookies = 'sid=' + loginTwitter.cookie + ';';
GM_xmlhttpRequest({
method: 'GET',
url: replay_url,
headers: {
'User-Agent': 'Periscope/2699 (iPhone; iOS 8.1.2; Scale/2.00)',
'cookie': downloader_cookies
},
onload: function (res) {
replay_url = res.finalUrl;
if (replay_url){//not "not found"
callback(replay_url, downloader_cookies);
}
}
});
}
function saveDecryptionKey(_url, id, cookies, got_M3U_playlist, mainCallback){
got_M3U_playlist ? request(_url, getKey, 'arraybuffer') : request(_url, getKeyUri);
cookies ? '': cookies = '';
function request(url, callback, respType) {
GM_xmlhttpRequest({
responseType : respType,
method: 'GET',
url: url,
headers: {
Cookie: cookies
},
onload: function (response) {
if (response.status == 200) {
NODEJS ? callback(response.responseArray) : callback(response.responseText);
}else{
mainCallback? mainCallback(true):'';//tell function to not attach links because obtaining key has failed
}
}
})
}
function getKeyUri(m3u_text) { // extract key uri from playlist
NODEJS ? m3u_text = m3u_text.join('') : '';
var keyURI = m3u_text.split('\n').filter(function (line) {
return /(^#EXT-X-KEY:.+)/g.test(line);
});
if (!keyURI[0])// broadcast starts but has o chunks AND key uri on the playlist
return; //TODO retry after 10s or so
keyURI = keyURI[0].split('"')[1];
request(keyURI, getKey, 'arraybuffer');
}
function getKey(respKey){
NODEJS ? respKey = respKey[0] : '';
var base = new Uint8Array(respKey);
var base64key = btoa(String.fromCharCode.apply(null, base));
limitAddIDs(broadcastsWithLinks, id, 200, []);
broadcastsWithLinks.addToBroadcastsLinks(id,{decryptKey : base64key})
mainCallback? mainCallback():'';
}
}
function getBothURLs(id) {
var live_url = '';
var urlCallback = function (hls_url, replay_url, cookies, _name, _folder_name, _broadcast_info, _partial_replay) {
broadcastsCache.interestingList.indexOf(id) < 0 ? broadcastsCache.interestingList.push(id) : '';
if(broadcastsCache.interestingList.length > 100){
broadcastsCache.interestingList.shift();
}
if(hls_url){
live_url = hls_url;
getURL(id, urlCallback, true, true);
}else if(replay_url){
switchSection('Console', {url: live_url, rurl: replay_url, cookies: cookies, name: _name, folder_name: _folder_name, broadcast_info: _broadcast_info});
}else if(hls_url === null && replay_url === null && liveUrl) { //when live just started and no partial replay available
switchSection('Console', {url: live_url, rurl: '', cookies: cookies, name: _name, folder_name: _folder_name, broadcast_info: _broadcast_info});
}
}
getURL(id, urlCallback);
}
function getM3U(id, jcontainer) {
var liveLContainer = jcontainer.find('.responseLinks');
var replayLContainer = jcontainer.find('.responseLinksReplay');
liveLContainer.addClass('oldLinks');
replayLContainer.addClass('oldLinks');
broadcastsCache.interestingList.indexOf(id) < 0 ? broadcastsCache.interestingList.push(id) : '';
if(broadcastsCache.interestingList.length > 100){
broadcastsCache.interestingList.shift();
}
var urlCallback = function (hls_url, replay_url, cookies, _name, _folder_name, _broadcast_info, _partial_replay) {
!_partial_replay ? (liveLContainer.removeClass('oldLinks'), liveLContainer.children().length ? liveLContainer.empty() : '') : '';
(_partial_replay || replay_url) ? (replayLContainer.removeClass('oldLinks'), replayLContainer.children().length ? replayLContainer.empty() : '') : '';
var locked = _broadcast_info.is_locked;
limitAddIDs(broadcastsWithLinks, id, 200, []);
if (hls_url) {
var clipboardLink = $('
Copy URL ');
var clipboardDowLink = $('
NodeDown ');
var downloadLink = $('
Record ')
.click(switchSection.bind(null, 'Console', {url: hls_url, rurl: '', cookies: cookies, name: _name, folder_name: _folder_name, broadcast_info: _broadcast_info}));
liveLContainer.append(
settings.showM3Ulinks ? '
Live M3U link ' : '', settings.showM3Ulinks ? ' | ' : '',
NODEJS ? [downloadLink, ' | ' ]: '',
clipboardLink,
((!NODEJS && (settings.showNodeDownLinks || (settings.showNodeDownLinksPrv && _broadcast_info.is_locked))) ? [' | ' ,clipboardDowLink] : ''), '
'
);
new ClipboardJS(clipboardLink.get(0));
new ClipboardJS(clipboardDowLink.get(0));
var linksObj = {
m3uLink : $('
Live M3U link '),
downloadLink : downloadLink.clone(true,true),
clipboardLink : clipboardLink.clone(),
clipboardDowLink : clipboardDowLink.clone()
}
broadcastsWithLinks.addToBroadcastsLinks(id,linksObj)
settings.showPRlinks ? getURL(id, urlCallback, true) : '';
}else if (replay_url){
var replay_base_url = replay_url.replace(/([^\/]+)\.m3u8.+/ig, '');
GM_xmlhttpRequest({
method: 'GET',
url: replay_url,
headers: {
Cookie: cookies
},
onload: function (m3u_text) {
m3u_text = m3u_text.responseText.replace(/(^[^#][^\s].*)/gm, replay_base_url + '$1');
var link = $('
Download' + (_partial_replay ? ' PR ' : ' replay ' ) + 'M3U ').click(saveAs.bind(null, m3u_text, 'playlist.m3u8'));
var clipboardLink = $('
' + (_partial_replay ? 'Copy PR_URL' : 'Copy R_URL') + ' ');
var clipboardDowLink = $('
' + (_partial_replay ? 'PR_NodeDown' : 'R_NodeDown') + ' ');
var downloadLink = $('
' +(_partial_replay ? 'Download PR' : 'Download' ) + ' ')
.click(switchSection.bind(null, 'Console', {url: '', rurl: replay_url, cookies: cookies, name: _name, folder_name: _folder_name, broadcast_info: _broadcast_info}));
replayLContainer.append(
settings.showM3Ulinks ? [link, ' | '] : '',
NODEJS ? [downloadLink.clone(true,true), ' | '] : '',
clipboardLink,
((!NODEJS && (settings.showNodeDownLinks || (settings.showNodeDownLinksPrv && locked))) ? [' | ' ,clipboardDowLink] : ''), '
'
);
new ClipboardJS(clipboardLink.get(0));
new ClipboardJS(clipboardDowLink.get(0));
var repLinksObj = {
Rm3uLink : link,
RdownloadLink : downloadLink.clone(true,true),
RclipboardLink : clipboardLink.clone(),
RclipboardDowLink : clipboardDowLink.clone()
}
if(broadcastsWithLinks[id]){
$.extend(true, broadcastsWithLinks[id], repLinksObj);
}else{
broadcastsWithLinks[id] = repLinksObj;
}
if (locked && !broadcastsWithLinks[id].hasOwnProperty('decryptKey') && m3u_text){
var keyURI = m3u_text.split('\n').filter(function (line) {
return /(^#EXT-X-KEY:.+)/g.test(line);
});
keyURI = keyURI[0].split('"')[1];
saveDecryptionKey(keyURI, id, cookies, true);
}
}
});
}
}
getURL(id, urlCallback);
}
/**
* @callback getURLCallback
* @param {String} hls_url
* @param {String} replay_url
* @param {Array} cookies
* @param {String} name
* @param {String} user_id
* @param {String} user_name
* @param {Object} broadcast
* @param {Boolean} partialReplay
*
*/
/**
*
* @param {String} id - broadcast ID
* @param {getURLCallback} callback - function applied against result
*/
function getURL(id, callback, partialReplay, whole){
var getURLCallback = function (r) {
var privateBroadacast = r.broadcast.is_locked === true;
var producer = (r.broadcast_source === 'producer' || r.broadcast_source === 'livecms');
var name = userFolderFileName(settings.userFileName || DefaultFolderFileNames.fileName, r.broadcast, partialReplay, !!r.replay_url, producer, whole);
var folder_name = userFolderFileName(settings.userFolderName || DefaultFolderFileNames.folderName, r.broadcast, partialReplay, !!r.replay_url, producer, whole);
// var cookies = r.cookies;
var cookies = '';
privateBroadacast ? cookies = ('sid=' + loginTwitter.cookie + ';') : '';
// For live
var hls_url = r.hls_url || r.https_hls_url || r.lhls_url;
if (hls_url) {
callback(hls_url, null, cookies, name, folder_name, r.broadcast);
}
// For replay
var replay_url = r.replay_url
if(replay_url && !replay_url.endsWith('?type=replay') ){ // 301 redirection For private replay and some rare non private.
linkRedirection301(replay_url, ifReplay)
}else if (replay_url){
ifReplay(replay_url, '');
}
function ifReplay(replay_Url, cookies){
if (replay_Url) {
callback(null, replay_Url, cookies, name, folder_name, r.broadcast, partialReplay);
}
}
//for no live no replay(when requested partial replay does not exist)
if(!hls_url && !replay_url)
callback(null, null, cookies, name, folder_name, r.broadcast);
};
var ApiParameters ={
broadcast_id: id,
};
partialReplay ? ( ApiParameters.replay_redirect = false, ApiParameters.latest_replay_playlist = true) : '';
(function a(ApiParameters){//private replays correctly attach to their card.
PeriscopeWrapper.V2_POST_Api('accessVideoPublic', ApiParameters, getURLCallback, function(){
PeriscopeWrapper.V2_POST_Api('accessChannel', ApiParameters, getURLCallback) // private video case
});
})(ApiParameters)
}
DefaultFolderFileNames = {
partialShort: 'P',
replayShort: 'R_',
privateShort: 'PV_',
producerShort: 'PRO_',
folderName: '#{user_id} (#{username})',
fileName: '#{private}#{partial}#{replay}#{year}-#{month}-#{day}_#{hour}.#{minute}_#{user_display_name}_#{status}'
}
function userFolderFileName(userString, b_info, partialReplay, replay, producer, whole){
var date_created = new Date(b_info.start);
b_info.year = date_created.getFullYear();
b_info.month = zeros(date_created.getMonth() + 1);
b_info.day = zeros(date_created.getDate());
b_info.hour = zeros(date_created.getHours());
b_info.minute = zeros(date_created.getMinutes());
b_info.second = zeros(date_created.getSeconds());
(partialReplay && !whole) ? (b_info.partial = (settings.userPartialShort || DefaultFolderFileNames.partialShort)) : '';
(replay && !whole) ? (b_info.replay = (settings.userReplayShort || DefaultFolderFileNames.replayShort)) : '';
b_info.is_locked ? (b_info.private = (settings.userPrivateShort || DefaultFolderFileNames.privateShort)) : '';
producer ? (b_info.replay = (settings.userProducerShort || DefaultFolderFileNames.producerShort)) : '';
return userString.replace(/(\#|\$){[a-z_]+}/gi, function(param){
return (b_info[param.slice(2,-1)] !== undefined) ? (param = b_info[param.slice(2,-1)]) : '';
});
}
function download(folder_name ,name, url, rurl, cookies, broadcast_info, jcontainer, replayLimit) { // cookies=['key=val','key=val']
function _arrayBufferToString(buf, callback) {
var bb = new Blob([new Uint8Array(buf)]);
var f = new FileReader();
f.onload = function (e) {
callback(e.target.result);
};
f.readAsText(bb);
}
var windows = process.platform === 'win32',
folder_separator = windows ? '\\' : '/';
var output_dir = settings.downloadPath + folder_separator
if (folder_name)
output_dir += cleanFilename(folder_name) + folder_separator;
const fs = require('fs');
try {
fs.mkdirSync(output_dir);
} catch (e) {}
name = cleanFilename(name || 'untitled')
output_name_check(0);
var otherProcessHasName = function (nameToCheck) {
return childProcesses.some(function (child) {
return child.file_name === nameToCheck;
});
}
function output_name_check(num) {
fs.stat(output_dir + name + (num ? num : '') + '.ts', function (err, stats) {
if (stats || otherProcessHasName(name + (num ? num : ''))) {
output_name_check(num + 1);
} else {
num ? name = name + num : '';
var decryption_key;
if (broadcastsWithLinks[broadcast_info.id] && broadcastsWithLinks[broadcast_info.id].hasOwnProperty('decryptKey')){ // if broadcast has decryption key saved
decryption_key = broadcastsWithLinks[broadcast_info.id].decryptKey;
$('#download_key').val(decryption_key);
}
const spawn = require('child_process').spawn(process.execPath, [
'downloaderNode.js',
'-url', url,
'-rurl', rurl,
'-dir', output_dir,
'-name', name,
'-cookies', cookies,
'-key', decryption_key,
'-limit', replayLimit === true ? settings.replayTimeLimit : ''
],{
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
});
spawn.b_info = broadcast_info;
spawn.folder_path = output_dir;
spawn.file_name = name;
childProcesses.push(spawn);
if(childProcesses.length > 100){
childProcesses.shift()
}
$(document).find('.card.' + broadcast_info.id).find('.recContainer').empty().append(downloadStatus(broadcast_info.id, true));
if (jcontainer) {
if (!spawn.pid)
jcontainer.append('FFMpeg not found. On Windows, place the static build into OpenPeriscope directory.');
spawn.stdout.on('data', function (data) {
_arrayBufferToString(data, function (d) {
jcontainer.append(d);
});
});
spawn.stderr.on('data', function (data) {
_arrayBufferToString(data, function (d) {
jcontainer.append(d);
});
});
spawn.on('error', function (code) {
_arrayBufferToString(code, function (d) {
console.log('error: ', d);
});
});
// $(window).keydown(function(event){
// spawn.stdin.write(String.fromCharCode(event.keyCode)+'\r\n');
// //spawn.stdin.close();
// });
} else {
if (spawn.pid) {
ffLog = "";
function writeToLog(data) {
_arrayBufferToString(data, function (d) {
ffLog += d;
});
}
spawn.stdout.on('data', writeToLog);
spawn.stderr.on('data', writeToLog);
spawn.on('close', function (code, signal) {
ffLog+="\nClose: code "+code+", signal "+signal;
});
spawn.on('error', writeToLog.bind("disconnected"));
spawn.on('disconnect', writeToLog);
spawn.on('exit', function (code, signal) {
ffLog+="\nExit: code "+code+", signal "+signal;
});
}
}
return spawn;
}
});
}
}
function saveAs(data, filename) {
if (NODEJS) {
$('
')/* .change(function () {
const fs = require('fs');
fs.writeFile($(this).val(), data);
}).click(); */
}
}
function getFlag(country) {
var a = ['en','zh','ar','uk','ja','kk','da','da','he','ko','nb','sv'];//language code
var b = ['gb','cn','sa','ua','jp','kz','dk','dk','il','kr','no','se'];//country flag code
var langIndex = a.indexOf(country);
(langIndex >= 0) ? country = b[langIndex] : '';
var flagOffset = 127365;
var both = String.fromCodePoint(country.codePointAt(0) + flagOffset) + String.fromCodePoint(country.codePointAt(1) + flagOffset);
var output = emoji.replace_unified(both);
return (output === both) ? country : output;
};
function loadScreenPreviewer(stream, thumbs) {
var win = window.open("", "screenPreviewer" + (settings.previewSeparateWindows?stream.id:''), "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=800,height=550,top=100,left="+(screen.width/2));
var title = '
'+(stream.status || 'Untitled')+' [My-OpenPeriscope] ';
var html = ''
+'
Switch to screenlist
';
for (var i in thumbs.chunks) {
html+='
';
}
html+='';
win.document.body.innerHTML = '';
win.document.write(title,html);
}
function getDescription(stream) {
var title = emoji_to_img(stream.status || 'Untitled');
var featured_reason = '';
if (stream.featured) {
title += '
' + (stream.featured_category || 'POPULAR') + ' ';
if (stream.featured_reason)
featured_reason = '
'+stream.featured_reason+' ';
}
var date_created = new Date(stream.created_at);
var duration = stream.end || stream.timedout ? new Date(new Date(stream.end || stream.timedout) - (new Date(stream.start))) : 0;
var userLink = $('
' + emoji_to_img(stream.user_display_name) + ' (@' + stream.username + ') ');
userLink.click(switchSection.bind(null, 'User', stream.user_id));
if (stream.share_display_names) {
var sharedByLink = $('
'+ emoji_to_img(stream.share_display_names[0]) + ' ')
.click(switchSection.bind(null, 'User', stream.share_user_ids[0]));
}
if (stream.user_id == loginTwitter.user.id)
var deleteLink = $('
').click(function () {
PeriscopeWrapper.V2_POST_Api('deleteBroadcast', {broadcast_id: stream.id}, function (resp) {
if (resp.success)
description.parent().remove();
});
});
var screenlistLink = $('
Preview ').click(function () {
PeriscopeWrapper.V2_POST_Api('replayThumbnailPlaylist', {
broadcast_id: stream.id
}, function (thumbs) {
loadScreenPreviewer(stream, thumbs);
});
});
var brdcstImage = $('
').one('error',function(){this.src = stream.image_url});
var showImage = $('
').click(function () {
var win = window.open("", "screen", "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=600,height=600,top=100,left="+(screen.width/2));
win.document.head.innerHTML = '
'+(stream.status || 'Untitled')+' [My-OpenPeriscope] ';
win.document.body.innerHTML = '
';
}).append(brdcstImage, (stream.is_locked ? '
' : '')
+ ((stream.broadcast_source === 'producer' || stream.broadcast_source === 'livecms') ? '
Producer ': ''));
var watchingTitle=('
' + (stream.n_total_watching || stream.n_web_watching || stream.n_watching || stream.n_total_watched || 0) + '
\
' + title + ' '+featured_reason)
var chatLink = $('
Chat ').click(switchSection.bind(null, 'Chat', stream.id));
var description = $('
')
.append(showImage, watchingTitle, deleteLink, '
', screenlistLink, userLink, (sharedByLink ? [', shared by ', sharedByLink] : ''), (stream.channel_name ? ', on: ' + emoji_to_img(stream.channel_name) : ''), '
', chatLink,
'
' + zeros(date_created.getDate()) + '.' + zeros(date_created.getMonth() + 1) + '.' + date_created.getFullYear() + ' ' + zeros(date_created.getHours()) + ':' + zeros(date_created.getMinutes()) + ' '
+ (duration ? '
' + zeros(duration.getUTCHours()) + ':' + zeros(duration.getMinutes()) + ':' + zeros(duration.getSeconds()) + ' ' : '')
+ (stream.friend_chat ? '
' : '')
+ (stream.is_locked ? '
' : ''),
'
' + getFlag(stream.language) + ' ',
(stream.has_location ? $('
' + stream.country + ', ' + stream.city + ' ').click(switchSection.bind(null, 'Map', stream.ip_lat + ',' + stream.ip_lng)) : ''), '
');
return description[0];
}
function getUserDescription(user) {
user.profile_image_urls.sort(function (a, b) {
return a.width * a.height - b.width * b.height;
});
var verified_icon = user.is_twitter_verified ? '
' : '';
return $('
')
.append((user.profile_image_urls.length ? '
' : '
')
+ '
' + user.n_followers + '
'
+ (user.n_hearts ? '
' + user.n_hearts + '
' : '')
+ (user.twitter_screen_name ? '' : '')
+ '
')
.append($('
' + verified_icon + emoji_to_img(user.display_name) + ' (@' + user.username + ')
').click(switchSection.bind(null, 'User', user.id)))
.append('Created: ' + (new Date(user.created_at)).toLocaleString()
+ (user.description ? '
' + emoji_to_img(user.description) +'
': '
'))
.append($('
' + (user.is_following ? 'unfollow' : 'follow') + ' ').click(function () {
var el = this;
var selectButton=$(el).next().next()
PeriscopeWrapper.V2_POST_Api(el.innerHTML, { // follow or unfollow
user_id: user.id
}, function (r) {
if (r.success) {
if (el.innerHTML == 'follow') {
el.innerHTML = 'unfollow';
$(el).addClass('activated');
} else {
el.innerHTML = 'follow';
$(el).removeClass('activated');
selectButton.text() == '-' ? selectButton.click() : '';
}
}
})
}))
.append($('
' + (user.is_blocked ? 'unblock' : 'block') + ' ').click(function () {
var el = this;
PeriscopeWrapper.V2_POST_Api(el.innerHTML == 'block' ? 'block/add' : 'block/remove', {
to: user.id
}, function (r) {
if (r.success)
el.innerHTML = el.innerHTML == 'block' ? 'unblock' : 'block';
})
}))
.append(NODEJS ? ($('
' + (selectedDownloadList.includes(user.id) ? '-' : '+') + ' ').click(function () {
var el = this;
var followButton = $(el).prev().prev()
if (el.innerHTML == '+') {
el.innerHTML = '-';
$(el).addClass('activated');
followButton.text() == 'follow' ? followButton.click() : '';
} else {
el.innerHTML = '+';
$(el).removeClass('activated');
}
var isStoredAt = selectedDownloadList.indexOf(user.id)
if (isStoredAt === 0){
selectedDownloadList="";
localStorage.setItem(('selectedUsersDownloadList'), selectedDownloadList)
}else if (isStoredAt > 0) {
selectedDownloadList = selectedDownloadList.substr(0, isStoredAt - 1) + selectedDownloadList.substr(isStoredAt + user.id.length)
localStorage.setItem(('selectedUsersDownloadList'), selectedDownloadList)
} else {
selectedDownloadList += user.id + ','
localStorage.setItem(('selectedUsersDownloadList'), selectedDownloadList)
}
})) : '')
.append('
');
}
function dManagerDescription(jcontainer) {
jcontainer.html('
');
debug = $('#debug').length && $('#debug')[0].checked;
if (childProcesses.length) {
for (var i = childProcesses.length - 1; i >= 0; i--) {
(function () { //IIFE to keep each iteration
var CProcess = childProcesses[i];
var broadcastInfo = CProcess.b_info;
var filePath = CProcess.folder_path;
var brdcstImage = $('
').one('error',function(){this.src = broadcastInfo.profile_image_url, $(this).addClass('avatar')});
var dManager_username = $('
' + emoji_to_img(broadcastInfo.user_display_name || "undefined") + ' (@' + broadcastInfo.username + ') ').click(switchSection.bind(null, 'User', broadcastInfo.user_id));
var brdcstTitle = $('
' + emoji_to_img(broadcastInfo.status || CProcess.file_name) + ' ').click(function () {
require('fs').stat(filePath, function (err, stats) {
if (err) {} else {
if (process.platform === 'win32') {
require('child_process').exec('"' + filePath + CProcess.file_name + '.ts"', function () {}); //open video
} else {
require('child_process').exec('xdg-open ' + "'" + filePath + CProcess.file_name + ".ts'", function () {});//open video on linux
}
}
});
});
let stopButton = $(' Stop ').click(function () {
try {
CProcess.stdin.end('q', /* CProcess.kill */);
}catch(e){}
});
var openFolderButton = $('
folder ').click(function () {
var self = $(this);
require('fs').stat(filePath, function (err, stats) {
if (err) { // Directory doesn't exist or something.
if (err.code === 'ENOENT') {
self.html("doesn't exist");
} else {
self.html("error");
console.error(err);
}
} else {
gui.Shell.showItemInFolder(filePath + CProcess.file_name +'.ts');
}
});
});
var dManagerExitStatus = $('
').append(downloadStatus(broadcastInfo.id, false));
var dManagerMessages = $('' + (CProcess.lastMessage ? CProcess.lastMessage : '') + '
');
var dManagerTimer = $('' + (CProcess.lastUptime ? CProcess.lastUptime : '') + ' ');
CProcess.removeAllListeners('message', function () {}) //to avoid multiple listeners, +1 at each refresh
CProcess.on('message', function (msg) {
if (typeof msg === 'string') {
var msgPrefix = msg.split(' ')[0];
}
if ((msgPrefix === 'Error' || msgPrefix === 'Warning') || ((typeof msg) === 'object')) {
if (typeof CProcess.errorsLog === 'undefined') {
CProcess.errorsLog = [];
CProcess.errorsLog.push(msg);
} else {
CProcess.errorsLog.push(msg);
}
}
if (msgPrefix === 'Uptime:') {
dManagerTimer.html(msg);
CProcess.lastUptime = msg;
} else if (typeof msg === 'string') {
dManagerMessages.html(msg);
CProcess.lastMessage = msg; //preserve last message from spawned process between refreshes
}
});
let broadcast_id = broadcastInfo.id;
CProcess.on('exit', function () {
stopButton.remove();
dManagerExitStatus.empty().append(downloadStatus(broadcast_id, false));
$(document).find('.card.' + broadcast_id).not('.downloadCard, .cardProfileImg').find('.recContainer').empty().append(downloadStatus(broadcast_id, true));
});
var messagesContainer = $('
').append(dManagerExitStatus, dManagerTimer, dManagerMessages);
var errButton = $('Show errors ').click(function () {
console.log(CProcess.errorsLog);
});
var downloadCard = $('
').append( $('
').append((CProcess.exitCode === null ? stopButton : ''), openFolderButton,
((CProcess.errorsLog && debug) ? errButton : ''), brdcstImage, brdcstTitle, ' ', dManager_username, ' ', messagesContainer
));
jcontainer.append(downloadCard);
})()
}
var downloadsCount = $('').append(function () {
var addedDownloads = childProcesses.filter(function (x) {
return x.exitCode === null;
}).length;
return addedDownloads + ' in progress, ' + (childProcesses.length - addedDownloads) + ' finished';
});
jcontainer.prepend(' ', downloadsCount);
} else
jcontainer.append('No results');
return jcontainer;
}
function downloadStatus(broadcast_id, link){
cpIndex = childProcesses.findIndex(function(cProcess) {return cProcess.b_info.id === broadcast_id;});
if (cpIndex >= 0){
let title = 'Recording/Downloading';
let emote = '🔴';
let eCode = childProcesses[cpIndex].exitCode;
if (link) {childProcesses.some(function(cProcess){ return (cProcess.b_info.id === broadcast_id && cProcess.exitCode === null)}) ? (eCode = null) : ''}; //if any process still downloading then show as downloading.
if (eCode === 0) title = 'Downloaded', emote = '✅';
if (eCode === 1) title = 'Stopped', emote = '❎';
if (eCode > 1) title = 'error', emote = '❌';
if(link){
let recLink = [$('' + emoji_to_img(emote) + ' ').click(
switchSection.bind(null,'Dmanager', broadcast_id)
), ' | '];
return recLink;
}else{
let exitEmote = '' + emoji_to_img(emote) + ' ';
return exitEmote;
}
};
return '';
}
function emoji_to_img(textInput){
if(ifEmoji('🐸')){
return textInput;
} else{
return emoji.replace_unified(textInput)// for browsers/systems without emojis support
}
}
function setSet(key, value) {
settings[key] = value;
localStorage.setItem('settings', JSON.stringify(settings));
}
function clearXHR() { // abort all running XHR requests
for (var i in XHR) {
Progress.stop();
XHR[i].abort();
}
XHR=[];
}
/* LEVEL 0 */
var XHR = [];
function SignIn3(session_key, session_secret) {
PeriscopeWrapper.V2_POST_Api('loginTwitter', {
"session_key": session_key,
"session_secret": session_secret
}, function (response) {
localStorage.setItem('loginTwitter', JSON.stringify(response));
loginTwitter = response;
Ready(loginTwitter);
if (!loginTwitter.user.username) // User registration
PeriscopeWrapper.V2_POST_Api('verifyUsername', {
username: loginTwitter.suggested_username,
display_name: loginTwitter.user.display_name
}, function (verified) {
if (verified.success) {
loginTwitter.user = verified.user;
localStorage.setItem('loginTwitter', JSON.stringify(loginTwitter));
} else
console.log('User verification failed!', verified);
});
})
}
function SignIn2(oauth_token, oauth_verifier) {
OAuthTwitter('access_token', function (oauth) {
localStorage.setItem('session_key', oauth.oauth_token);
localStorage.setItem('session_secret', oauth.oauth_token_secret);
session_key = oauth.oauth_token;
session_secret = oauth.oauth_token_secret;
SignIn3(session_key, session_secret);
}, {oauth_token: oauth_token, oauth_verifier: oauth_verifier});
}
function SignIn1() {
setSet('consumer_secret', $('#secret').val());
if (settings.consumer_secret) {
$(this).text('Loading...');
OAuthTwitter('request_token', function (oauth) {
location.href = 'https://api.twitter.com/oauth/authorize?oauth_token=' + oauth.oauth_token;
}, {oauth_callback: 'twittersdk://openperiscope/index.html'});
}
}
function SignOut() {
localStorage.clear();
setSet();
location.pathname = 'index.html';
}
function OAuthTwitter(endpoint, callback, extra){
OAuth('https://api.twitter.com/oauth/' + endpoint, 'POST', callback,extra);
}
function OAuth(endpoint, _method, callback, extra) {
var method = _method || 'POST';
var url = endpoint;
var params = {
oauth_consumer_key: '9I4iINIyd0R01qEPEwT9IC6RE',
oauth_nonce: Date.now(),
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: Date.now() / 1000 | 0,
oauth_version: '1.0'
};
for (var i in extra)
params[i] = extra[i];
var signatureBase = [];
var keys = Object.keys(params).sort();
for (i in keys)
signatureBase.push(keys[i] + '=' + params[keys[i]]);
var signatureBaseString = method + '&' + encodeURIComponent(url) + '&' + encodeURIComponent(signatureBase.join('&'));
params.oauth_signature = encodeURIComponent(
CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA1(signatureBaseString, settings.consumer_secret + '&' + (session_secret || ''))
)
);
var params_prepared = [];
for (i in params) {
params_prepared.push(i + '="' + params[i] + '"');
}
GM_xmlhttpRequest({
method: method,
url: url,
headers: {
Authorization: 'OAuth ' + params_prepared.join(', ')
},
onload: function (r) {
if (r.status == 200) {
var oauth = {};
var response = r.responseText.split('&');
for (var i in response) {
var kv = response[i].split('=');
oauth[kv[0]] = kv[1];
}
callback(oauth);
}
else if (r.status == 401) { // old tokens: reload page
console.log('oauth error 401: ' + r.responseText);
SignOut();
}
else
console.log('oauth error: ' + r.status + ' ' + r.responseText);
}
});
}
function SignInSMS() {
setSet('consumer_secret', $('#secret').val());
if (settings.consumer_secret) {
OAuthDigits('oauth2/token', {
form: {
grant_type: 'client_credentials'
},
token_type: 'Basic',
access_token: btoa('9I4iINIyd0R01qEPEwT9IC6RE:' + settings.consumer_secret)
}, function (response_token) {
OAuthDigits('1.1/guest/activate.json', {
token_type: response_token.token_type,
access_token: response_token.access_token
}, function (response_activate) {
var phone = $(' ');
$(document.body).append(' ', phone, $('Send SMS ').click(function () {
OAuthDigits('1/sdk/login', {
form: {
x_auth_phone_number: phone.val(),
verification_type: 'sms'
},
guest: response_activate.guest_token,
token_type: response_token.token_type,
access_token: response_token.access_token
}, function (response_login) {
var code = $(' ');
$(document.body).append(' ', code, $('Check code ').click(function () {
OAuthDigits('auth/1/xauth_challenge.json', {
form: {
login_verification_request_id: response_login.login_verification_request_id,
login_verification_user_id: response_login.login_verification_user_id,
login_verification_challenge_response: code.val()
},
guest: response_activate.guest_token,
token_type: response_token.token_type,
access_token: response_token.access_token
}, function (response_xauth) {
localStorage.setItem('session_key', response_xauth.oauth_token);
localStorage.setItem('session_secret', response_xauth.oauth_token_secret);
session_key = response_xauth.oauth_token;
session_secret = response_xauth.oauth_token_secret;
SignIn3(session_key, session_secret);
});
}));
});
}));
});
});
}
}
function OAuthDigits(endpoint, options, callback) {
Progress.start();
var args = {
method: 'POST',
url: 'https://api.digits.com/' + endpoint,
headers: {
'Authorization': options.token_type + ' ' + options.access_token
},
onload: function (r) {
Progress.stop();
if (r.status == 200)
callback(JSON.parse(r.responseText.replace(/"login_verification_user_id":(\d+)/, '"login_verification_user_id":"$1"'))); // fix for integral precision in JS
else if (r.status == 401 || r.status == 400) // wrong sms code
alert('Authorization error!');
}
};
if (options.guest) {
args.headers['x-guest-token'] = options.guest;
}
if (options.form) {
args.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
args.data = '';
for (var i in options.form)
args.data += i + '=' + encodeURIComponent(options.form[i]) + '&';
args.data = args.data.substr(0, args.data.length - 1);
}
GM_xmlhttpRequest(args);
}
function SignInSessionID()
{
setSet('session_cookie', $('#secret').val());
if (settings.session_cookie)
{
PeriscopeWrapper.V2_POST_Api('user', { cookie: settings.session_cookie },
function (userResponse)
{
loginTwitter = localStorage.getItem('loginTwitter');
if (!loginTwitter)
loginTwitter = {cookie: settings.session_cookie, user: userResponse.user, suggested_username: '', settings: {} };
loginTwitter.user = userResponse.user;
loginTwitter.cookie = settings.session_cookie;
localStorage.setItem('loginTwitter', JSON.stringify(loginTwitter));
loginTwitter.user.profile_image_urls.sort(function (a, b) {
return a.width * a.height - b.width * b.height;
});
PeriscopeWrapper.V2_POST_Api('getSettings', {},
function (settingsResponse)
{
loginTwitter.settings = settingsResponse;
localStorage.setItem('loginTwitter', JSON.stringify(loginTwitter));
Ready(loginTwitter);
}
)
}
)
}
}