Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Job Management, Optimize Queries, and Introduce Thread-Safe Features #512

Merged
merged 6 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/components/load_disk_process_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class LoadDiskProcessComponent < ViewComponent::Base
extend Dry::Initializer

def self.job
Job.sort_by_created_at.active.find_by(name: 'LoadDiskWorker')
Backgrounder.managers.find { _1.current_job&.name == 'LoadDiskWorker' }&.current_job
end

def self.show?
Expand Down
4 changes: 2 additions & 2 deletions app/components/rip_process_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

class RipProcessComponent < ViewComponent::Base
def self.job
Job.sort_by_created_at.active.find_by(name: 'RipWorker')
Backgrounder.managers.find { _1.current_job&.name == 'RipWorker' }&.current_job
end

def self.show?
Video.auto_start.any? || job&.active?
job&.active? || Video.auto_start.any?
end

def hidden?
Expand Down
2 changes: 1 addition & 1 deletion app/components/scan_plex_process_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class ScanPlexProcessComponent < ViewComponent::Base
extend Dry::Initializer

def self.job
Job.sort_by_created_at.active.find_by(name: 'ScanPlexWorker')
Backgrounder.managers.find { _1.current_job&.name == 'ScanPlexWorker' }&.current_job
end

def self.show?
Expand Down
5 changes: 3 additions & 2 deletions app/components/upload_process_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class UploadProcessComponent < ViewComponent::Base
def self.job
Job.sort_by_created_at.active.find_by(name: 'UploadWorker')
Backgrounder.managers.find { _1.current_job&.name == 'UploadWorker' }&.current_job
end

def self.show?
Expand All @@ -18,7 +18,7 @@ def dom_id
end

def uploadable_video_blobs
@uploadable_video_blobs ||= VideoBlob.uploadable.order(updated_at: :desc)
@uploadable_video_blobs ||= VideoBlob.uploadable.includes(:video, :episode_last, episode: { season: :tv }).order(updated_at: :desc)
end

def percentage(completed, total)
Expand All @@ -27,6 +27,7 @@ def percentage(completed, total)

def uploaded_recently_video_blobs
@uploaded_recently_video_blobs ||= VideoBlob.uploaded_recently
.includes(:video, :episode_last, episode: [:season])
.order(uploaded_on: :desc)
.limit(3)
end
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/images_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

class ImagesController < ApplicationController
HOST_URL = 'https://image.tmdb.org/t/p'

