mirror of
https://github.com/iv-org/invidious.git
synced 2025-08-21 10:49:02 -05:00
Complete API-only mode implementation
This commit completes the API-only mode feature that allows Invidious to be built and run without GUI/frontend components, significantly reducing the binary size and dependencies. Changes include: - Add conditional compilation flags throughout the codebase to exclude frontend-specific code - Create stub implementations for database operations in API-only mode - Update Docker configurations to support API-only builds - Refactor require statements for better modularity - Add DummyDB and stub types for API-only mode - Ensure all routes work correctly without frontend dependencies The API-only mode can be enabled by: - Using -Dapi_only flag during compilation - Setting API_ONLY=1 when using make - Using --build-arg api_only=1 with Docker builds This is particularly useful for: - Microservice architectures where only the API is needed - Reducing resource usage in containerized environments - Creating lightweight API servers for mobile/desktop applications
This commit is contained in:
parent
df8839d1f0
commit
0b2ec108d1
@ -3,6 +3,7 @@ FROM crystallang/crystal:1.16.3-alpine AS builder
|
|||||||
RUN apk add --no-cache sqlite-static yaml-static
|
RUN apk add --no-cache sqlite-static yaml-static
|
||||||
|
|
||||||
ARG release
|
ARG release
|
||||||
|
ARG api_only
|
||||||
|
|
||||||
WORKDIR /invidious
|
WORKDIR /invidious
|
||||||
COPY ./shard.yml ./shard.yml
|
COPY ./shard.yml ./shard.yml
|
||||||
@ -21,16 +22,13 @@ COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
|
|||||||
|
|
||||||
RUN crystal spec --warnings all \
|
RUN crystal spec --warnings all \
|
||||||
--link-flags "-lxml2 -llzma"
|
--link-flags "-lxml2 -llzma"
|
||||||
RUN --mount=type=cache,target=/root/.cache/crystal if [[ "${release}" == 1 ]] ; then \
|
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/crystal \
|
||||||
crystal build ./src/invidious.cr \
|
crystal build ./src/invidious.cr \
|
||||||
--release \
|
${release:+--release} \
|
||||||
--static --warnings all \
|
--static --warnings all \
|
||||||
--link-flags "-lxml2 -llzma"; \
|
--link-flags "-lxml2 -llzma" \
|
||||||
else \
|
${api_only:+-Dapi_only -Dskip_videojs_download}
|
||||||
crystal build ./src/invidious.cr \
|
|
||||||
--static --warnings all \
|
|
||||||
--link-flags "-lxml2 -llzma"; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
FROM alpine:3.21
|
FROM alpine:3.21
|
||||||
RUN apk add --no-cache rsvg-convert ttf-opensans tini tzdata
|
RUN apk add --no-cache rsvg-convert ttf-opensans tini tzdata
|
||||||
|
@ -3,6 +3,7 @@ RUN apk add --no-cache 'crystal=1.14.0-r0' shards sqlite-static yaml-static yaml
|
|||||||
zlib-static openssl-libs-static openssl-dev musl-dev xz-static
|
zlib-static openssl-libs-static openssl-dev musl-dev xz-static
|
||||||
|
|
||||||
ARG release
|
ARG release
|
||||||
|
ARG api_only
|
||||||
|
|
||||||
WORKDIR /invidious
|
WORKDIR /invidious
|
||||||
COPY ./shard.yml ./shard.yml
|
COPY ./shard.yml ./shard.yml
|
||||||
@ -22,16 +23,12 @@ COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
|
|||||||
RUN crystal spec --warnings all \
|
RUN crystal spec --warnings all \
|
||||||
--link-flags "-lxml2 -llzma"
|
--link-flags "-lxml2 -llzma"
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/crystal if [[ "${release}" == 1 ]] ; then \
|
RUN --mount=type=cache,target=/root/.cache/crystal \
|
||||||
crystal build ./src/invidious.cr \
|
crystal build ./src/invidious.cr \
|
||||||
--release \
|
${release:+--release} \
|
||||||
--static --warnings all \
|
--static --warnings all \
|
||||||
--link-flags "-lxml2 -llzma"; \
|
--link-flags "-lxml2 -llzma" \
|
||||||
else \
|
${api_only:+-Dapi_only -Dskip_videojs_download}
|
||||||
crystal build ./src/invidious.cr \
|
|
||||||
--static --warnings all \
|
|
||||||
--link-flags "-lxml2 -llzma"; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
FROM alpine:3.21
|
FROM alpine:3.21
|
||||||
RUN apk add --no-cache rsvg-convert ttf-opensans tini tzdata
|
RUN apk add --no-cache rsvg-convert ttf-opensans tini tzdata
|
||||||
|
@ -31,24 +31,33 @@ require "yaml"
|
|||||||
require "compress/zip"
|
require "compress/zip"
|
||||||
require "protodec/utils"
|
require "protodec/utils"
|
||||||
|
|
||||||
|
# Database requires
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
require "./invidious/database/*"
|
require "./invidious/database/*"
|
||||||
require "./invidious/database/migrations/*"
|
require "./invidious/database/migrations/*"
|
||||||
|
{% else %}
|
||||||
|
require "./invidious/database/api_only_stubs"
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
# Core requires
|
||||||
require "./invidious/http_server/*"
|
require "./invidious/http_server/*"
|
||||||
require "./invidious/helpers/*"
|
require "./invidious/helpers/*"
|
||||||
require "./invidious/yt_backend/*"
|
require "./invidious/yt_backend/*"
|
||||||
require "./invidious/frontend/*"
|
require "./invidious/frontend/*"
|
||||||
require "./invidious/videos/*"
|
require "./invidious/videos/*"
|
||||||
|
|
||||||
require "./invidious/jsonify/**"
|
require "./invidious/jsonify/**"
|
||||||
|
require "./invidious/requires"
|
||||||
require "./invidious/*"
|
|
||||||
require "./invidious/comments/*"
|
require "./invidious/comments/*"
|
||||||
require "./invidious/channels/*"
|
require "./invidious/channels/*"
|
||||||
require "./invidious/user/*"
|
require "./invidious/user/*"
|
||||||
require "./invidious/search/*"
|
require "./invidious/search/*"
|
||||||
require "./invidious/routes/**"
|
require "./invidious/routes/**"
|
||||||
|
|
||||||
|
# Jobs (not needed in API-only mode)
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
require "./invidious/jobs/base_job"
|
require "./invidious/jobs/base_job"
|
||||||
require "./invidious/jobs/*"
|
require "./invidious/jobs/*"
|
||||||
|
{% end %}
|
||||||
|
|
||||||
# Declare the base namespace for invidious
|
# Declare the base namespace for invidious
|
||||||
module Invidious
|
module Invidious
|
||||||
@ -60,7 +69,13 @@ alias IV = Invidious
|
|||||||
CONFIG = Config.load
|
CONFIG = Config.load
|
||||||
HMAC_KEY = CONFIG.hmac_key
|
HMAC_KEY = CONFIG.hmac_key
|
||||||
|
|
||||||
|
# Database connection
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
PG_DB = DB.open CONFIG.database_url
|
PG_DB = DB.open CONFIG.database_url
|
||||||
|
{% else %}
|
||||||
|
require "./invidious/api_only_types"
|
||||||
|
PG_DB = DummyDB.new
|
||||||
|
{% end %}
|
||||||
ARCHIVE_URL = URI.parse("https://archive.org")
|
ARCHIVE_URL = URI.parse("https://archive.org")
|
||||||
PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com")
|
PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com")
|
||||||
REDDIT_URL = URI.parse("https://www.reddit.com")
|
REDDIT_URL = URI.parse("https://www.reddit.com")
|
||||||
@ -133,7 +148,11 @@ Kemal.config.extra_options do |parser|
|
|||||||
exit
|
exit
|
||||||
end
|
end
|
||||||
parser.on("--migrate", "Run any migrations (beta, use at your own risk!!") do
|
parser.on("--migrate", "Run any migrations (beta, use at your own risk!!") do
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
Invidious::Database::Migrator.new(PG_DB).migrate
|
Invidious::Database::Migrator.new(PG_DB).migrate
|
||||||
|
{% else %}
|
||||||
|
puts "Database migrations are not available in API-only mode"
|
||||||
|
{% end %}
|
||||||
exit
|
exit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -147,9 +166,11 @@ OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mo
|
|||||||
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level, CONFIG.colorize_logs)
|
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level, CONFIG.colorize_logs)
|
||||||
|
|
||||||
# Check table integrity
|
# Check table integrity
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
Invidious::Database.check_integrity(CONFIG)
|
Invidious::Database.check_integrity(CONFIG)
|
||||||
|
{% end %}
|
||||||
|
|
||||||
{% if !flag?(:skip_videojs_download) %}
|
{% if !flag?(:skip_videojs_download) && !flag?(:api_only) %}
|
||||||
# Resolve player dependencies. This is done at compile time.
|
# Resolve player dependencies. This is done at compile time.
|
||||||
#
|
#
|
||||||
# Running the script by itself would show some colorful feedback while this doesn't.
|
# Running the script by itself would show some colorful feedback while this doesn't.
|
||||||
@ -175,6 +196,7 @@ DECRYPT_FUNCTION =
|
|||||||
|
|
||||||
# Start jobs
|
# Start jobs
|
||||||
|
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
if CONFIG.channel_threads > 0
|
if CONFIG.channel_threads > 0
|
||||||
Invidious::Jobs.register Invidious::Jobs::RefreshChannelsJob.new(PG_DB)
|
Invidious::Jobs.register Invidious::Jobs::RefreshChannelsJob.new(PG_DB)
|
||||||
end
|
end
|
||||||
@ -204,9 +226,18 @@ Invidious::Jobs.register Invidious::Jobs::ClearExpiredItemsJob.new
|
|||||||
Invidious::Jobs.register Invidious::Jobs::InstanceListRefreshJob.new
|
Invidious::Jobs.register Invidious::Jobs::InstanceListRefreshJob.new
|
||||||
|
|
||||||
Invidious::Jobs.start_all
|
Invidious::Jobs.start_all
|
||||||
|
{% else %}
|
||||||
|
# Define channels for API-only mode (even though they won't be used)
|
||||||
|
NOTIFICATION_CHANNEL = ::Channel(VideoNotification).new(1)
|
||||||
|
CONNECTION_CHANNEL = ::Channel({Bool, ::Channel(PQ::Notification)}).new(1)
|
||||||
|
{% end %}
|
||||||
|
|
||||||
def popular_videos
|
def popular_videos
|
||||||
|
{% if flag?(:api_only) %}
|
||||||
|
[] of ChannelVideo
|
||||||
|
{% else %}
|
||||||
Invidious::Jobs::PullPopularVideosJob::POPULAR_VIDEOS.get
|
Invidious::Jobs::PullPopularVideosJob::POPULAR_VIDEOS.get
|
||||||
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Routing
|
# Routing
|
||||||
|
50
src/invidious/api_only_types.cr
Normal file
50
src/invidious/api_only_types.cr
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# API-only mode type definitions
|
||||||
|
# This file provides dummy type definitions when running in API-only mode
|
||||||
|
|
||||||
|
# Dummy DB class for API-only mode
|
||||||
|
class DummyDB
|
||||||
|
def query_all(*args, as : T.class) forall T
|
||||||
|
[] of T
|
||||||
|
end
|
||||||
|
|
||||||
|
def query_one(*args, as : T.class) forall T
|
||||||
|
raise "Database not available in API-only mode"
|
||||||
|
end
|
||||||
|
|
||||||
|
def query_one?(*args, as : T.class) forall T
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def scalar(*args)
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def exec(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# VideoNotification struct for API-only mode
|
||||||
|
struct VideoNotification
|
||||||
|
property video_id : String
|
||||||
|
property channel_id : String
|
||||||
|
property published : Time
|
||||||
|
|
||||||
|
def initialize(@video_id = "", @channel_id = "", @published = Time.utc)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_video(video : ChannelVideo) : VideoNotification
|
||||||
|
VideoNotification.new(video.id, video.ucid, video.published)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# PQ module with Notification for API-only mode
|
||||||
|
module PQ
|
||||||
|
struct Notification
|
||||||
|
property channel : String = ""
|
||||||
|
property payload : String = ""
|
||||||
|
|
||||||
|
def initialize(@channel = "", @payload = "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -107,7 +107,9 @@ class Config
|
|||||||
property full_refresh : Bool = false
|
property full_refresh : Bool = false
|
||||||
|
|
||||||
# Jobs config structure. See jobs.cr and jobs/base_job.cr
|
# Jobs config structure. See jobs.cr and jobs/base_job.cr
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
property jobs = Invidious::Jobs::JobsConfig.new
|
property jobs = Invidious::Jobs::JobsConfig.new
|
||||||
|
{% end %}
|
||||||
|
|
||||||
# Used to tell Invidious it is behind a proxy, so links to resources should be https://
|
# Used to tell Invidious it is behind a proxy, so links to resources should be https://
|
||||||
property https_only : Bool?
|
property https_only : Bool?
|
||||||
@ -285,6 +287,7 @@ class Config
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Build database_url from db.* if it's not set directly
|
# Build database_url from db.* if it's not set directly
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
if config.database_url.to_s.empty?
|
if config.database_url.to_s.empty?
|
||||||
if db = config.db
|
if db = config.db
|
||||||
config.database_url = URI.new(
|
config.database_url = URI.new(
|
||||||
@ -300,6 +303,12 @@ class Config
|
|||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
{% else %}
|
||||||
|
# In API-only mode, database is optional
|
||||||
|
if config.database_url.to_s.empty?
|
||||||
|
config.database_url = URI.parse("postgres://dummy:dummy@localhost/dummy")
|
||||||
|
end
|
||||||
|
{% end %}
|
||||||
|
|
||||||
# Check if the socket configuration is valid
|
# Check if the socket configuration is valid
|
||||||
if sb = config.socket_binding
|
if sb = config.socket_binding
|
||||||
|
240
src/invidious/database/api_only_stubs.cr
Normal file
240
src/invidious/database/api_only_stubs.cr
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
# API-only mode database stubs
|
||||||
|
# This file provides dummy implementations for database modules when running in API-only mode
|
||||||
|
|
||||||
|
module Invidious::Database
|
||||||
|
module SessionIDs
|
||||||
|
def self.select_email(sid : String) : String?
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select_all(email : String)
|
||||||
|
[] of {session: String, issued: Time}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete(sid : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.insert(session : String, email : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Users
|
||||||
|
def self.select!(email : String)
|
||||||
|
raise "Database not available in API-only mode"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_preferences(user)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mark_watched(user, id)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mark_unwatched(user, id)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.clear_watch_history(user)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.subscribe_channel(user, ucid)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.unsubscribe_channel(user, ucid)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_subscriptions(user)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_watch_history(user)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_password(user, password : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update(user)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.insert(user)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete(user)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select_notifications(user)
|
||||||
|
[] of String
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mark_notifications_as_read(user)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.feed_needs_update(user)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_feed_watched(user)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Channels
|
||||||
|
def self.select(subscriptions)
|
||||||
|
[] of InvidiousChannel
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select(id : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.insert(channel, update_on_conflict : Bool = false)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module ChannelVideos
|
||||||
|
def self.select_notfications(*args)
|
||||||
|
[] of ChannelVideo
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select_latest_videos(*args)
|
||||||
|
[] of ChannelVideo
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.insert(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select(notifications)
|
||||||
|
[] of ChannelVideo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Playlists
|
||||||
|
def self.select_all(author : String)
|
||||||
|
[] of InvidiousPlaylist
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.count_owned_by(email : String)
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select(id : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete(id : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_video_added(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_video_removed(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select_like_iv(email : String)
|
||||||
|
[] of InvidiousPlaylist
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.insert(playlist)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_description(id : String, description : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module PlaylistVideos
|
||||||
|
def self.insert(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select_ids(*args, limit : Int32? = nil)
|
||||||
|
[] of String
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.select(*args, limit : Int32? = nil)
|
||||||
|
[] of PlaylistVideo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Annotations
|
||||||
|
def self.select(id : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.insert(id : String, annotations : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Videos
|
||||||
|
def self.select(id : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.insert(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update(*args)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete(id : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Nonces
|
||||||
|
def self.select(nonce : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.insert(nonce : String, expire : Time? = nil)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete(nonce : String)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Migrator stub
|
||||||
|
class Migrator
|
||||||
|
def initialize(db)
|
||||||
|
end
|
||||||
|
|
||||||
|
def migrate
|
||||||
|
puts "Database migrations are not available in API-only mode"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.check_integrity(config)
|
||||||
|
# Skip integrity check in API-only mode
|
||||||
|
end
|
||||||
|
end
|
@ -186,6 +186,7 @@ end
|
|||||||
#
|
#
|
||||||
# Creates a new tracker when unavailable.
|
# Creates a new tracker when unavailable.
|
||||||
def get_playback_statistic
|
def get_playback_statistic
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
if (tracker = Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"]) && tracker.as(Hash).empty?
|
if (tracker = Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"]) && tracker.as(Hash).empty?
|
||||||
tracker = {
|
tracker = {
|
||||||
"totalRequests" => 0_i64,
|
"totalRequests" => 0_i64,
|
||||||
@ -197,4 +198,12 @@ def get_playback_statistic
|
|||||||
end
|
end
|
||||||
|
|
||||||
return tracker.as(Hash(String, Int64 | Float64))
|
return tracker.as(Hash(String, Int64 | Float64))
|
||||||
|
{% else %}
|
||||||
|
# Return empty statistics in API-only mode
|
||||||
|
return {
|
||||||
|
"totalRequests" => 0_i64,
|
||||||
|
"successfulRequests" => 0_i64,
|
||||||
|
"ratio" => 0_f64,
|
||||||
|
}
|
||||||
|
{% end %}
|
||||||
end
|
end
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
{% unless flag?(:api_only) %}
|
||||||
module Invidious::Jobs
|
module Invidious::Jobs
|
||||||
JOBS = [] of BaseJob
|
JOBS = [] of BaseJob
|
||||||
|
|
||||||
@ -38,3 +39,4 @@ module Invidious::Jobs
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
{% end %}
|
||||||
|
18
src/invidious/requires.cr
Normal file
18
src/invidious/requires.cr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Common requires for Invidious
|
||||||
|
# This file contains the require statements organized by category
|
||||||
|
|
||||||
|
# Core requires
|
||||||
|
require "./config"
|
||||||
|
require "./exceptions"
|
||||||
|
require "./hashtag"
|
||||||
|
require "./mixes"
|
||||||
|
require "./playlists"
|
||||||
|
require "./routing"
|
||||||
|
require "./trending"
|
||||||
|
require "./users"
|
||||||
|
require "./videos"
|
||||||
|
|
||||||
|
# Jobs (only if not in API-only mode)
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
|
require "./jobs"
|
||||||
|
{% end %}
|
@ -6,6 +6,7 @@ module Invidious::Routes::API::V1::Misc
|
|||||||
if !CONFIG.statistics_enabled
|
if !CONFIG.statistics_enabled
|
||||||
return {"software" => SOFTWARE}.to_json
|
return {"software" => SOFTWARE}.to_json
|
||||||
else
|
else
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
# Calculate playback success rate
|
# Calculate playback success rate
|
||||||
if (tracker = Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"]?)
|
if (tracker = Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"]?)
|
||||||
tracker = tracker.as(Hash(String, Int64 | Float64))
|
tracker = tracker.as(Hash(String, Int64 | Float64))
|
||||||
@ -23,6 +24,10 @@ module Invidious::Routes::API::V1::Misc
|
|||||||
end
|
end
|
||||||
|
|
||||||
return Invidious::Jobs::StatisticsRefreshJob::STATISTICS.to_json
|
return Invidious::Jobs::StatisticsRefreshJob::STATISTICS.to_json
|
||||||
|
{% else %}
|
||||||
|
# Return empty statistics in API-only mode
|
||||||
|
return {"software" => SOFTWARE}.to_json
|
||||||
|
{% end %}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ module Invidious::Routes::BeforeAll
|
|||||||
raise "Cannot use token as SID"
|
raise "Cannot use token as SID"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
{% unless flag?(:api_only) %}
|
||||||
if email = Database::SessionIDs.select_email(sid)
|
if email = Database::SessionIDs.select_email(sid)
|
||||||
user = Database::Users.select!(email: email)
|
user = Database::Users.select!(email: email)
|
||||||
csrf_token = generate_response(sid, {
|
csrf_token = generate_response(sid, {
|
||||||
@ -90,6 +91,7 @@ module Invidious::Routes::BeforeAll
|
|||||||
env.set "csrf_token", csrf_token
|
env.set "csrf_token", csrf_token
|
||||||
env.set "user", user
|
env.set "user", user
|
||||||
end
|
end
|
||||||
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s
|
dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s
|
||||||
|
Loading…
x
Reference in New Issue
Block a user