Odysee Auto-Liker

Automatically likes Odysee videos

// ==UserScript==
// @name           Odysee Auto-Liker
// @namespace      https://github.com/Kite8409/odysee-auto-liker
// @version        1.0.4
// @description    Automatically likes Odysee videos
// @author         Kite8409 (fork from https://github.com/HatScripts/youtube-auto-liker)
// @license        MIT
// @icon           https://raw.githubusercontent.com/Kite8409/odysee-auto-liker/master/logo.svg
// @match          http*://odysee.com/*
// @require        https://openuserjs.org/src/libs/sizzle/GM_config.js
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_registerMenuCommand
// @run-at         document-idle
// @noframes
// @supportURL     https://github.com/Kite8409/odysee-auto-liker/issues/
// ==/UserScript==

/* global GM_config, GM_info, GM_registerMenuCommand */

(() => {
  'use strict'

  GM_config.init({
    id: 'ytal_config',
    title: GM_info.script.name + ' Settings',
    fields: {
      DEBUG_MODE: {
        label: 'Debug mode',
        type: 'checkbox',
        default: false,
        title: 'Log debug messages to the console'
      },
      CHECK_FREQUENCY: {
        label: 'Check frequency (ms)',
        type: 'number',
        min: 1,
        default: 5000,
        title: 'The number of milliseconds to wait between checking if video should be liked'
      },
      WATCH_THRESHOLD: {
        label: 'Watch threshold %',
        type: 'number',
        min: 0,
        max: 100,
        default: 50,
        title: 'The percentage watched to like the video at'
      },
      LIKE_IF_NOT_SUBSCRIBED: {
        label: 'Like if not following',
        type: 'checkbox',
        default: true,
        title: 'Like videos from channels you are not following'
      }
    }
  })

  GM_registerMenuCommand('Settings', () => {
    GM_config.open()
  })

  function Debugger (name, enabled) {
    this.debug = {}
    if (!window.console) {
      return () => {}
    }
    for (const m in console) {
      if (typeof console[m] === 'function') {
        if (enabled) {
          this.debug[m] = console[m].bind(window.console, name + ': ')
        } else {
          this.debug[m] = () => {}
        }
      }
    }
    return this.debug
  }

  const DEBUG = new Debugger(GM_info.script.name, GM_config.get('DEBUG_MODE'))
  // Define CSS selectors
  const SELECTORS = {
    PLAYER: 'video',
    FIRE_BUTTON: 'button.button-like:nth-child(1)',
    FIRE_BUTTON_CLICKED_CLASS: 'button--fire',
    FOLLOW_BUTTON: '.button-group > button:nth-child(1)',
    FOLLOW_BUTTON_CLICKED_CLASS: 'button-following',
  }

  const autoLikedVideoIds = []

  setTimeout(wait, GM_config.get('CHECK_FREQUENCY'))

  function getVideoId () {
    return location.pathname
  }

  function watchThresholdReached () {
    const player = document.querySelector(SELECTORS.PLAYER)
    if (!player || player.clientHeight <= 288) { // Check if player is not a mini player
      return false
    }
    return player.currentTime / player.duration >= (GM_config.get('WATCH_THRESHOLD') / 100)
  }

  function isSubscribed () {
    return document.querySelector(SELECTORS.FOLLOW_BUTTON).classList.contains(SELECTORS.FOLLOW_BUTTON_CLICKED_CLASS)
  }

  function isLiked(likeButton) {
    return likeButton.classList.contains(SELECTORS.FIRE_BUTTON_CLICKED_CLASS)
  }

  function wait () {
    if (!watchThresholdReached()) {
      setTimeout(wait, GM_config.get('CHECK_FREQUENCY'))
      return
    }

    try {
      if (GM_config.get('LIKE_IF_NOT_SUBSCRIBED') || isSubscribed()) {
        like()
      }
    } catch (e) {
      DEBUG.info(`Failed to like video: ${e}. Will try again in ${GM_config.get('CHECK_FREQUENCY')} ms...`)
    }

    setTimeout(wait, GM_config.get('CHECK_FREQUENCY'))
  }

  function like () {
    DEBUG.info('Trying to like video...')

    const likeButton =  document.querySelector(SELECTORS.FIRE_BUTTON)
    if (!likeButton) {
      throw Error('Couldn\'t find like button')
    }

    const videoId = getVideoId()

    if (isLiked(likeButton)) {
      DEBUG.info('Like button has already been clicked')
      autoLikedVideoIds.push(videoId)
    }
    else if (autoLikedVideoIds.includes(videoId)) {
      DEBUG.info('Video has already been auto-liked. User must have un-liked it, so we won\'t like it again')
    }
    else {
      DEBUG.info('Found like button')
      DEBUG.info('It\'s unclicked. Clicking it...')
      likeButton.click()
      autoLikedVideoIds.push(videoId)
      DEBUG.info('Successfully liked video')
    }
  }
})()