Skip to content

Instantly share code, notes, and snippets.

@Yengas
Last active April 5, 2022 01:23
Show Gist options
  • Save Yengas/ef6f6d09eadda009ecbb to your computer and use it in GitHub Desktop.
Save Yengas/ef6f6d09eadda009ecbb to your computer and use it in GitHub Desktop.
Continous watching, Localstorage features(Where you left an episode), Downloading and Streaming functionality to Kissanime
// ==/UserScript==
// ==UserScript==
// @name Premium Kissanime
// @version 0.5
// @description Tampermonkey script for better kissanime usage.
// @include /https?://(www\.|)kissanime\.(to|org|me|com)/*/
// @copyright 2012+, Yengas
// ==/UserScript==
// Disable Adblock Detection
if(typeof DoDetect2 != 'undefined') DoDetect2 = null;
// Hide Left/Right floats + floating hide links that adblock doesnt remove
$(".divCloseBut a").click();
$("#divFloatLeft").remove();
$("#divFloatRight").remove();
$("a:contains('Click here to download all')").remove();
// Global Functions
String.format = function() {
var s = arguments[0];
for (var i = 0; i < arguments.length - 1; i++) {
var reg = new RegExp("\\{" + i + "\\}", "gm");
s = s.replace(reg, arguments[i + 1]);
}
return s;
}
String.prototype.htmlEncode = function(){
return this.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
};
String.prototype.sanitize = function(){
return this.htmlEncode().replace(/(\r\n|\n|\r)/gm, "").trim();
};
// Download all feature
var download_links = {}, titles = {}, downloadButton = document.createElement('div');
function getByQuality(quality){
var returnArray = [], titleArray = [];
if( quality in download_links ){
var sorted = Object.keys(download_links[quality]).sort(function(x, y) { return x - y; });
sorted.forEach(function(key){
returnArray.push(download_links[quality][key]);
titleArray.push(titles[key]);
});
}
return [returnArray, titleArray];
}
function downloadByQuality(quality){
getByQuality(quality)[0].forEach(function(url){
$("body").append('<iframe width="1" height="1" frameborder="0" src="' + url + '"></iframe>');
});
}
function downloadLinksToHTML(d){
return $(d).find("#divDownload")[0].innerHTML;
}
downloadButton.innerHTML = "<button>Download all</button>";
$(".listing").before(downloadButton);
$(downloadButton).find("button").on('click', function(e){
e.preventDefault();
var downloader = $(this).parent().html('<span class="bigChar">Downloader</span><br />Quality: <select id="quality"></select><br />Total: <span id="count">0</span><br /><input type="button" id="playlist" value="XSPF Playlist" /><input type="button" id="iframe" value="Iframe All" /><input type="button" id="listButton" value="Get List of URLs"><br /><textarea id="list" wrap="off" style="display:none;resize:none;width:300px;height:200px;"></textarea>');
var select = downloader.find("select"), count = downloader.find('#count'), list = downloader.find("#list");
var playlistStrings = { main: '<?xml version="1.0" encoding="UTF-8"?><playlist xmlns="http://xspf.org/ns/0/" xmlns:vlc="http://www.videolan.org/vlc/playlist/ns/0/" version="1"><title>{0}</title><trackList>{1}</trackList><extension application="http://www.videolan.org/vlc/playlist/0">{2}</extension></playlist>', track: '<track><location>{0}</location><title>{1}</title><extension application="http://www.videolan.org/vlc/playlist/0"><vlc:id>{2}</vlc:id><vlc:option>network-caching=1000</vlc:option></extension></track>', extension: '<vlc:item tid="{0}"/>' }
var anime_title = $("a.bigChar").text().sanitize();
function createPlaylist(){
var id = 0, trackList = [], extensionList = [], quality = select.val(), sortedEpisodes = Object.keys(download_links[quality]).sort(function(x, y) { return x - y; });
sortedEpisodes.forEach(function(key){
var title = titles[key].sanitize(), url = download_links[quality][key];
trackList.push(String.format(playlistStrings.track, url.htmlEncode(), title, id));
extensionList.push(String.format(playlistStrings.extension, id++));
});
return new Blob([String.format(playlistStrings.main, anime_title, trackList.join(''), extensionList.join(''))], { type: 'application/octet-stream' });
}
downloader.find("#iframe").on('click', function(){ downloadByQuality(select.val()); });
downloader.find("#listButton").on('click', function(){
var text = "";
list.show();
var episodes = getByQuality(select.val());
for(var key in episodes[0]){
text += episodes[0][key] + "&=" + encodeURIComponent(episodes[1][key]) + "\r\n";
}
list.val(text);
});
downloader.find("#playlist").on('click', function(){
var a = document.createElement('a');
a.className = "bigChar";
a.href = window.URL.createObjectURL(createPlaylist());
a.download = anime_title.replace(/[\/:*?"<>|]+/g, "") + '.xspf';
a.textContent = 'Download file!';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
select.change(function(){ count.text( getByQuality(select.val())[0].length ); list.hide(); });
function updateDownloader(){
var keys = Object.keys(download_links);
select.find('option').each(function(){ var index = keys.indexOf(this.innerText); if(index > -1) keys.splice(index, 1); });
keys.forEach(function(op){ select.append('<option value="' + op + '">' + op + '</option>'); });
select.trigger('change');
}
var episodeLinks = $('table.listing a');
episodeLinks.each(function(){
var link = $(this), href = this.href;
function request_page(){
$.get(href, function(data){
if(data.includes("Are You Human")){
return window.setTimeout(function(){ request_page(); }, 1000);
}
var d = document.createElement('div');
d.innerHTML = data;
d.innerHTML = downloadLinksToHTML(d);
var links = $(d).find('a');
console.log(links);
if(links.length > 0){
var episode = episodeLinks.length - episodeLinks.index(link);
titles[episode] = link[0].innerText;
link.after("<br />" + $.map(links, function(l){
if(!(l.innerText in download_links)){ download_links[l.innerText] = {}; }
download_links[l.innerText][episode] = l.href;
return "--> " + l.outerHTML;
}).join("<br />"))
}
updateDownloader();
});
}
request_page();
});
});
// Skip to next when buffered and time save feature here
var splitted = location.href.split("Anime/"), nextEpisode = $("img[title='Next episode']").parent(), animeName = splitted.length < 2 ? undefined : (splitted[1].split('/'))[0];
var last = localStorage[animeName + "-last"], current = new Date().getTime(), limit = 30 * 1000, goNext = true, paused = false;
if(last == null){
localStorage[animeName + "-last"] = last = 0;
}else{
last = window.parseInt(last);
}
var Player = function(){};
Player.prototype.getSelected = function(){
var el = document.querySelector("#selectPlayer");
return (el == null || el.offsetParent === null) ? "html5" : el.value/* == "flash" ? "html5" : "flash"*/;
};
Player.prototype.video = function(){
var selected = this.getSelected();
if(Player[selected] != null) return Player[selected];
var video = selected == "html5" ? videojs("my_video_1_html5_api") : /*jwplayer()*/document.querySelector("#embedVideo");
video.type = selected;
return Player[selected] = video;
};
Player.prototype.getBuffered = function(){
var video = this.video();
if(video == null || video.type == null) return 0;
else if(video.type == "html5"){
var end = this.video().buffered().end( this.video().buffered().length - 1 ), duration = this.video().duration();
return end > duration ? 1 : end / duration;
}else if(video.type == "flash"){
//return this.video().getBuffer() / 100.0;
return this.video().getVideoLoadedFraction();
}else{
return 0;
}
};
Player.prototype.getCurrentTime = function(){
var video = this.video();
if(video == null || video.type == null) return 0;
else if(video.type == "html5"){
return this.video().currentTime();
}else if(video.type == "flash"){
//return this.video().getPosition();
return this.video().getCurrentTime();
}else{
return 0;
}
};
Player.prototype.pause = function(state){
var video = this.video();
if(video == null || video.type == null) return;
else if(video.type == "html5"){
this.video().pause();
}else if(video.type == "flash"){
//this.video().pause(state == null ? true : state);
this.video().pauseVideo();
}else{
return;
}
};
Player.prototype.paused = function(){
var video = this.video();
if(video == null || video.type == null) return 0;
else if(video.type == "html5"){
return this.video().paused();
}else if(video.type == "flash"){
//return this.video().getState() == "PAUSED";
return this.video().getPlayerState() == 2;
}else{
return ;
}
};
Player.prototype.seek = function(point){
var video = this.video();
if(video == null || video.type == null) return 0;
else if(video.type == "html5"){
this.video().currentTime(point);
}else if(video.type == "flash"){
//this.video().seek(point);
this.video().seekTo(point, true);
}else{
return ;
}
};
Player.prototype.getDuration = function(){
var video = this.video();
if(video == null || video.type == null) return 0;
else if(video.type == "html5"){
return this.video().duration();
}else if(video.type == "flash"){
return this.video().getDuration();
}else{
return -1;
}
};
Player.prototype.getBufferedPercent = function(){
var video = this.video();
if(video == null || video.type == null) return 0;
else if(video.type == "html5"){
return this.video().bufferedPercent();
}else if(video.type == "flash"){
return this.getBuffered();
}else{
return 0;
}
};
Player.prototype.ready = function(){
var video = this.video();
if(video == null || video.type == null) return false;
if(video.type == "html5")
return true;
else if(video.type == "flash"){
//return true;
return video.seekTo != undefined;
}
return false;
};
var player = new Player();
function saveInterval(){
var time = player.getCurrentTime();
if(time != null && time > 0){
localStorage[splitted[1]] = time;
if(!goNext){ if(!paused){ player.pause(); paused = true; } goNext = !player.paused(); }
if(goNext && nextEpisode.length > 0 && (player.getBufferedPercent() >= 0.90 || ( player.getBuffered() >= 0.90 && player.getCurrentTime() > player.getDuration() * 9 / 10 ))){
localStorage[animeName + "-last"] = new Date().getTime();
nextEpisode.attr('target', '_blank');
nextEpisode[0].click();
nextEpisode.attr('target', '_self');
nextEpisode = [];
}
}
}
if(splitted.length > 1){
if(localStorage[splitted[1]] != null){
var setInterval = window.setInterval(function(){
var video = player.video();
if(player.ready()){
var save = parseFloat(localStorage[splitted[1]]), selected = player.getSelected();
window.clearInterval(setInterval);
/*if(selected != "flash"){
$("#selectPlayer").val("flash").change();
}*/
/*if(save >= 15 && selected != "html5"){
$("#selectPlayer").val("html5").change();
}else if((save == NaN || save < 15) && selected != "flash"){
$("#selectPlayer").val("flash").change();
}*/
player.seek(save, true);
if(current - last < limit){ player.pause(); goNext = false; }
window.setInterval(saveInterval, 5000);
}
}, 3000);
}else{
/*if(player.getSelected() != "flash"){
$("#selectPlayer").val("flash").change();
}*/
goNext = current - last >= limit;
window.setInterval(saveInterval, 5000);
}
}
// Show where we left off...
$(".listing td a").each(function(){
var key = this.href.split("Anime/")[1];
var time = parseFloat(localStorage[key]) || 0;
if(time > 10){
var mins = Math.floor(time / 60), seconds = Math.floor(time % 60);
this.innerHTML += " [" + mins + ":" + seconds + "]";
}
});
#/usr/bin/bash
containsElement () {
local e
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
urldecode() {
# urldecode <string>
local url_encoded="${1//+/ }"
printf '%b' "${url_encoded//%/\x}"
}
url_regex='(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]'
working_directory=${PWD##*/}
any_url=false
stream_mode=false
arguments=()
for arg in $*; do
if [ $arg == "--stream" ]; then
stream_mode=true
else
arguments+=($arg)
fi
done
if read -t 0; then
content=$(</dev/stdin)
echo "Using STDIN as the source."
elif [ ${#arguments[@]} -gt 0 ]; then
content=$(cat ${arguments[@]})
echo "Using given file(s) as the source."
else
content=$(xclip -o)
echo "Using clipboard as the source."
fi
if [ -z "$content" ]; then echo "Source was empty."; exit; fi
echo "Source length:" ${#content} chars
if [ "$stream_mode" = true ]; then echo "Stream mode"; fi
printf '%s\n\n\n' "Running on: $working_directory"
for url in $content; do
if [[ ! $url =~ $url_regex ]] || [[ ! "$url" =~ "&=" ]]; then continue; fi
any_url=true
split=$(echo $url | sed 's/\&\=/\r\n/')
title=$(echo $url | awk -F "&=" '{print $2}' | sed "s@+@ @g;s@%@\\\\x@g" | xargs -0 printf "%b")
url=$(echo $url | awk -F "&=" '{print $1}')
echo "title is: $title, url is: $url"
if [ "$stream_mode" = false ]; then
echo "Downloading " $title
aria2c -V -c -x 16 -s 10 -o "$title" "$url"
else
echo "Starting to stream " $title "on vlc."
vlc -vvv "$url" --play-and-exit --no-embedded-video --video-title "$title" > /dev/null 2>&1
fi
done
if [ "$any_url" = false ]; then echo "Couldn't find any valid url in the given source."; fi

Kissanime

This gist is a result of my practice with tampermonkey and bash scripting.

JS Script

JS file should be added to tampermonkey or greasemonkey as a script. This file contains anti adblock detection for kissanime, clicks all the hide buttons, removes left and right floating divs and adds functionality to 'Click here to download all' link. Using this link and the bash file in this gist, you can download/stream anime from kissanime sequentally and easily.

Features

  • No Ads (This is why i haven't published this gist anywhere on the internet. This is just a personal customization.)
  • Uses localstorage to store how long have you watched an episode so you can easily see where you've left an anime series and uses this information to automatically seek video player to where you have left an episode.
  • Opens up the next episode of an anime series in a new tab if the current episode is buffered 90%+ and pauses the newly opened video player. Max 2 tabs are opened at the same time. So if you afk or forget that you have kissanime opened, the script wont open every episode of the series. When you resume the second tab(probably after you've finished with the first tab), only then the 3 tab will be opened and this goes on until there are no episodes left.
  • Click Here To Download All button activated. You can download XSRF playlist with oneclick or get a list of all the download urls or iframe all the links and hope internet download manager will catch them.

Bash Script

Copy bash script to /usr/local/bin/kissanime and make it executable. Bash script given a source(clipboard by default, stdin, arguments) downloads or streams the urls copied from the functionality added by the js file. Bash script depends on xclip for clipboard reading, curl for downloading and vlc for streaming. Below are some examples of usage.

Examples of Using the Bash Script

Starts downloading the contents of my clipboard

kissanime

Concatenates the contents of the special_a.list and clannad.list and uses them as the source.

kissanime special_a.list clannad.list

Starts streaming from the 5. url(episode) in my clipboard

xclip -o | tail --lines=+5 | kissanime --stream
@hammadshifa97
Copy link

still working ok kissanime

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment