phibao37 / MovieResumer

// ==UserScript==
// @name         MovieResumer
// @version      2.7
// @description  Remember the last position when playing video
// @author       phibao37
// @match        *.phimmoi.net/*
// @match        *.dongphim.tv/*
// @match        http*://bilutv.org/*
// @match        http://tvhay.org/*
// @match        http://apitvh.net/*
// @match        https://fptplay.vn/*
// @match        https://motphim.co/*
// @match        https://playhydrax.com/*
// @grant   GM_getValue
// @grant   GM_setValue
// @grant   GM_addValueChangeListener
// @grant   GM_getTab
// @grant   GM_saveTab
// @grant   GM_deleteValue
// @grant   GM_listValues
// @run-at  document-idle
// @copyright 2020, phibao37 (https://openuserjs.org/users/phibao37)
// @license MIT
// ==/UserScript==
/* globals $ */
/* globals jwplayer */
/* globals videojs */
/* globals CPlayer */

(function() {
    'use strict';
    var INTERVAL_SYNC = 1000;
    var MOVIE_FINISHED_REMAIN_TIME = 180;

    var current_time = ['.jw-text-elapsed', '.vjs-current-time-display', '.cplayer-current-time-display'];
    var total_time = ['.jw-text-duration', '.vjs-duration-display', '.cplayer-duration-display'];
    var SeekVideo = function(value) {
        if (typeof jwplayer !== 'undefined'){
            jwplayer().seek(value);
        }
        else if (typeof videojs !== 'undefined'){
            var id = $('video').attr('id');
            videojs(id).currentTime(value);
        }

        else if (typeof CPlayer !== 'undefined'){
            CPlayer($('video').attr('id')).currentTime(value);
        }
    }
    var FindElementFromArray = function(vars, arr, use_old_value){
        if (use_old_value && vars != null) return vars;
        for (var i = 0; i < arr.length; i++){
            var find = $(arr[i]);
            if (find.length > 0){
                return find;
            }
        }
        return null;
    }
    var $current_time = null;
    var $total_time = null;

    function GM_onMessage(label, callback) {
        GM_addValueChangeListener(label, function() {
            callback.apply(undefined, arguments[2]);
        });
}

    function dragElement(elmnt, header,callback) {
  var dif_x = 0, dif_y = 0, cur_x = 0, cur_y = 0;
        var max_x = 0, max_y = 0;
  if (header) {
    // if present, the header is where you move the DIV from:
    header.onmousedown = dragMouseDown;
  } else {
    // otherwise, move the DIV from anywhere inside the DIV:
    elmnt.onmousedown = dragMouseDown;
  }

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    dif_x = e.clientX - elmnt.offsetLeft;
    dif_y = e.clientY - elmnt.offsetTop;
      max_x = Math.min(window.innerWidth,document.body.clientWidth) - elmnt.offsetWidth;
      max_y = window.innerHeight - elmnt.offsetHeight;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
      cur_x = e.clientX - dif_x;
      cur_y = e.clientY - dif_y;
      cur_x = Math.max(Math.min(cur_x, max_x), 0);
      cur_y = Math.max(Math.min(cur_y, max_y), 0);

    // set the element's new position:
    elmnt.style.top = cur_y + "px";
    elmnt.style.left = cur_x + "px";
      elmnt.style.right = "";
      elmnt.style.bottom = "";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
      if (callback && elmnt.style.left != "" && elmnt.style.top != ""){
          callback(elmnt, elmnt.style.left, elmnt.style.top);
      }
  }
}

    function GM_sendMessage(label) {
        GM_setValue(label, Array.from(arguments).slice(1));
    }
    var ParseTime = function(str){
        var arr = str.split(':');
        try {
        if (arr.length == 3){
            return parseInt(arr[0]) * 3600 + parseInt(arr[1]) * 60 + parseInt(arr[2]);
        } else {
            return parseInt(arr[0]) * 60 + parseInt(arr[1]);
        }
        } catch (e) {
            return -1;
        }
    }
    var SaveValue = function(key, current, total){
        if (current == "00:00" && total == "00:00"){
            return false;
        }
        if (ParseTime(current) + MOVIE_FINISHED_REMAIN_TIME >= ParseTime(total)){
            GM_deleteValue(key);
            return false;
        }
        GM_setValue(key, {'timestamp':new Date().getTime(),'title':document.title,'time':current, 'total':total});
        return true;
    }

    var Execute = function(){
    var internal_id = new Date().getTime();
    if (self === top){

        console.log('MovieResumer start');

        GM_getTab(function(tab){
            tab.internal_id = internal_id;
            GM_saveTab(tab);
        });
        $(window).on("beforeunload", function() {
            GM_deleteValue(internal_id);
            GM_deleteValue(internal_id + "_frame");
        })

        $('<div id="movie-resumer-base" style="display:none;border-width:3px;background-color:#cae8ca;border-style:solid;position:fixed;padding:7px;bottom:10px;right:10px;width:450px;height;z-index:999;border-color:#4CAF50"><div style="color:green;font-weight:bold;cursor:default;height:25px" id="movie-resumer-title">MovieResumer Manager <button type="button" class="btn btn-red btn-danger btn-xs" style="float:right;padding: 1px 5px;font-size: 12px;line-height: 1.5;border-radius: 3px;" id="movie-resumer-base-close">X</button></div><div style="height:350px;overflow:auto;background-color:white"><table style="width:100%;height:auto" id="movie-resumer-list" class="table table-striped table-hover"><col width="40px"/><col/></table></div><div style="padding-left:8px"><input type="checkbox" id="movie-resumer-select-all" /><label for="movie-resumer-select-all" style="font-weight:normal">&nbsp;Select All</label>&nbsp;<button type="button" class="btn btn-link" id="movie-resumer-delete-selected">Delete</button></div><div style="color:red;text-align:center;padding-top:5px">Copyright @ Sam Duc Vu</div></div>').appendTo(document.body);

        $('<div id="movie-resumer-small" style="border-width:3px;background-color:#cae8ca;border-style:solid;position:fixed;padding:7px;bottom:10px;right:10px;width:200px;z-index:999;border-color:#4CAF50"><table style="width:100%;display:none" id="movie-resumer-progress"><col width="70%"/><col width="30%"/><tr style="color:blue"><td style="cursor:pointer" title="Click to seek to last play time" id="movie-resumer-last-play-label">Last play time:</td><td id="movie-resumer-last-play">--:--</td></tr><tr style="color:red"><td style="cursor:pointer" title="Click to stop recording current play time"  id="movie-resumer-current-play-label">Current play time:</td><td id="movie-resumer-current-play">--:--</td></tr></table><table style="width:100%;margin-top:10px;margin-bottom:5px"><col width="50%"/><col width="50%"/><tr><td align="center"><button type="button" class="btn btn-primary btn-sm btn-small" id="movie-resumer-manage">Manage</button></td><td align="center"><button type="button" class="btn btn-red btn-danger btn-sm btn-small" id="movie-resumer-close">Close [`]</button></td></tr></table></div>').appendTo(document.body);
        var eBase = $('#movie-resumer-base')[0];
        var eSmall = $('#movie-resumer-small')[0];
        dragElement(eBase, $('#movie-resumer-title')[0], function(e,x,y){
            GM_setValue('base_location', {'x':x, 'y':y});
        });
        dragElement(eSmall, null, function(e,x,y){
            GM_setValue('small_location', {'x':x, 'y':y});
        });
        var base_location = GM_getValue('base_location');
        var small_location = GM_getValue('small_location');
        if (base_location){
            eBase.style.left = base_location.x;
            eBase.style.top = base_location.y;
            eBase.style.right = "";
            eBase.style.bottom = "";
        }
        if (small_location){
            eSmall.style.left = small_location.x;
            eSmall.style.top = small_location.y;
            eSmall.style.right = "";
            eSmall.style.bottom = "";
        }

        var $sync_last_time = $('#movie-resumer-last-play');
        var $sync_current_time = $('#movie-resumer-current-play');
        var is_enable_sync = true;
        var is_direct_sync = true;

        $('#movie-resumer-current-play-label').click(function(){
            is_enable_sync = false;
            $('#movie-resumer-progress').slideUp();
            GM_deleteValue(document.location.href);
        });
        var iframe_index = 0;
        $('#movie-resumer-last-play-label').click(function(){
            var value = ParseTime($('#movie-resumer-last-play').html());
            if (value <= 0) return;

            if (is_direct_sync){
                SeekVideo(value);
            } else {
                GM_sendMessage(internal_id + "_frame", iframe_index++, value);
            }
        });

        $('#movie-resumer-manage').click(function(){
            $('#movie-resumer-base').show();
            $('#movie-resumer-small').hide();

            $('#movie-resumer-list tr').remove();
            var list = GM_listValues();
            window.list_entries = [];
            var i, value;
            for (i = 0; i < list.length; i++){
                if (list[i].startsWith('http')){
                    value = GM_getValue(list[i]);
                    value.url = list[i];
                    window.list_entries.push(value);
                }
            }
            window.list_entries.sort(function(a,b){
                return a.timestamp > b.timestamp ? -1 : 1;
            });

            for (i = 0; i < window.list_entries.length; i++){
                value = window.list_entries[i];
                var url = new URL(value.url);
                $('<tr><td><input type="checkbox" value="'+i+'" id="movie-resumer-item-'+i+'" /><label for="movie-resumer-item-'+i+'">&nbsp;</label></td><td><a style="color:blue;display:block" href="' + value.url + '">' + value.title + '</a><div style="text-align:left;cursor:default"><span style="color:red;font-weight:bold">'+value.time+'</span> / '+value.total+' - <a href="'+url.origin+'" style="color:orange;font-weight:bold">' + url.hostname + '</a><button type="button" class="btn btn-link" style="float:right;padding: 1px 2px;font-size: 12px;line-height: 1.5;">Delete</button></div></td></tr>').appendTo('#movie-resumer-list');
            }

            $('#movie-resumer-list tr').each(function(){
                var tr = $(this);
                tr.find('button').click(function(){
                    var input = tr.find('input')[0];
                    var key = window.list_entries[input.value].url;
                    GM_deleteValue(key);
                    tr.remove();
                });
            });


            $('#movie-resumer-list input').click(function(){
                var num = $('#movie-resumer-list input:checkbox:checked').length;
                var total = $('#movie-resumer-list input:checkbox').length;
                if (num == 0){
                    $('#movie-resumer-select-all').prop('indeterminate', false);
                    $('#movie-resumer-select-all').prop('checked', false);
                } else if (num == total){
                    $('#movie-resumer-select-all').prop('indeterminate', false);
                    $('#movie-resumer-select-all').prop('checked', true);
                } else {
                    $('#movie-resumer-select-all').prop('indeterminate', true);
                }
            });

        });
        $('#movie-resumer-select-all').click(function(){
            $('#movie-resumer-list input').prop('checked', this.checked);
        });
        $('#movie-resumer-delete-selected').click(function(){
            $('#movie-resumer-list tr').each(function(){
                var input = $(this).find('input')[0];
                if (input.checked){
                    var key = window.list_entries[input.value].url;
                    GM_deleteValue(key);
                    $(this).remove();
                }
            });
            $('#movie-resumer-select-all').prop('checked', false);
        });
        $('#movie-resumer-close').click(function(){
            $('#movie-resumer-small').hide();
        });
        $('#movie-resumer-base-close').click(function(){
            $('#movie-resumer-base').hide();
            $('#movie-resumer-small').show();
        });

        $(document).keypress(function(e){
            //console.log(e);
            if (e.keyCode == 126){

                    GM_deleteValue('base_location');
                    GM_deleteValue('small_location');
                    eBase.style.left = "";
                    eBase.style.top = "";
                    eBase.style.right = "10px";
                    eBase.style.bottom = "10px";

                    eSmall.style.left = "";
                    eSmall.style.top = "";
                    eSmall.style.right = "10px";
                    eSmall.style.bottom = "10px";

                    return;

            }
            if (e.keyCode == 96){

                if ($('#movie-resumer-base').css('display') == 'block') {
                    $('#movie-resumer-base').hide();
                    $('#movie-resumer-small').show();
                    return;
                }

                if ($('#movie-resumer-small').css('display') == 'block'){
                    $('#movie-resumer-small').hide();
                } else {
                    $('#movie-resumer-small').show();
                }
            }
        });
        var key = document.location.href;
        var last_time_value = GM_getValue(key);
        $sync_last_time.html(last_time_value ? last_time_value.time : "--:--");

        var load_direct = setInterval(function(){
            if (document.location.href != key){
                is_enable_sync = true;
                key = document.location.href;
                last_time_value = GM_getValue(key);
                $sync_last_time.html(last_time_value ? last_time_value.time : "--:--");
            }
            if (!is_enable_sync) return;
            $current_time = FindElementFromArray($current_time, current_time, false);
            $total_time = FindElementFromArray($total_time, total_time, false);
            if ($current_time != null && $total_time != null){
                
                var value = $current_time.html();
                $('#movie-resumer-progress').slideDown();

                if (SaveValue(key, value, $total_time.html())){
                    $sync_current_time.html(value);
                } else {
                    $sync_current_time.html("--:--");
                }
                
            } else {
                $('#movie-resumer-progress').slideUp();
            }
        }, INTERVAL_SYNC);

        GM_onMessage(internal_id, function(id, value, total) {
            if (document.location.href != key){
                is_enable_sync = true;
                key = document.location.href;
                last_time_value = GM_getValue(key);
                $sync_last_time.html(last_time_value ? last_time_value.time : "--:--");
            }
            if (!is_enable_sync) return;
            if (load_direct !== 0){
                clearInterval(load_direct);
                load_direct = 0;
                is_direct_sync = false;
            }

            if (value != null){
                
                $('#movie-resumer-progress').slideDown();
                if (SaveValue(key, value, total)){
                    $sync_current_time.html(value);
                } else {
                    $sync_current_time.html("--:--");
                }
            } else {
                $('#movie-resumer-progress').slideUp();
            }
        });
    }

    else {
        var i = 1;
        console.log("MovieResumer on iFrame: " + document.location.href);
        GM_getTab(function(tab){
            var internal_id = tab.internal_id;

            GM_onMessage(internal_id + "_frame", function(i, value){
                SeekVideo(value);
            });

            setInterval(function(){
                $current_time = FindElementFromArray($current_time, current_time, false);
                $total_time = FindElementFromArray($total_time, total_time, false);
                if ($current_time != null && $total_time != null){
                    GM_sendMessage(internal_id, i++, $current_time.html(), $total_time.html());
                } else {
                    GM_sendMessage(internal_id, i++, null, null);
                }
            }, INTERVAL_SYNC);
        });
       
    }
    }

    var checkJQuery = setInterval(function(){
        try {
            $();
            clearInterval(checkJQuery);
            Execute();
        } catch (e) {}
    }, 500);


})();