skip_before_action :mkv_config, :movie_db_config, :plex_config
def show
send_data image, disposition: :inline, type: "image/#{params[:format]}"
end
Expand Down
7 changes: 4 additions & 3 deletions app/controllers/movies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
class MoviesController < ApplicationController
# movie GET /movies/:id(.:format)
def show
scope = Movie.includes(:video_blobs)
@movie = if params[:id].start_with?('tmdb:')
Movie.find_or_initialize_by(the_movie_db_id: params[:id].split(':').last)
scope.find_or_initialize_by(the_movie_db_id: params[:id].split(':').last)
else
Movie.find(params[:id])
scope.find(params[:id])
end
@movie.subscribe(TheMovieDb::VideoListener.new)
@movie.save!
Expand All @@ -16,7 +17,7 @@ def show
# rip_movie POST /movies/:id/rip(.:format)
def rip
movie = Movie.find(params[:id])
disk = Disk.find(params[:disk_id])
disk = Disk.includes(disk_titles: %i[episode episode_last]).find(params[:disk_id])
disk_titles = rip_disk_titles(disk, movie)
job = RipWorker.perform_async(
disk_id: disk.id,
Expand Down
15 changes: 10 additions & 5 deletions app/controllers/seasons_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

class SeasonsController < ApplicationController
def show
@tv = Tv.find(params[:tv_id])
@season = @tv.seasons.includes(episodes: [:ripped_disk_titles]).find(params[:id])
@tv = Tv.includes(:ripped_disk_titles).find(params[:tv_id])
@season = @tv.seasons.includes(
:ripped_disk_titles,
episodes: [
{ uploaded_video_blobs: %i[episode episode_last] }, { ripped_disk_titles: %i[episode_last] }, :video_blobs
]
).find(params[:id])
@season.subscribe(TheMovieDb::EpisodesListener.new)
@season.save!
@disks = Disk.not_ejected
@disks = Disk.not_ejected.includes(:disk_titles)
end

def rip
Expand All @@ -17,11 +22,11 @@ def rip
private

def season
@season ||= tv.seasons.find(params[:id])
@season ||= tv.seasons.includes(:episodes).find(params[:id])
end

def disk
@disk ||= Disk.find(params[:disk_id])
@disk ||= Disk.includes(disk_titles: [:episode]).find(params[:disk_id])
end

def tv
Expand Down
5 changes: 3 additions & 2 deletions app/controllers/tvs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

class TvsController < ApplicationController
def show
scope = Tv.includes(:seasons)
@tv = if params[:id].start_with?('tmdb:')
Tv.find_or_initialize_by(the_movie_db_id: params[:id].split(':').last)
scope.find_or_initialize_by(the_movie_db_id: params[:id].split(':').last)
else
Tv.find(params[:id])
scope.find(params[:id])
end
@tv.subscribe(TheMovieDb::VideoListener.new)
@tv.save!
Expand Down
2 changes: 1 addition & 1 deletion app/listeners/disk_listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def disk_loading(_)
end

def disk_loaded(disk)
return reload_page! unless (video = Video.auto_start.first)
return reload_page! unless (video = Video.auto_start.includes(:ripped_disk_titles).first)

info_disk_titles = rip_disk_titles(disk, video)
return reload_page! if info_disk_titles.empty?
Expand Down
2 changes: 1 addition & 1 deletion app/models/disk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Disk < ApplicationRecord

after_commit { broadcast(:disk_updated, self) }

has_many :disk_titles, dependent: :nullify, autosave: true
has_many :disk_titles, -> { order(:segment_map) }, dependent: :nullify, autosave: true, inverse_of: :disk
has_many :not_ripped_disk_titles, -> { not_ripped }, dependent: false, inverse_of: :disk, class_name: 'DiskTitle'
belongs_to :video, optional: true
belongs_to :episode, optional: true
Expand Down
11 changes: 10 additions & 1 deletion app/models/disk_title.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# filename :string not null
# name :string
# ripped_at :datetime
# segment_map :string
# size :integer default(0), not null
# created_at :datetime not null
# updated_at :datetime not null
Expand All @@ -34,6 +35,8 @@
class DiskTitle < ApplicationRecord
include ActionView::Helpers::DateHelper

serialize :segment_map, coder: JSON, type: Array

belongs_to :video, optional: true
belongs_to :episode, optional: true
belongs_to :episode_last, optional: true, class_name: 'Episode'
Expand All @@ -43,13 +46,19 @@ class DiskTitle < ApplicationRecord

scope :not_ripped, -> { where(ripped_at: nil) }
scope :ripped, -> { where.not(ripped_at: nil) }
scope :sort_by_segment_map, -> { order(:segment_map) }

validates :filename, presence: true

before_save :set_episode_last

def to_label
"##{title_id} #{name || filename} #{distance_of_time_in_words(duration)}"
[
"##{title_id}",
filename || name,
distance_of_time_in_words(duration, 0, include_seconds: false, scope: 'datetime.distance_in_words.short'),
segment_map.join(', ')
].compact_blank.join(' ')
end

def episode_numbers
Expand Down
6 changes: 5 additions & 1 deletion app/models/job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,12 @@ def perform
nil
end

def name_constant
@name_constant ||= name.constantize
end

def worker
@worker ||= name.constantize.new(**arguments.symbolize_keys, job: self)
@worker ||= name_constant.new(**arguments.symbolize_keys, job: self)
rescue StandardError => e
record_exception!(e)
broadcast_page_reload!
Expand Down
2 changes: 1 addition & 1 deletion app/models/video_blob.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def title
def episode_numbers
return if episode.nil?

episode.episode_number..(episode_last&.episode_number || episode.episode_number)
@episode_numbers ||= episode.episode_number..(episode_last&.episode_number || episode.episode_number)
end

def uploaded?
Expand Down
20 changes: 13 additions & 7 deletions app/services/create_disks_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
class CreateDisksService < ApplicationService
include Shell

option :job, Types.Instance(Job)
option :listener, optional: true

def call
return [] if (drives = list_drives).empty?
Expand All @@ -14,7 +14,7 @@ def call
private

def create_or_update_disks(drive)
find_or_initalize_disk(drive).tap do |disk|
find_or_initialize_disk(drive).tap do |disk|
disk.update!(loading: true)
broadcast(:disk_loading, disk)
disk.disk_titles.each(&:mark_for_destruction)
Expand All @@ -29,17 +29,20 @@ def create_or_update_disks(drive)
def find_or_build_disk_titles(disk)
disk_info(disk).each do |info|
disk_title = find_or_build_disk_title(disk, info)
Rails.logger.debug { "found #{disk_title.to_label} " }
disk_title.unmark_for_destruction
end
end

def find_or_initalize_disk(drive)
Disk.find_or_initialize_by(name: drive.drive_name, disk_name: drive.disc_name)
def find_or_initialize_disk(drive)
Disk.find_or_initialize_by(name: drive.drive_name, disk_name: drive.disc_name).tap do |disk|
disk.disk_titles.load
end
end

def disk_info(disk)
service = DiskInfoService.new(disk_name: disk.disk_name)
service.subscribe(MkvDiskLoadListener.new(job:))
service.subscribe(listener) if listener
service.call
end

Expand All @@ -50,15 +53,18 @@ def find_or_build_disk_title(disk, title)
title.filename == disk_title.filename &&
title.bytes == disk_title.size &&
title.duration_seconds == disk_title.duration &&
title.angle == disk_title.angle
title.angle == disk_title.angle &&
title.description == disk_title.description &&
title.segment_map == disk_title.segment_map
end || disk.disk_titles.build(
title_id: title.id,
name: title.name,
filename: title.filename,
size: title.bytes,
duration: title.duration_seconds,
angle: title.angle,
description: title.description
description: title.description,
segment_map: title.segment_map
)
end
end
5 changes: 3 additions & 2 deletions app/services/create_mkv_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ def tmp_dir
end

def video_blob
scope = VideoBlob.includes(:video, :episode_last, episode: { season: :tv })
@video_blob ||= if extra_type == :feature_films || extra_type.blank? || edition.present?
VideoBlob.find_or_create_by!(
scope.find_or_create_by!(
video: disk_title.video,
episode: disk_title.episode,
episode_last: disk_title.episode_last,
Expand All @@ -70,7 +71,7 @@ def video_blob
part:
)
else
VideoBlob.create!(
scope.create!(
video: disk_title.video,
episode: disk_title.episode,
episode_last: disk_title.episode_last,
Expand Down
4 changes: 4 additions & 0 deletions app/services/disk_info_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ class TitleInfo

param :id, Types::Coercible::Integer

def segment_map
Array.wrap(@segment_map&.split(',')&.map(&:to_i))
end

def duration_seconds
hours, minutes, seconds = duration.split(':')
seconds.to_i + (minutes.to_i * 60) + (hours.to_i * 60 * 60)
Expand Down
8 changes: 6 additions & 2 deletions app/services/episode_disk_title_selector_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ def episode_disk_title(episode)
return if disk.nil?
return if uploaded?(episode) || ripped?(episode)

disk.disk_titles.find { within_range?(episode, _1) && selected_disk_titles.exclude?(_1) }.tap do |disk_title|
disk_titles.find { within_range?(episode, _1) && selected_disk_titles.exclude?(_1) }.tap do |disk_title|
selected_disk_titles.append(disk_title) if disk_title
end
end

def disk_titles
disk.disk_titles.sort_by { _1.segment_map.sum }
end

def select_episode(disk_title)
episode = sort_episodes.find { selected_episodes.exclude?(_1) && within_range?(_1, disk_title) }
selected_episodes.append(episode)
Expand All @@ -42,7 +46,7 @@ def select_episode(disk_title)
def uploaded?(episode)
uploaded_episodes_numbers.any? { _1.include?(episode.episode_number) } ||
episode.ripped_disk_titles.any? { _1&.video_blob&.uploaded? } ||
episode.video_blobs.any?(&:uploaded?)
episode.uploaded_video_blobs.any?
end

def ripped?(episode)
Expand Down
2 changes: 1 addition & 1 deletion app/tool_box/mkv_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module MkvParser
15 => :angle,
16 => :source_file_name,
25 => :segment_count,
26 => :segement_map,
26 => :segment_map,
27 => :filename,
28 => :lang,
29 => :language,
Expand Down
12 changes: 6 additions & 6 deletions app/views/seasons/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
Title Select
<small>
<% if @tv.duration_stats.weighted_average %>
(<%= distance_of_time_in_words(@tv.duration_range.min, 0, include_seconds: params[:debug] == 'true') %>
(<%= distance_of_time_in_words(@tv.duration_range.min, 0, include_seconds: params[:debug] == 'true', scope: "datetime.distance_in_words.short") %>
..
<%= distance_of_time_in_words(@tv.duration_range.max, 0, include_seconds: params[:debug] == 'true') %>)
<%= distance_of_time_in_words(@tv.duration_range.max, 0, include_seconds: params[:debug] == 'true', scope: "datetime.distance_in_words.short") %>)

<% if params[:debug] == 'true' %>
<br>
Expand Down Expand Up @@ -92,14 +92,14 @@
<td>
<% if @disks.any? %>
<%= select_tag 'episodes[][disk_title_id]',
options_from_collection_for_select(@disks.first.disk_titles, :id, :to_label, title.disk_title&.id), prompt: "", data: { manage_selections_target: :input }, class: 'form-select mb-2' %>
options_from_collection_for_select(@disks.first.disk_titles.sort_by { _1.segment_map.sum }, :id, :to_label, title.disk_title&.id), prompt: "", data: { manage_selections_target: :input }, class: 'form-select mb-2' %>
<% end %>
<% if title.episode.duration_stats.weighted_average %>
<%= distance_of_time_in_words(title.episode.duration_stats.weighted_average) %>
<%= distance_of_time_in_words(title.episode.duration_stats.weighted_average, 0) %>
<% elsif @season.duration_stats.weighted_average%>
<%= distance_of_time_in_words(@tv.duration_stats.weighted_average) %>
<%= distance_of_time_in_words(@tv.duration_stats.weighted_average, 0) %>
<% elsif title.episode.runtime %>
<%= distance_of_time_in_words(title.episode.runtime) %>
<%= distance_of_time_in_words(title.episode.runtime, 0) %>
<% end %>
</td>
<td>
Expand Down
Loading
Loading