Skip to content

Commit 52915cc

Browse files
naveed-ahmadclaude
andauthored
Related gapped and gapless audio (#642)
* Add audio reprocess pipeline design spec Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * add basic regex based Quran search * add test cases * use linear regexp * Related gapped and gapless audio --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b675ce1 commit 52915cc

10 files changed

Lines changed: 111 additions & 22 deletions

app/admin/audio/audio_file.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,40 @@
8282
row :created_at
8383
row :updated_at
8484
end
85+
86+
panel 'Audio URLs by source' do
87+
resource_content = resource.recitation&.resource_content
88+
sources = resource_content&.audio_sources || []
89+
90+
table do
91+
thead do
92+
tr do
93+
th 'Source'
94+
th 'URL'
95+
end
96+
end
97+
98+
tbody do
99+
tr do
100+
td 'default'
101+
td do
102+
link_to resource.audio_url, resource.audio_url, target: '_blank', rel: 'noopener' if resource.audio_url.present?
103+
end
104+
end
105+
106+
sources.each do |source, mapping|
107+
tr do
108+
td source
109+
td do
110+
url = resource.get_audio_url(source: mapping)
111+
link_to url, url, target: '_blank', rel: 'noopener' if url.present?
112+
end
113+
end
114+
end
115+
end
116+
end
117+
end
118+
85119
active_admin_comments
86120
end
87121

app/admin/audio/audio_recitation.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
:recitation_style_id,
1919
:qirat_type_id,
2020
:reciter_id,
21+
:gapped_recitation_id,
2122
:segment_locked
2223

2324
scope :all
@@ -233,6 +234,11 @@
233234
row :resource_content do
234235
resource.get_resource_content
235236
end
237+
row :gapped_recitation do
238+
if resource.gapped_recitation
239+
link_to resource.gapped_recitation.humanize, [:cms, resource.gapped_recitation]
240+
end
241+
end
236242
row :recitation_style
237243
row :qirat_type
238244
row :arabic_name
@@ -327,6 +333,9 @@
327333
f.input :qirat_type_id,
328334
as: :searchable_select,
329335
ajax: { resource: QiratType }
336+
f.input :gapped_recitation_id,
337+
as: :searchable_select,
338+
ajax: { resource: Recitation }
330339
end
331340

332341
f.actions

app/admin/audio/recitation.rb

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
recitation_style_id
134134
resource_content_id
135135
qirat_type_id
136+
gapless_recitation_id
136137
style
137138
segment_locked
138139
reciter_name
@@ -174,13 +175,36 @@
174175
end
175176
end
176177

178+
row :gapless_recitation do |r|
179+
if r.gapless_recitation
180+
link_to r.gapless_recitation.humanize, [:cms, r.gapless_recitation]
181+
end
182+
end
183+
177184
row :approved do
178185
resource.approved?
179186
end
180187
row :segment_locked
181188
row :created_at
182189
row :updated_at
183190
end
191+
192+
panel "Audio files (#{resource.audio_files.size})" do
193+
table_for resource.audio_files.order(:chapter_id, :verse_number) do
194+
column :id do |audio_file|
195+
link_to audio_file.id, [:cms, audio_file]
196+
end
197+
column :verse_key do |audio_file|
198+
audio_file.verse_key
199+
end
200+
column :audio_url do |audio_file|
201+
if audio_file.audio_url.present?
202+
link_to audio_file.audio_url, audio_file.audio_url, target: '_blank', rel: 'noopener'
203+
end
204+
end
205+
end
206+
end
207+
184208
active_admin_comments
185209
end
186210

@@ -201,18 +225,15 @@
201225
f.input :qirat_type_id,
202226
as: :searchable_select,
203227
ajax: { resource: QiratType }
228+
f.input :gapless_recitation_id,
229+
as: :searchable_select,
230+
ajax: { resource: Audio::Recitation }
204231
f.input :segment_locked
205232
end
206233

207234
f.actions
208235
end
209236

210-
sidebar 'Audio files', only: :show do
211-
div do
212-
link_to 'View audio files', "/cms/audio_files?utf8=✓&q%5Brecitation_id_eq%5D=#{resource.id}"
213-
end
214-
end
215-
216237
member_action :download_segments, method: ['get', 'put'] do
217238
authorize! :download, :from_admin
218239

app/admin/content/resource_content.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,9 @@
449449
div do
450450
if resource.translation?
451451
if resource.one_word?
452-
link_to 'WBW Translations', "/cms/word_translations?q%5Bresource_content_id_eq%5D==#{resource.id}"
452+
link_to 'WBW Translations', "/cms/word_translations?q%5Bresource_content_id_eq%5D=#{resource.id}"
453453
else
454-
link_to 'Translations', "/cms/translations?q%5Bresource_content_id_eq%5D==#{resource.id}"
454+
link_to 'Translations', "/cms/translations?q%5Bresource_content_id_eq%5D=#{resource.id}"
455455
end
456456
elsif resource.tafsir?
457457
link_to 'Tafsir', "/cms/tafsirs?q%5Bresource_content_id_eq%5D=#{resource.id}&order=id_desc&commit=Filter"

app/models/audio/recitation.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class Recitation < QuranApiRecord
4949
has_many :related_recitations, class_name: 'Audio::RelatedRecitation', foreign_key: :audio_recitation_id, dependent: :delete_all
5050
has_many :audio_change_logs, class_name: 'Audio::ChangeLog', foreign_key: :audio_recitation_id, dependent: :delete_all
5151
has_many :audio_segments, class_name: 'Audio::Segment', foreign_key: :audio_recitation_id, dependent: :delete_all
52+
has_one :gapped_recitation, class_name: 'Recitation', foreign_key: :gapless_recitation_id, dependent: :nullify
5253

5354
belongs_to :section, class_name: 'Audio::Section', optional: true
5455
belongs_to :recitation_style, optional: true
@@ -292,7 +293,6 @@ def update_audio_stats
292293
end
293294

294295
protected
295-
296296
def update_related_resources
297297
if get_resource_content.nil?
298298
resource = build_resource_content

app/models/audio_file.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ def file_name
101101
url.split('/').last
102102
end
103103

104+
def get_audio_url(source:)
105+
cdn = source[:cdn]
106+
path = source[:audio_path]
107+
name = "#{chapter_id.to_s.rjust(3, '0')}#{verse_number.to_s.rjust(3, '0')}.mp3"
108+
109+
"#{cdn}/#{path}/#{name}"
110+
end
111+
104112
def segment_progress
105113
if segments_count.to_i.zero?
106114
0

app/models/recitation.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ class Recitation < QuranApiRecord
3131
belongs_to :recitation_style
3232
belongs_to :qirat_type, optional: true
3333
belongs_to :resource_content, optional: true
34+
belongs_to :gapless_recitation, class_name: 'Audio::Recitation', optional: true
3435

3536
has_many :audio_files
37+
3638
alias get_resource_content resource_content
3739

3840
scope :approved, -> { where(approved: true) }

app/models/resource_content.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,24 @@ module SubType
196196
RootDetail = 'root-detail'
197197
end
198198

199+
def audio_sources
200+
meta = fetch_metadata
201+
sources = meta&.keys
202+
.to_a
203+
.select { |key| key.to_s.include?('cdn') }
204+
205+
source_mapping = {}
206+
sources.each do |cdn_key|
207+
source = cdn_key.split('-').first
208+
209+
source_mapping[source] ||= {}
210+
source_mapping[source][:cdn] = meta_value(cdn_key)
211+
source_mapping[source][:audio_path] = meta_value("#{source}-path")
212+
end
213+
214+
source_mapping
215+
end
216+
199217
def get_language_name
200218
language_name.presence || language&.name
201219
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class AddGaplessRecitationToRecitations < ActiveRecord::Migration[8.0]
2+
def change
3+
c = Recitation.connection
4+
5+
c.add_column :recitations, :gapless_recitation_id, :integer, if_not_exists: true
6+
c.add_index :recitations, :gapless_recitation_id, unique: true, if_not_exists: true
7+
end
8+
end

db/schema.rb

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[8.0].define(version: 2026_05_18_000000) do
13+
ActiveRecord::Schema[8.0].define(version: 2026_06_23_000000) do
1414
# These are extensions that must be enabled in order to support this database
1515
enable_extension "pg_catalog.plpgsql"
1616

@@ -530,10 +530,10 @@
530530

531531
create_table "synonyms", force: :cascade do |t|
532532
t.string "text"
533-
t.text "synonyms"
534533
t.datetime "created_at", precision: nil, null: false
535534
t.datetime "updated_at", precision: nil, null: false
536535
t.jsonb "approved_synonyms", default: []
536+
t.jsonb "synonyms", default: []
537537
end
538538

539539
create_table "uloom_contents", force: :cascade do |t|
@@ -629,17 +629,6 @@
629629
t.index ["reviewed"], name: "index_versions_on_reviewed"
630630
end
631631

632-
create_table "word_mistakes", force: :cascade do |t|
633-
t.integer "word_id", null: false
634-
t.integer "mistake_count", default: 0, null: false
635-
t.integer "char_start"
636-
t.integer "char_end"
637-
t.datetime "created_at", null: false
638-
t.datetime "updated_at", null: false
639-
t.index ["word_id", "char_start", "char_end"], name: "index_word_mistakes_on_word_id_and_char_start_and_char_end"
640-
t.index ["word_id"], name: "index_word_mistakes_on_word_id"
641-
end
642-
643632
create_table "word_synonyms", force: :cascade do |t|
644633
t.integer "synonym_id"
645634
t.integer "word_id"

0 commit comments

Comments
 (0)