multimedia_paradise 1.1.344

Sign up to get free protection for your applications and to get access to all the features.
Files changed (382) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +2720 -0
  3. data/bin/audio_player +12 -0
  4. data/bin/auto_title +7 -0
  5. data/bin/convert_audio_to_mp4video_with_image +7 -0
  6. data/bin/cut_audio +7 -0
  7. data/bin/cut_multimedia +7 -0
  8. data/bin/extract_audio +7 -0
  9. data/bin/extract_images_from_this_video_file +7 -0
  10. data/bin/ffmpeg_merge +7 -0
  11. data/bin/file_duration +7 -0
  12. data/bin/gtk_radio +7 -0
  13. data/bin/has_audio +7 -0
  14. data/bin/loop_this_video +7 -0
  15. data/bin/merge_avi_files +7 -0
  16. data/bin/merge_mp3 +7 -0
  17. data/bin/missing_video_files +7 -0
  18. data/bin/movie_searcher +7 -0
  19. data/bin/mp3_to_opus +7 -0
  20. data/bin/mpg_to_mp4 +7 -0
  21. data/bin/mplayer_wrapper +7 -0
  22. data/bin/multimedia_information +11 -0
  23. data/bin/multimedia_paradise +9 -0
  24. data/bin/multimedia_paradise_sinatra +7 -0
  25. data/bin/play_random_simpsons_video +7 -0
  26. data/bin/playlist +7 -0
  27. data/bin/random_video +7 -0
  28. data/bin/remove_audio +9 -0
  29. data/bin/remove_last_second +7 -0
  30. data/bin/remove_subtitles +7 -0
  31. data/bin/reverse_video +7 -0
  32. data/bin/rubio +58 -0
  33. data/bin/tag_mp3_files +7 -0
  34. data/bin/to_aiff +10 -0
  35. data/bin/to_mp4 +7 -0
  36. data/bin/verbose_analyse_this_mp3_file_for_id3_tags +9 -0
  37. data/bin/video_codec +7 -0
  38. data/bin/video_information +8 -0
  39. data/bin/video_player +11 -0
  40. data/bin/video_thumbnail +7 -0
  41. data/bin/video_to_images +7 -0
  42. data/bin/vp9 +9 -0
  43. data/bin/waveform +73 -0
  44. data/doc/CHANGELOG.md +44 -0
  45. data/doc/LINKS.md +6 -0
  46. data/doc/MOTIVATION_FOR_THIS_PROJECT.md +22 -0
  47. data/doc/MergingVideoLectures.md +12 -0
  48. data/doc/README.gen +2658 -0
  49. data/doc/Readme_for_the_cut_audio_component.md +168 -0
  50. data/doc/todo/todo_for_the_GUIs.md +0 -0
  51. data/doc/todo/todo_for_the_multimedia_paradise_project.md +111 -0
  52. data/lib/multimedia_paradise/audio/audio_player/audio_player.rb +1143 -0
  53. data/lib/multimedia_paradise/audio/audio_tag_reader/README.md +7 -0
  54. data/lib/multimedia_paradise/audio/audio_tag_reader/audio_tag_reader.rb +503 -0
  55. data/lib/multimedia_paradise/audio/base.rb +67 -0
  56. data/lib/multimedia_paradise/audio/compress.rb +97 -0
  57. data/lib/multimedia_paradise/audio/create_m3u_playlist.rb +256 -0
  58. data/lib/multimedia_paradise/audio/extract_audio/constants.rb +37 -0
  59. data/lib/multimedia_paradise/audio/extract_audio/extract_audio.rb +417 -0
  60. data/lib/multimedia_paradise/audio/file_duration/constants.rb +53 -0
  61. data/lib/multimedia_paradise/audio/file_duration/file_duration.rb +606 -0
  62. data/lib/multimedia_paradise/audio/genres/boogie.rb +35 -0
  63. data/lib/multimedia_paradise/audio/genres/concerts.rb +35 -0
  64. data/lib/multimedia_paradise/audio/genres/constants.rb +53 -0
  65. data/lib/multimedia_paradise/audio/genres/eurodance.rb +35 -0
  66. data/lib/multimedia_paradise/audio/genres/genre.rb +696 -0
  67. data/lib/multimedia_paradise/audio/genres/hip_hop.rb +35 -0
  68. data/lib/multimedia_paradise/audio/genres/italian_songs.rb +35 -0
  69. data/lib/multimedia_paradise/audio/genres/the_1980s.rb +35 -0
  70. data/lib/multimedia_paradise/audio/genres/trance.rb +35 -0
  71. data/lib/multimedia_paradise/audio/lyrics_fetcher.rb +238 -0
  72. data/lib/multimedia_paradise/audio/merge_audio_files.rb +114 -0
  73. data/lib/multimedia_paradise/audio/modify_year_of_audio_file.rb +136 -0
  74. data/lib/multimedia_paradise/audio/n_audio_songs.rb +208 -0
  75. data/lib/multimedia_paradise/audio/play_all_audio_files.rb +122 -0
  76. data/lib/multimedia_paradise/audio/playlist/playlist.rb +1636 -0
  77. data/lib/multimedia_paradise/audio/remove_audio.rb +98 -0
  78. data/lib/multimedia_paradise/audio/remove_last_second.rb +83 -0
  79. data/lib/multimedia_paradise/audio/report_missing_id.rb +131 -0
  80. data/lib/multimedia_paradise/audio/streamripper/constants.rb +51 -0
  81. data/lib/multimedia_paradise/audio/streamripper/streamripper_wrapper.rb +279 -0
  82. data/lib/multimedia_paradise/audio/to_mp3.rb +138 -0
  83. data/lib/multimedia_paradise/audio/to_ogg.rb +104 -0
  84. data/lib/multimedia_paradise/audio/wav_to_mp3.rb +87 -0
  85. data/lib/multimedia_paradise/audio/waveform/class.rb +341 -0
  86. data/lib/multimedia_paradise/audio/waveform/constants.rb +38 -0
  87. data/lib/multimedia_paradise/audio/waveform/log.rb +101 -0
  88. data/lib/multimedia_paradise/autoinclude.rb +3 -0
  89. data/lib/multimedia_paradise/autoinclude_remove_audio.rb +6 -0
  90. data/lib/multimedia_paradise/base/base.rb +34 -0
  91. data/lib/multimedia_paradise/base/colours.rb +391 -0
  92. data/lib/multimedia_paradise/base/commandline_arguments.rb +60 -0
  93. data/lib/multimedia_paradise/base/constants.rb +19 -0
  94. data/lib/multimedia_paradise/base/encoding.rb +31 -0
  95. data/lib/multimedia_paradise/base/misc.rb +665 -0
  96. data/lib/multimedia_paradise/base/namespace.rb +36 -0
  97. data/lib/multimedia_paradise/base/time.rb +25 -0
  98. data/lib/multimedia_paradise/colours/colours.rb +89 -0
  99. data/lib/multimedia_paradise/commandline/menu.rb +463 -0
  100. data/lib/multimedia_paradise/configuration/play_zoomed.yml +1 -0
  101. data/lib/multimedia_paradise/constants/constants.rb +23 -0
  102. data/lib/multimedia_paradise/constants/conversions.rb +62 -0
  103. data/lib/multimedia_paradise/constants/directory_constants.rb +139 -0
  104. data/lib/multimedia_paradise/constants/encodings.rb +26 -0
  105. data/lib/multimedia_paradise/constants/file_constants.rb +176 -0
  106. data/lib/multimedia_paradise/constants/misc.rb +80 -0
  107. data/lib/multimedia_paradise/constants/my_video_directory.rb +30 -0
  108. data/lib/multimedia_paradise/constants/namespace.rb +14 -0
  109. data/lib/multimedia_paradise/constants/newline.rb +14 -0
  110. data/lib/multimedia_paradise/constants/video_filetypes.rb +27 -0
  111. data/lib/multimedia_paradise/constants/web_constants.rb +150 -0
  112. data/lib/multimedia_paradise/conversions/README.md +2 -0
  113. data/lib/multimedia_paradise/conversions/conversions.rb +930 -0
  114. data/lib/multimedia_paradise/css/project.css +15 -0
  115. data/lib/multimedia_paradise/data/asoundrc +12 -0
  116. data/lib/multimedia_paradise/ffmpeg/README.md +2 -0
  117. data/lib/multimedia_paradise/ffmpeg/ffmpeg.rb +620 -0
  118. data/lib/multimedia_paradise/gui/fox/play_from_radio_station.rb +75 -0
  119. data/lib/multimedia_paradise/gui/glimmer/rubio/model/bookmark.rb +61 -0
  120. data/lib/multimedia_paradise/gui/glimmer/rubio/model/player.rb +226 -0
  121. data/lib/multimedia_paradise/gui/glimmer/rubio/model/radio_browser.rb +57 -0
  122. data/lib/multimedia_paradise/gui/glimmer/rubio/model/radio_presenter.rb +132 -0
  123. data/lib/multimedia_paradise/gui/glimmer/rubio/model/station.rb +48 -0
  124. data/lib/multimedia_paradise/gui/glimmer/rubio/view/radio.rb +296 -0
  125. data/lib/multimedia_paradise/gui/gosu/video_player/video_player.rb +109 -0
  126. data/lib/multimedia_paradise/gui/gtk2/multimedia_converter/multimedia_converter.rb +33 -0
  127. data/lib/multimedia_paradise/gui/gtk2/notebook.rb +144 -0
  128. data/lib/multimedia_paradise/gui/gtk2/play_video_from_my_collection/play_video_from_my_collection.rb +43 -0
  129. data/lib/multimedia_paradise/gui/gtk2/simple_play_widget/README.md +12 -0
  130. data/lib/multimedia_paradise/gui/gtk2/simple_play_widget/simple_play_widget.rb +40 -0
  131. data/lib/multimedia_paradise/gui/gtk2/widget_increase_or_decrease_audio/widget_increase_or_decrease_audio.rb +42 -0
  132. data/lib/multimedia_paradise/gui/gtk2/youtube_downloader/youtube_downloader.rb +32 -0
  133. data/lib/multimedia_paradise/gui/gtk3/lyrics/lyrics.rb +0 -0
  134. data/lib/multimedia_paradise/gui/gtk3/multimedia_converter/multimedia_converter.rb +34 -0
  135. data/lib/multimedia_paradise/gui/gtk3/multimedia_notebook/multimedia_notebook.rb +34 -0
  136. data/lib/multimedia_paradise/gui/gtk3/play_video_from_my_collection/play_video_from_my_collection.rb +34 -0
  137. data/lib/multimedia_paradise/gui/gtk3/playlist/playlist.rb +34 -0
  138. data/lib/multimedia_paradise/gui/gtk3/simple_play_widget/simple_play_widget.rb +38 -0
  139. data/lib/multimedia_paradise/gui/gtk3/video_editor/video_editor.rb +34 -0
  140. data/lib/multimedia_paradise/gui/gtk3/video_player/video_player.rb +34 -0
  141. data/lib/multimedia_paradise/gui/gtk3/webcam_widget/webcam_widget.rb +425 -0
  142. data/lib/multimedia_paradise/gui/gtk3/widget_increase_or_decrease_audio/widget_increase_or_decrease_audio.rb +36 -0
  143. data/lib/multimedia_paradise/gui/gtk3/youtube_channels/youtube_channels.rb +210 -0
  144. data/lib/multimedia_paradise/gui/gtk3/youtube_downloader/youtube_downloader.rb +32 -0
  145. data/lib/multimedia_paradise/gui/gui_base.rb +92 -0
  146. data/lib/multimedia_paradise/gui/libui/change_metadata_widget/change_metadata_widget.rb +115 -0
  147. data/lib/multimedia_paradise/gui/libui/cut_multimedia/cut_multimedia.rb +119 -0
  148. data/lib/multimedia_paradise/gui/libui/lyrics/lyrics.rb +67 -0
  149. data/lib/multimedia_paradise/gui/libui/simple_play_widget/simple_play_widget.rb +119 -0
  150. data/lib/multimedia_paradise/gui/libui/video_player/video_player.rb +81 -0
  151. data/lib/multimedia_paradise/gui/libui/widget_increase_or_decrease_audio/widget_increase_or_decrease_audio.rb +126 -0
  152. data/lib/multimedia_paradise/gui/libui/youtube_channels/youtube_channels.rb +116 -0
  153. data/lib/multimedia_paradise/gui/libui/youtube_downloader/youtube_downloader.rb +107 -0
  154. data/lib/multimedia_paradise/gui/shared_code/lyrics/lyrics_module.rb +0 -0
  155. data/lib/multimedia_paradise/gui/shared_code/multimedia_converter/multimedia_converter_module.rb +478 -0
  156. data/lib/multimedia_paradise/gui/shared_code/multimedia_notebook/multimedia_notebook_module.rb +171 -0
  157. data/lib/multimedia_paradise/gui/shared_code/play_video_from_my_collection/play_video_from_my_collection_module.rb +464 -0
  158. data/lib/multimedia_paradise/gui/shared_code/playlist/playlist_module.rb +151 -0
  159. data/lib/multimedia_paradise/gui/shared_code/simple_play_widget/simple_play_widget_module.rb +257 -0
  160. data/lib/multimedia_paradise/gui/shared_code/sound_effect_widget/sound_effect_widget_module.rb +0 -0
  161. data/lib/multimedia_paradise/gui/shared_code/video_editor/video_editor_module.rb +287 -0
  162. data/lib/multimedia_paradise/gui/shared_code/video_player/video_player_module.rb +185 -0
  163. data/lib/multimedia_paradise/gui/shared_code/widget_increase_or_decrease_audio/widget_increase_or_decrease_audio_module.rb +310 -0
  164. data/lib/multimedia_paradise/gui/shared_code/youtube_downloader/youtube_downloader_module.rb +339 -0
  165. data/lib/multimedia_paradise/gui/tk/tk_multimedia_wrapper.rb +60 -0
  166. data/lib/multimedia_paradise/gui/universal_widgets/change_metadata_widget/README.md +3 -0
  167. data/lib/multimedia_paradise/gui/universal_widgets/change_metadata_widget/change_metadata_widget.rb +751 -0
  168. data/lib/multimedia_paradise/gui/universal_widgets/cut_multimedia/cut_multimedia.rb +657 -0
  169. data/lib/multimedia_paradise/gui/universal_widgets/information_about_a_mp3_file/information_about_a_mp3_file.rb +597 -0
  170. data/lib/multimedia_paradise/gui/universal_widgets/lyrics/lyrics.rb +265 -0
  171. data/lib/multimedia_paradise/gui/universal_widgets/radio/radio.rb +1364 -0
  172. data/lib/multimedia_paradise/gui/universal_widgets/sound_effect_widget/sound_effect_widget.rb +265 -0
  173. data/lib/multimedia_paradise/gui/universal_widgets/tag_mp3_files/tag_mp3_files.config +6 -0
  174. data/lib/multimedia_paradise/gui/universal_widgets/tag_mp3_files/tag_mp3_files.rb +2429 -0
  175. data/lib/multimedia_paradise/gui/universal_widgets/video_collection/video_collection.rb +246 -0
  176. data/lib/multimedia_paradise/help/help.rb +75 -0
  177. data/lib/multimedia_paradise/images/MULTIMEDIA.jpg +0 -0
  178. data/lib/multimedia_paradise/images/MULTIMEDIA_PARADISE_ID_TAG_GUI_IN_RUBY_GTK3.png +0 -0
  179. data/lib/multimedia_paradise/images/MULTIMEDIA_PARADISE_LOGO.png +0 -0
  180. data/lib/multimedia_paradise/images/UK_flag.png +0 -0
  181. data/lib/multimedia_paradise/images/US_flag.png +0 -0
  182. data/lib/multimedia_paradise/images/austrian_flag.png +0 -0
  183. data/lib/multimedia_paradise/images/french_flag.png +0 -0
  184. data/lib/multimedia_paradise/images/german_flag.png +0 -0
  185. data/lib/multimedia_paradise/images/radio_image.png +0 -0
  186. data/lib/multimedia_paradise/images/trance.png +0 -0
  187. data/lib/multimedia_paradise/java/AudioPlayer.class +0 -0
  188. data/lib/multimedia_paradise/java/AudioPlayer.java +103 -0
  189. data/lib/multimedia_paradise/java/README.md +3 -0
  190. data/lib/multimedia_paradise/misc/long_format_to_milliseconds_converter.rb +182 -0
  191. data/lib/multimedia_paradise/misc/milliseconds_to_long_format_converter.rb +186 -0
  192. data/lib/multimedia_paradise/multimedia/analyse_multimedia_file.rb +180 -0
  193. data/lib/multimedia_paradise/multimedia/avisynth/README.md +4 -0
  194. data/lib/multimedia_paradise/multimedia/avisynth/avisynth_code.avs +471 -0
  195. data/lib/multimedia_paradise/multimedia/base.rb +60 -0
  196. data/lib/multimedia_paradise/multimedia/chord.rb +174 -0
  197. data/lib/multimedia_paradise/multimedia/convert_audio_to_video_with_image.rb +171 -0
  198. data/lib/multimedia_paradise/multimedia/cut_multimedia/10_minutes_chop.rb +29 -0
  199. data/lib/multimedia_paradise/multimedia/cut_multimedia/15_minutes_chop.rb +29 -0
  200. data/lib/multimedia_paradise/multimedia/cut_multimedia/30_minutes_chop.rb +29 -0
  201. data/lib/multimedia_paradise/multimedia/cut_multimedia/5_minutes_chop.rb +29 -0
  202. data/lib/multimedia_paradise/multimedia/cut_multimedia/cut_multimedia.rb +2021 -0
  203. data/lib/multimedia_paradise/multimedia/cut_multimedia/evaluate_from_this_file.rb +79 -0
  204. data/lib/multimedia_paradise/multimedia/cut_multimedia/interactive_menu.rb +525 -0
  205. data/lib/multimedia_paradise/multimedia/interactive_shell.rb +84 -0
  206. data/lib/multimedia_paradise/multimedia/merge_multimedia.rb +127 -0
  207. data/lib/multimedia_paradise/multimedia/note.rb +500 -0
  208. data/lib/multimedia_paradise/multimedia/play_from_this_list.rb +163 -0
  209. data/lib/multimedia_paradise/multimedia/read_meta_tags.rb +173 -0
  210. data/lib/multimedia_paradise/multimedia/simulate_youtube_playlist.rb +209 -0
  211. data/lib/multimedia_paradise/multimedia/start_length_duration.rb +205 -0
  212. data/lib/multimedia_paradise/multimedia/video_downloader/video_downloader.rb +135 -0
  213. data/lib/multimedia_paradise/project/project.rb +47 -0
  214. data/lib/multimedia_paradise/requires/require_audio_files.rb +74 -0
  215. data/lib/multimedia_paradise/requires/require_cut_multimedia.rb +7 -0
  216. data/lib/multimedia_paradise/requires/require_extract_audio.rb +7 -0
  217. data/lib/multimedia_paradise/requires/require_file_duration.rb +7 -0
  218. data/lib/multimedia_paradise/requires/require_streamripper.rb +7 -0
  219. data/lib/multimedia_paradise/requires/require_the_audio_player.rb +7 -0
  220. data/lib/multimedia_paradise/requires/require_the_multimedia_paradise_project.rb +38 -0
  221. data/lib/multimedia_paradise/requires/require_the_sinatra_components.rb +7 -0
  222. data/lib/multimedia_paradise/requires/require_toplevel_methods_files.rb +25 -0
  223. data/lib/multimedia_paradise/requires/require_video_files.rb +25 -0
  224. data/lib/multimedia_paradise/requires/require_video_player.rb +7 -0
  225. data/lib/multimedia_paradise/sinatra/app.rb +318 -0
  226. data/lib/multimedia_paradise/sinatra/embeddable_interface.rb +379 -0
  227. data/lib/multimedia_paradise/sound_effects/README.md +12 -0
  228. data/lib/multimedia_paradise/sound_effects/cat_meow.mp3 +0 -0
  229. data/lib/multimedia_paradise/sound_effects/channel_opened.mp3 +0 -0
  230. data/lib/multimedia_paradise/sound_effects/phone_ring.mp3 +0 -0
  231. data/lib/multimedia_paradise/statistics/README.md +5 -0
  232. data/lib/multimedia_paradise/statistics/video.rb +84 -0
  233. data/lib/multimedia_paradise/toplevel_cut_audio.rb +16 -0
  234. data/lib/multimedia_paradise/toplevel_methods/analyze_audio_stream.rb +31 -0
  235. data/lib/multimedia_paradise/toplevel_methods/chop_into_segments_of_n_seconds_size.rb +32 -0
  236. data/lib/multimedia_paradise/toplevel_methods/chop_off_first_five_minutes.rb +22 -0
  237. data/lib/multimedia_paradise/toplevel_methods/chop_off_first_n_seconds.rb +92 -0
  238. data/lib/multimedia_paradise/toplevel_methods/chop_off_first_ten_minutes.rb +22 -0
  239. data/lib/multimedia_paradise/toplevel_methods/chop_off_first_two_minutes.rb +24 -0
  240. data/lib/multimedia_paradise/toplevel_methods/codecs.rb +50 -0
  241. data/lib/multimedia_paradise/toplevel_methods/copy_and_merge_this_video_n_times.rb +51 -0
  242. data/lib/multimedia_paradise/toplevel_methods/copy_file.rb +18 -0
  243. data/lib/multimedia_paradise/toplevel_methods/create_video_from_this_audio.rb +65 -0
  244. data/lib/multimedia_paradise/toplevel_methods/cut_from_to.rb +156 -0
  245. data/lib/multimedia_paradise/toplevel_methods/delay_audio.rb +31 -0
  246. data/lib/multimedia_paradise/toplevel_methods/denoise.rb +90 -0
  247. data/lib/multimedia_paradise/toplevel_methods/deshake.rb +43 -0
  248. data/lib/multimedia_paradise/toplevel_methods/e.rb +16 -0
  249. data/lib/multimedia_paradise/toplevel_methods/encode_this_video.rb +49 -0
  250. data/lib/multimedia_paradise/toplevel_methods/ensure_that_the_output_directory_exists.rb +27 -0
  251. data/lib/multimedia_paradise/toplevel_methods/esystem.rb +43 -0
  252. data/lib/multimedia_paradise/toplevel_methods/files_and_directories.rb +92 -0
  253. data/lib/multimedia_paradise/toplevel_methods/flip_and_rotate.rb +58 -0
  254. data/lib/multimedia_paradise/toplevel_methods/has_audio.rb +48 -0
  255. data/lib/multimedia_paradise/toplevel_methods/increase_volume_of_this_audio_file.rb +61 -0
  256. data/lib/multimedia_paradise/toplevel_methods/is_a_multimedia_file.rb +27 -0
  257. data/lib/multimedia_paradise/toplevel_methods/is_audio_file.rb +27 -0
  258. data/lib/multimedia_paradise/toplevel_methods/is_image_file.rb +31 -0
  259. data/lib/multimedia_paradise/toplevel_methods/is_on_roebe.rb +17 -0
  260. data/lib/multimedia_paradise/toplevel_methods/is_video_file.rb +62 -0
  261. data/lib/multimedia_paradise/toplevel_methods/merge_multimedia_file.rb +331 -0
  262. data/lib/multimedia_paradise/toplevel_methods/merge_these_videos.rb +106 -0
  263. data/lib/multimedia_paradise/toplevel_methods/misc.rb +964 -0
  264. data/lib/multimedia_paradise/toplevel_methods/opn.rb +24 -0
  265. data/lib/multimedia_paradise/toplevel_methods/output_directory.rb +61 -0
  266. data/lib/multimedia_paradise/toplevel_methods/player_in_use.rb +80 -0
  267. data/lib/multimedia_paradise/toplevel_methods/query_the_audio_codec_of_this_file.rb +24 -0
  268. data/lib/multimedia_paradise/toplevel_methods/radio.rb +381 -0
  269. data/lib/multimedia_paradise/toplevel_methods/rds.rb +24 -0
  270. data/lib/multimedia_paradise/toplevel_methods/return_all_video_files.rb +64 -0
  271. data/lib/multimedia_paradise/toplevel_methods/return_data_from_video_collection_file_for_this_entry.rb +38 -0
  272. data/lib/multimedia_paradise/toplevel_methods/return_duration_of_this_multimedia_file.rb +27 -0
  273. data/lib/multimedia_paradise/toplevel_methods/return_full_name_for_video_at_this_position.rb +54 -0
  274. data/lib/multimedia_paradise/toplevel_methods/return_path_to_random_simpsons_video_file.rb +43 -0
  275. data/lib/multimedia_paradise/toplevel_methods/return_random_video_file_from_the_video_collection.rb +73 -0
  276. data/lib/multimedia_paradise/toplevel_methods/return_screen_resolution.rb +24 -0
  277. data/lib/multimedia_paradise/toplevel_methods/run_sys_command.rb +30 -0
  278. data/lib/multimedia_paradise/toplevel_methods/scale_video.rb +27 -0
  279. data/lib/multimedia_paradise/toplevel_methods/set_title_of.rb +65 -0
  280. data/lib/multimedia_paradise/toplevel_methods/slow_down_this_video_file.rb +33 -0
  281. data/lib/multimedia_paradise/toplevel_methods/start_screencast.rb +70 -0
  282. data/lib/multimedia_paradise/toplevel_methods/subtitles.rb +59 -0
  283. data/lib/multimedia_paradise/toplevel_methods/to_flac.rb +30 -0
  284. data/lib/multimedia_paradise/toplevel_methods/to_mp4.rb +24 -0
  285. data/lib/multimedia_paradise/toplevel_methods/total_duration.rb +35 -0
  286. data/lib/multimedia_paradise/toplevel_methods/use_lame_codec.rb +32 -0
  287. data/lib/multimedia_paradise/toplevel_methods/video_dataset.rb +76 -0
  288. data/lib/multimedia_paradise/version/version.rb +28 -0
  289. data/lib/multimedia_paradise/video/all_videos.rb +101 -0
  290. data/lib/multimedia_paradise/video/capture_screen.rb +285 -0
  291. data/lib/multimedia_paradise/video/check_numbers.rb +215 -0
  292. data/lib/multimedia_paradise/video/columbo/columbo.rb +139 -0
  293. data/lib/multimedia_paradise/video/copy_missing_video_files.rb +292 -0
  294. data/lib/multimedia_paradise/video/copy_random_video.rb +98 -0
  295. data/lib/multimedia_paradise/video/correct_video_numbers.rb +263 -0
  296. data/lib/multimedia_paradise/video/create_dvd.rb +50 -0
  297. data/lib/multimedia_paradise/video/create_video_thumbnails.rb +137 -0
  298. data/lib/multimedia_paradise/video/encode_video.rb +81 -0
  299. data/lib/multimedia_paradise/video/ffmpeg_options.rb +156 -0
  300. data/lib/multimedia_paradise/video/find_video/find_video.rb +668 -0
  301. data/lib/multimedia_paradise/video/fix_married_with_children_videos.rb +70 -0
  302. data/lib/multimedia_paradise/video/genres.rb +333 -0
  303. data/lib/multimedia_paradise/video/guess_video_name.rb +189 -0
  304. data/lib/multimedia_paradise/video/imdb_rating/imdb_rating.rb +133 -0
  305. data/lib/multimedia_paradise/video/mike_hammer/mike_hammer.rb +74 -0
  306. data/lib/multimedia_paradise/video/missing_video_files/missing_video_files.rb +286 -0
  307. data/lib/multimedia_paradise/video/movie_searcher.rb +281 -0
  308. data/lib/multimedia_paradise/video/mplayer_wrapper.rb +504 -0
  309. data/lib/multimedia_paradise/video/n_videos.rb +142 -0
  310. data/lib/multimedia_paradise/video/play_random_file.rb +91 -0
  311. data/lib/multimedia_paradise/video/play_random_realvideo.rb +92 -0
  312. data/lib/multimedia_paradise/video/prepare_video_lecture.rb +267 -0
  313. data/lib/multimedia_paradise/video/random_video.rb +302 -0
  314. data/lib/multimedia_paradise/video/registered_video_file.rb +131 -0
  315. data/lib/multimedia_paradise/video/remove_metadata.rb +52 -0
  316. data/lib/multimedia_paradise/video/rename_video_file.rb +149 -0
  317. data/lib/multimedia_paradise/video/report_local_videos.rb +106 -0
  318. data/lib/multimedia_paradise/video/report_missing_videos_in_the_yaml_file.rb +107 -0
  319. data/lib/multimedia_paradise/video/resolution.rb +194 -0
  320. data/lib/multimedia_paradise/video/reverse_video.rb +74 -0
  321. data/lib/multimedia_paradise/video/simpsons.rb +129 -0
  322. data/lib/multimedia_paradise/video/smart_animals/smart_animals.rb +188 -0
  323. data/lib/multimedia_paradise/video/speed_up_video.rb +135 -0
  324. data/lib/multimedia_paradise/video/split_this_video.rb +126 -0
  325. data/lib/multimedia_paradise/video/srt_regex.rb +211 -0
  326. data/lib/multimedia_paradise/video/store_available_video_files.rb +138 -0
  327. data/lib/multimedia_paradise/video/the_simpsons/README.md +9 -0
  328. data/lib/multimedia_paradise/video/the_simpsons/good_the_simpsons_episodes.rb +107 -0
  329. data/lib/multimedia_paradise/video/the_simpsons/the_simpsons.rb +87 -0
  330. data/lib/multimedia_paradise/video/video.rb +14 -0
  331. data/lib/multimedia_paradise/video/video_encoding_settings.rb +57 -0
  332. data/lib/multimedia_paradise/video/video_information.rb +445 -0
  333. data/lib/multimedia_paradise/video/video_metadata.rb +132 -0
  334. data/lib/multimedia_paradise/video/video_player.rb +580 -0
  335. data/lib/multimedia_paradise/video/video_renamer.rb +161 -0
  336. data/lib/multimedia_paradise/video/watermark.rb +282 -0
  337. data/lib/multimedia_paradise/video/youtube/youtube.rb +17 -0
  338. data/lib/multimedia_paradise/video/youtube_embedder.rb +235 -0
  339. data/lib/multimedia_paradise/yaml/audio_formats.yml +14 -0
  340. data/lib/multimedia_paradise/yaml/best_songs_in_an_era/eurodance/top20.yml +28 -0
  341. data/lib/multimedia_paradise/yaml/game_shows/ruck_zuck/ruck_zuck.yml +25 -0
  342. data/lib/multimedia_paradise/yaml/genres/1980s.yml +198 -0
  343. data/lib/multimedia_paradise/yaml/genres/README.md +5 -0
  344. data/lib/multimedia_paradise/yaml/genres/boogie.yml +68 -0
  345. data/lib/multimedia_paradise/yaml/genres/concerts.yml +29 -0
  346. data/lib/multimedia_paradise/yaml/genres/eurodance.yml +456 -0
  347. data/lib/multimedia_paradise/yaml/genres/hip_hop.yml +49 -0
  348. data/lib/multimedia_paradise/yaml/genres/italian_songs.yml +53 -0
  349. data/lib/multimedia_paradise/yaml/genres/trance.yml +167 -0
  350. data/lib/multimedia_paradise/yaml/good_horror_movies/good_horror_movies.yml +12 -0
  351. data/lib/multimedia_paradise/yaml/good_movie_actors/good_movie_actors.yml +6 -0
  352. data/lib/multimedia_paradise/yaml/image_formats.yml +8 -0
  353. data/lib/multimedia_paradise/yaml/internal/README.md +2 -0
  354. data/lib/multimedia_paradise/yaml/internal/installed_binaries.yml +31 -0
  355. data/lib/multimedia_paradise/yaml/lyrics.yml +6323 -0
  356. data/lib/multimedia_paradise/yaml/movie_reviews/the_hateful_eight_september_2016.md +305 -0
  357. data/lib/multimedia_paradise/yaml/music_genres.yml +51 -0
  358. data/lib/multimedia_paradise/yaml/playlist.yml +130 -0
  359. data/lib/multimedia_paradise/yaml/radio/README.md +5 -0
  360. data/lib/multimedia_paradise/yaml/radio/radio_stations.yml +359 -0
  361. data/lib/multimedia_paradise/yaml/song_tags.yml +818 -0
  362. data/lib/multimedia_paradise/yaml/tales_from_the_crypt/tales_from_the_crypt.yml +426 -0
  363. data/lib/multimedia_paradise/yaml/tom_and_jerry/README.md +2 -0
  364. data/lib/multimedia_paradise/yaml/tom_and_jerry/tom_and_jerry_episodes.yml +27 -0
  365. data/lib/multimedia_paradise/yaml/tv_channels/tv_channels.yml +46 -0
  366. data/lib/multimedia_paradise/yaml/use_this_video_player.yml +1 -0
  367. data/lib/multimedia_paradise/yaml/video/video.yml +26 -0
  368. data/lib/multimedia_paradise/yaml/video_collection/video_collection.yml +1933 -0
  369. data/lib/multimedia_paradise/yaml/video_encoding_settings.yml +62 -0
  370. data/lib/multimedia_paradise/yaml/video_filter_aliases.yml +221 -0
  371. data/lib/multimedia_paradise/yaml/youtube/alltagsgeschichte/alltagsgeschichte.yml +190 -0
  372. data/lib/multimedia_paradise/yaml/youtube/songs/README.md +3 -0
  373. data/lib/multimedia_paradise/yaml/youtube/songs/songs.yml +6 -0
  374. data/lib/multimedia_paradise/yaml/youtube_playlist/youtube_playlist.yml +6 -0
  375. data/lib/multimedia_paradise.rb +5 -0
  376. data/multimedia_paradise.gemspec +74 -0
  377. data/test/testing_audio_player.rb +15 -0
  378. data/test/testing_ffmpeg_options.rb +14 -0
  379. data/test/testing_file_duration.rb +17 -0
  380. data/test/testing_modify_year_of_audio_file.rb +14 -0
  381. data/test/testing_multimedia_paradise_project.rb +151 -0
  382. metadata +560 -0
@@ -0,0 +1,2429 @@
1
+ #!/usr/bin/ruby -w
2
+ # Encoding: UTF-8
3
+ # frozen_string_literal: true
4
+ # =========================================================================== #
5
+ # === MultimediaParadise::GUI::UniversalWidgets::TagMp3Files
6
+ #
7
+ # This class, a ruby-gtk widget, was originally written (and finished)
8
+ # in April 2007 - so a really, really long time ago.
9
+ #
10
+ # In November 2020 I decided to fix what was broken and update it to
11
+ # better coding standards. It should now work better than it did at
12
+ # one point in time, many years ago.
13
+ #
14
+ # In January 2021 I adapted the code for ruby-gtk3, and rewrote this
15
+ # application (almost) from scratch. The new interface is a bit better
16
+ # than the old ruby-gtk2 variant.
17
+ #
18
+ # In September 2021 the project was re-written and renamed again. Support
19
+ # for ruby-gtk2 was also dropped - it may be re-implemented one day, but
20
+ # for the time being we will focus only on ruby-gtk3. In October 2021
21
+ # the project was visually enhanced a bit and querying the MPEG layer
22
+ # was added as well.
23
+ #
24
+ # In January 2022 the widget was, again, rewritten - this was to fix
25
+ # a weird bug where the application was freezing.
26
+ #
27
+ # Usage example:
28
+ #
29
+ # MultimediaParadise::GUI::UniversalWidgets::TagMp3Files.new(ARGV)
30
+ #
31
+ # =========================================================================== #
32
+ # require 'multimedia_paradise/gui/universal_widgets/tag_mp3_files/tag_mp3_files.rb'
33
+ # =========================================================================== #
34
+ require 'multimedia_paradise/base/base.rb'
35
+
36
+ module MultimediaParadise
37
+
38
+ module GUI
39
+
40
+ module UniversalWidgets
41
+
42
+ class TagMp3Files < ::MultimediaParadise::Base # === MultimediaParadise::GUI::UniversalWidgets::TagMp3Files
43
+
44
+ require 'universal_widgets/base_module/base_module.rb'
45
+ include ::UniversalWidgets::BaseModule
46
+
47
+ require 'multimedia_paradise/constants/file_constants.rb'
48
+ require 'multimedia_paradise/constants/directory_constants.rb'
49
+ require 'multimedia_paradise/constants/encodings.rb'
50
+ require 'multimedia_paradise/constants/file_constants.rb'
51
+ require 'multimedia_paradise/audio/audio_tag_reader/audio_tag_reader.rb'
52
+ require 'multimedia_paradise/audio/file_duration/file_duration.rb'
53
+ require 'multimedia_paradise/gui/shared_code/information_about_a_mp3_file/information_about_a_mp3_file_module.rb'
54
+ require 'multimedia_paradise/gui/gtk3/information_about_a_mp3_file/information_about_a_mp3_file.rb'
55
+ require 'multimedia_paradise/toplevel_methods/is_audio_file.rb'
56
+ require 'multimedia_paradise/toplevel_methods/misc.rb'
57
+
58
+ include MultimediaParadise::GUI::InformationAboutAMp3FileModule
59
+
60
+ # ========================================================================= #
61
+ # === TITLE
62
+ # ========================================================================= #
63
+ TITLE = 'Tag .mp3 files'
64
+
65
+ # ========================================================================= #
66
+ # === WIDTH
67
+ # ========================================================================= #
68
+ WIDTH = '99% or minimum 1800px'
69
+
70
+ # ========================================================================= #
71
+ # === HEIGHT
72
+ # ========================================================================= #
73
+ HEIGHT = '85% or minimum 500px'
74
+
75
+ # ========================================================================= #
76
+ # === USE_THIS_FONT
77
+ # ========================================================================= #
78
+ USE_THIS_FONT = :hack_23
79
+
80
+ # ========================================================================= #
81
+ # === LARGER_FONT
82
+ # ========================================================================= #
83
+ LARGER_FONT = :hack_25
84
+
85
+ # ========================================================================= #
86
+ # === SMALLER_FONT
87
+ # ========================================================================= #
88
+ SMALLER_FONT = :hack_20
89
+
90
+ # ========================================================================= #
91
+ # === USE_THESE_CSS_RULES
92
+ # ========================================================================= #
93
+ USE_THESE_CSS_RULES = '
94
+ button {
95
+ border-color: steelblue;
96
+ border-width: 2px;
97
+ }
98
+
99
+ .button_class {
100
+ border: 2px solid steelblue;;
101
+ }
102
+
103
+ #fancy_separator separator {
104
+ border: 1px dotted lightgrey;
105
+ padding: 0px;
106
+ margin: 4px;
107
+ }
108
+
109
+ .save_button {
110
+ border: 3px dashed steelblue;
111
+ padding: 2px;
112
+ margin: 4px;
113
+ }
114
+
115
+ .fancy_combobox window.popup {
116
+ color: royalblue;
117
+ font-weight: bold;
118
+ border: 3px dashed lightgreen;
119
+ }
120
+
121
+ /* style the top header bar next */
122
+ #header_bar_on_top {
123
+ border: 2px dotted royalblue;
124
+ }
125
+ '
126
+ # ========================================================================= #
127
+ # === initialize
128
+ # ========================================================================= #
129
+ def initialize(
130
+ commandline_arguments = nil,
131
+ run_already = true
132
+ )
133
+ determine_the_GUI_to_be_used(commandline_arguments) # This must come first, even before reset().
134
+ reset
135
+ set_commandline_arguments(
136
+ commandline_arguments
137
+ )
138
+ on_delete_event_quit_the_application
139
+ run if run_already
140
+ end
141
+
142
+ # ========================================================================= #
143
+ # === reset (reset tag)
144
+ # ========================================================================= #
145
+ def reset
146
+ super() if respond_to?(:super)
147
+ reset_the_shared_module # This can come early.
148
+ reset_the_base_module
149
+ reset_the_internal_variables
150
+ infer_the_namespace
151
+ # ======================================================================= #
152
+ # === @configuration
153
+ # ======================================================================= #
154
+ @configuration = [true, __dir__, namespace?]
155
+ # ======================================================================= #
156
+ # === Set the title, width, height and the font in use.
157
+ # ======================================================================= #
158
+ title_width_height_font(TITLE, WIDTH, HEIGHT, USE_THIS_FONT)
159
+ infer_the_size_automatically
160
+ # ======================================================================= #
161
+ # === @information_about_a_mp3_file
162
+ # ======================================================================= #
163
+ @information_about_a_mp3_file = return_widget_information_about_a_mp3_file
164
+ # ======================================================================= #
165
+ # === @metadata_for_the_song
166
+ #
167
+ # This variable will change when the metadata of a song is analysed.
168
+ # It will contain the dataset of that song, including the audio
169
+ # tags, at a later point - whenever a .mp3 file is analysed.
170
+ # ======================================================================= #
171
+ @metadata_for_the_song = nil
172
+ require_the_appropriate_id_tagger
173
+ end
174
+
175
+ # ========================================================================= #
176
+ # === smaller_font?
177
+ # ========================================================================= #
178
+ def smaller_font?
179
+ SMALLER_FONT
180
+ end
181
+
182
+ # ========================================================================= #
183
+ # === handle_CSS_rules (CSS tag)
184
+ #
185
+ # This is currently only in use if gtk3 is in use.
186
+ # ========================================================================= #
187
+ def handle_CSS_rules
188
+ use_gtk_paradise_project_css_file
189
+ append_project_css_file
190
+ more_CSS_then_apply_it(USE_THESE_CSS_RULES)
191
+ end
192
+
193
+ # ========================================================================= #
194
+ # === reset_the_shared_module
195
+ #
196
+ # This method can be used for ruby-gtk3 and ruby-libui, among other
197
+ # toolkits.
198
+ # ========================================================================= #
199
+ def reset_the_shared_module
200
+ end
201
+
202
+ # ========================================================================= #
203
+ # === padding?
204
+ # ========================================================================= #
205
+ def padding?
206
+ 4
207
+ end
208
+
209
+ # ========================================================================= #
210
+ # === border_size?
211
+ # ========================================================================= #
212
+ def border_size?
213
+ 2
214
+ end
215
+
216
+ # ========================================================================= #
217
+ # === do_all_startup_related_actions
218
+ # ========================================================================= #
219
+ def do_all_startup_related_actions
220
+ menu # Must come after the skeleton has been created.
221
+ do_focus_on_the_entry_containing_the_title_of_the_song
222
+ end
223
+
224
+ # ========================================================================= #
225
+ # === require_the_appropriate_id_tagger
226
+ #
227
+ # This method will load the correct id-tagger for .mp3 files. We need
228
+ # to make this flexible, in the event that the user wants to use
229
+ # another id-tagger rather than the "default" one.
230
+ # ========================================================================= #
231
+ def require_the_appropriate_id_tagger(
232
+ i = ID_TAGGER_TO_USE_FOR_THE_MP3_FILES
233
+ )
234
+ case i
235
+ # ======================================================================= #
236
+ # === :taglib
237
+ #
238
+ # Note that since as of the year 2021, taglib is the default id3-tagger
239
+ # that will be used by this application, thus replacing the older
240
+ # id3lib code. The latter is still retained, though, in the event
241
+ # we may have to switch again one day. But in my opinion taglib is
242
+ # actually better than idlib.
243
+ # ======================================================================= #
244
+ when :taglib
245
+ begin
246
+ require 'taglib'
247
+ rescue LoadError
248
+ e "the taglib gem is not available. Please install it."
249
+ e
250
+ e ' gem install taglib-ruby # The github-page is at: '\
251
+ 'https://github.com/robinst/taglib-ruby'
252
+ e
253
+ if is_on_roebe?
254
+ e 'As we are on roebe we will install the gem next.'
255
+ e
256
+ system 'gem install taglib-ruby'
257
+ e
258
+ begin
259
+ require 'taglib'
260
+ rescue LoadError; end
261
+ end
262
+ end
263
+ # ======================================================================= #
264
+ # === when :idlib
265
+ # ======================================================================= #
266
+ when :idlib
267
+ begin
268
+ require 'id3lib'
269
+ rescue LoadError
270
+ e "ruby-id3lib not available. Please install it."
271
+ e
272
+ e ' gem install id3lib-ruby'
273
+ e
274
+ end
275
+ else
276
+ e "Unknown ID-tagger #{i}"
277
+ end
278
+ end
279
+
280
+ # ========================================================================= #
281
+ # === create_the_status_icon
282
+ # ========================================================================= #
283
+ def create_the_status_icon(
284
+ tooltip_text = 'This text will show up upon a hover-event.'
285
+ )
286
+ @status_icon = create_status_icon
287
+ @status_icon.stock = ::Gtk::Stock::DIALOG_INFO
288
+ @status_icon.set_icon_name('default_status_icon_name') # We can set a name.
289
+ @status_icon.set_tooltip_text(tooltip_text)
290
+ @status_icon.signal_connect(:activate) { |icon|
291
+ if icon.respond_to? :blinking
292
+ icon.blinking = !(icon.blinking?)
293
+ end
294
+ }
295
+ # =========================================================================== #
296
+ # Add a menu and a quit-button too. These will be triggered upon a
297
+ # right-mouse-button presse vent.
298
+ # =========================================================================== #
299
+ menu = create_menu
300
+
301
+ play_button = ::Gtk::ImageMenuItem.new(stock: ::Gtk::Stock::MEDIA_PLAY)
302
+ play_button.signal_connect(:activate) {
303
+ do_play_the_selected_song
304
+ }
305
+ menu.append(play_button)
306
+
307
+ quit_button = ::Gtk::ImageMenuItem.new(stock: ::Gtk::Stock::QUIT)
308
+ quit_button.signal_connect(:activate) { ::Gtk.main_quit }
309
+ menu.append(quit_button)
310
+
311
+ menu.show_all
312
+ @status_icon.signal_connect(:popup_menu) { |icon, button, time|
313
+ menu.popup(nil, nil, button, time)
314
+ }
315
+ end
316
+
317
+ # ========================================================================= #
318
+ # === create_the_buttons (buttons tag, button tag)
319
+ # ========================================================================= #
320
+ def create_the_buttons
321
+ # ======================================================================= #
322
+ # === @button_analyse_the_mp3_file
323
+ # ======================================================================= #
324
+ @button_analyse_the_mp3_file = button('_Analyse the .mp3 file')
325
+ @button_analyse_the_mp3_file.on_clicked {
326
+ do_analyse_the_mp3_file
327
+ }
328
+ # ======================================================================= #
329
+ # === @box_for_the_button_analyse_the_mp3_file
330
+ # ======================================================================= #
331
+ @box_for_the_button_analyse_the_mp3_file = create_hbox
332
+ @box_for_the_button_analyse_the_mp3_file.maximal(@button_analyse_the_mp3_file, 2)
333
+ @spinner = create_spinner
334
+ @box_for_the_button_analyse_the_mp3_file.minimal(@spinner, 4)
335
+ # ======================================================================= #
336
+ # === @button_assume
337
+ # ======================================================================= #
338
+ @button_assume = button('Assume')
339
+ @button_assume.hint = 'This button can be used to assume and infer '\
340
+ 'from the filename of the .mp3 file at hand '\
341
+ 'the artist and the name of the song. This will '\
342
+ 'only work for some songs, naturally, and you need '\
343
+ 'to have a file assigned prior to being able to '\
344
+ 'use this button.'
345
+ @button_assume.on_clicked {
346
+ do_assume_the_name_of_the_song_and_the_artist_from_the_filename_directly
347
+ }
348
+ # ======================================================================= #
349
+ # === @button_play
350
+ # ======================================================================= #
351
+ @button_play = return_button_play
352
+ # ======================================================================= #
353
+ # === @event_box_open_file
354
+ #
355
+ # This is a small "icon" that also makes use of an open-file functionality.
356
+ # ======================================================================= #
357
+ @event_box_open_file = return_event_box_for_opening_a_file
358
+ # ======================================================================= #
359
+ # === @button_debug
360
+ #
361
+ # The debug-button is created next.
362
+ # ======================================================================= #
363
+ @button_debug = create_button('_Debug')
364
+ @button_debug.on_hover(:lightgreen)
365
+ @button_debug.on_clicked {
366
+ load_the_id3_dataset_from_this_file
367
+ do_debug
368
+ }
369
+ @button_debug.hint = 'This button is just for quick debugging. '\
370
+ 'Most users will not need this button.'
371
+ # ======================================================================= #
372
+ # === @button_update
373
+ #
374
+ # The update button. It will update the id3 tags at hand, once pressed.
375
+ # ======================================================================= #
376
+ @button_update = create_button('_Update') # update tag
377
+ use_this_icon_name = 'software-update-available-symbolic.symbolic'
378
+ icon = load_icon(use_this_icon_name, 32, 0)
379
+ @button_update.set_image(icon)
380
+ @button_update.set_always_show_image(true)
381
+ @button_update.set_border_width(1)
382
+ @button_update.set_size_request(125, 68)
383
+ @button_update.on_hover(:lightgreen)
384
+ @button_update.hint = 'Press this button to '\
385
+ '<b>update the id3-tags</b> of the .mp3 file at hand.'
386
+ @button_update.on_clicked {
387
+ do_save_the_tags # Save the dataset upon a click-event.
388
+ }
389
+ # ======================================================================= #
390
+ # === @button_delete_the_tags
391
+ #
392
+ # The delete-the-tags-button.
393
+ # ======================================================================= #
394
+ @button_delete_the_tags = create_button('Delete the tags')
395
+ use_this_icon_name = 'edit-delete-symbolic.symbolic'
396
+ icon = load_icon(use_this_icon_name, 32, 0)
397
+ @button_delete_the_tags.set_image(icon)
398
+ @button_delete_the_tags.set_always_show_image(true)
399
+ @button_delete_the_tags.set_border_width(1)
400
+ @button_delete_the_tags.set_size_request(125, 68)
401
+ @button_delete_the_tags.hint =
402
+ "Click on this button to <b>remove all tags</b> "\
403
+ "from the given .mp3 file at hand. (The "\
404
+ "<b>save-button</b> works in precisely the same "\
405
+ "manner, by the way.)\n\nBe certain to use this only when you really"\
406
+ "want to remove all tags. Otherwise it may be better to ignore this "\
407
+ "button and instead modify the content, as-is, on the right-hand "\
408
+ "side."
409
+ @button_delete_the_tags.set_border_width(1)
410
+ @button_delete_the_tags.on_clicked {
411
+ do_empty_the_tags
412
+ }
413
+ @button_delete_the_tags.set_size_request(125, 68)
414
+ # ======================================================================= #
415
+ # === @button_save
416
+ #
417
+ # The save-button. It is located on the left-hand side of the main
418
+ # UI interface. This could be made inactive via a not_sensitive call.
419
+ # ======================================================================= #
420
+ @button_save = return_the_save_button(:do_not_make_use_of_a_special_CSS_class)
421
+ # ======================================================================= #
422
+ # === @button_close
423
+ # ======================================================================= #
424
+ @button_close = create_button_with_label(' _Close') # close tag
425
+ use_this_icon_name = 'application-exit'
426
+ icon = load_icon(use_this_icon_name, 32, 0) # Use the video-x-generic button.
427
+ @button_close.set_image(icon)
428
+ @button_close.set_always_show_image(true)
429
+ @button_close.set_border_width(1)
430
+ @button_close.set_size_request(125, 68)
431
+ @button_close.on_clicked { ::Gtk.main_quit }
432
+ @button_close.do_focus
433
+ @button_close.hint = 'Press this button to '\
434
+ '<b>exit the application</b>.'
435
+ # ======================================================================= #
436
+ # === @button_open_a_random_audio_file
437
+ # ======================================================================= #
438
+ @button_open_a_random_audio_file = return_button_open_a_random_audio_file
439
+ # ======================================================================= #
440
+ # === @button_perform_a_google_search
441
+ # ======================================================================= #
442
+ @button_perform_a_google_search = create_button_with_label(' _Google search')
443
+ use_this_icon_name = 'help-browser'
444
+ icon = load_icon(use_this_icon_name, 32, 0) # Use the video-x-generic button.
445
+ @button_perform_a_google_search.set_image(icon)
446
+ @button_perform_a_google_search.set_always_show_image(true)
447
+ @button_perform_a_google_search.set_border_width(1)
448
+ @button_perform_a_google_search.set_size_request(125, 68)
449
+ @button_perform_a_google_search.on_clicked {
450
+ do_perform_a_google_search
451
+ }
452
+ @button_perform_a_google_search.do_focus
453
+ @button_perform_a_google_search.hint = "Search via google "\
454
+ "for wikipedia-related entries about the assumed "\
455
+ "song and group.\n\nNote that this requires some entries to "\
456
+ "exist already, such as the name of the song, and ideally "\
457
+ "the name of the artist as well.\n\nThis functionality depends "\
458
+ "on the gem called '<b>open</b>', so install that gem before "\
459
+ "trying to use this button.\n"
460
+ end
461
+
462
+ # ========================================================================= #
463
+ # === create_the_header_bar (header tag)
464
+ # ========================================================================= #
465
+ def create_the_header_bar
466
+ # ======================================================================= #
467
+ # === @header_bar
468
+ # ======================================================================= #
469
+ @header_bar = widget_top
470
+ end
471
+
472
+ # ========================================================================= #
473
+ # === set_the_file_size_from_this_file
474
+ # ========================================================================= #
475
+ def set_the_file_size_from_this_file(
476
+ this_file = @entry_file_path.text?
477
+ )
478
+ @information_about_a_mp3_file.label_file_size_content.set_text(
479
+ "#{(File.size(this_file) / 1024.0 / 1000.0).round(2)} MB".
480
+ rjust(8)
481
+ )
482
+ end
483
+
484
+ # ========================================================================= #
485
+ # === sync_comment_onto_entry
486
+ # ========================================================================= #
487
+ def sync_comment_onto_entry
488
+ dataset = dataset?
489
+ if dataset
490
+ _ = dataset.comment.to_s.dup
491
+ # ===================================================================== #
492
+ # We have to use the proper encoding for the comment-field.
493
+ # ===================================================================== #
494
+ _ = _.force_encoding(::MultimediaParadise::UTF_ENCODING)
495
+ @entry_for_the_comment.set_text(_)
496
+ end
497
+ end
498
+
499
+ # ========================================================================= #
500
+ # === set_a_uniform_background_for_the_entries_on_the_right_side_widget
501
+ #
502
+ # This is currently (August 2021) not in use. I realized that it actually
503
+ # rather distracts then enhances. I keep it here if we want to
504
+ # re-enable it again at one moment in time.
505
+ # ========================================================================= #
506
+ def set_a_uniform_background_for_the_entries_on_the_right_side_widget(
507
+ array = return_all_entries_on_the_right_side_widget
508
+ )
509
+ array.each {|this_entry|
510
+ this_entry.clear_background
511
+ this_entry.very_light_yellowish_background
512
+ }
513
+ end
514
+
515
+ # ========================================================================= #
516
+ # === entry_use_which_audio_file?
517
+ # ========================================================================= #
518
+ def entry_use_which_audio_file?
519
+ @entry_file_path
520
+ end; alias entry_on_top? entry_use_which_audio_file? # === entry_on_top?
521
+ alias entry_file_path? entry_use_which_audio_file? # === entry_file_path?
522
+
523
+ # ========================================================================= #
524
+ # === the_save_button_is_available_again
525
+ #
526
+ # This is currently (January 2021) not in use. I am not sure whether
527
+ # it will be re-enabled, but for now the method will remain part of
528
+ # the widget.
529
+ # ========================================================================= #
530
+ def the_save_button_is_available_again
531
+ @button_save.is_sensitive_again
532
+ end; alias do_activate_the_save_button the_save_button_is_available_again # === do_activate_the_save_button
533
+
534
+ # ========================================================================= #
535
+ # === sync_title_onto_entry
536
+ # ========================================================================= #
537
+ def sync_title_onto_entry
538
+ dataset = dataset?
539
+ if dataset
540
+ @entry_for_the_title_of_the_song.set_text(dataset.title.to_s)
541
+ end
542
+ end
543
+
544
+ # ========================================================================= #
545
+ # === display_information_about_the_song (debug tag)
546
+ #
547
+ # This method exists mostly to simplify debugging the application at hand.
548
+ # ========================================================================= #
549
+ def display_information_about_the_song
550
+ # ======================================================================= #
551
+ # Get and set text frames with convenience methods.
552
+ # ======================================================================= #
553
+ e horizontal_unicode_bar*80
554
+ dataset = metadata?
555
+ if dataset
556
+ ee 'Title: '.ljust(30); pp dataset.title # => "Talk"
557
+ ee 'Album: '.ljust(30); pp dataset.album # new album setter
558
+ ee 'Performer: (not in use) '.ljust(30);pp dataset.performer # performer; this is not in use by this application though.
559
+ ee 'Artist: '.ljust(30); pp dataset.artist
560
+ ee 'Band: '.ljust(30); pp dataset.band
561
+ ee 'bpm: '.ljust(30); pp dataset.bpm
562
+ ee 'Date: '.ljust(30); pp dataset.date
563
+ ee 'Disc: '.ljust(30); pp dataset.disc
564
+ ee 'Genre: '.ljust(30); pp dataset.genre
565
+ ee 'time: '.ljust(30); pp dataset.time if dataset.respond_to? :time
566
+ ee 'track: '.ljust(30); pp dataset.track if dataset.respond_to? :track
567
+ ee 'language: '.ljust(30); pp dataset.language if dataset.respond_to? :language
568
+ ee 'Grouping: '.ljust(30); pp dataset.grouping if dataset.respond_to? :grouping
569
+ ee 'Encoded by: '.ljust(30);pp dataset.encoded_by if dataset.respond_to? :encoded_by
570
+ ee 'composer: '.ljust(30); pp dataset.composer if dataset.respond_to? :composer
571
+ ee 'Track: '.ljust(30); pp dataset.track if dataset.respond_to? :track # new track setter
572
+ ee 'Year: '.ljust(30); pp dataset.year # 2000, query year.
573
+ ee 'Comment: '.ljust(30); pp dataset.comment # Comments are listed there.
574
+ e horizontal_unicode_bar*80
575
+ e
576
+ else
577
+ e 'No dataset has been assigned yet.'
578
+ end
579
+ end; alias do_debug display_information_about_the_song # === do_debug
580
+
581
+ # ========================================================================= #
582
+ # === clear_old_entries
583
+ # ========================================================================= #
584
+ def clear_old_entries
585
+ @entry_for_the_album.set_text('')
586
+ @information_about_a_mp3_file.label_mpeg_level_content.set_text('')
587
+ end
588
+
589
+ # ========================================================================= #
590
+ # === filename?
591
+ # ========================================================================= #
592
+ def filename?
593
+ @entry_file_path.text?.to_s
594
+ end
595
+
596
+ # ========================================================================= #
597
+ # === do_assume_the_name_of_the_song_and_the_artist_from_the_filename_directly
598
+ # ========================================================================= #
599
+ def do_assume_the_name_of_the_song_and_the_artist_from_the_filename_directly
600
+ _ = filename?
601
+ if _ and File.exist?(_)
602
+ raw_name = File.basename(_)
603
+ # ===================================================================== #
604
+ # Get rid of the extension.
605
+ # ===================================================================== #
606
+ raw_name.delete_suffix!(
607
+ File.extname(raw_name)
608
+ )
609
+ if raw_name.include? '_'
610
+ splitted = raw_name.split('_')
611
+ artist_name = splitted.first.to_s
612
+ song_name = splitted.last.to_s
613
+ @entry_for_the_artist.set_text(artist_name)
614
+ entry_for_the_song_name?.set_text(song_name)
615
+ else
616
+ @entry_for_the_artist.set_text(raw_name)
617
+ end
618
+ else
619
+ e 'No file has been assigned yet. Please assign a file first.'
620
+ end
621
+ end
622
+
623
+ # ========================================================================= #
624
+ # === set_all_relevant_entries_based_on_the_metadata (sync tag)
625
+ #
626
+ # This method will sync all relevant entries into the corresponding
627
+ # gtk-entry at hand.
628
+ # ========================================================================= #
629
+ def set_all_relevant_entries_based_on_the_metadata(
630
+ this_file = main_file?
631
+ )
632
+ # ======================================================================= #
633
+ # First ensure that the file exists.
634
+ # ======================================================================= #
635
+ if this_file.nil? or !File.exist?(this_file)
636
+ e "No file exists at `#{sfile(this_file)}`."
637
+ _ = return_popover("No file exists at #{this_file}.", entry_on_top?)
638
+ _.popup
639
+ return
640
+ end
641
+ set_the_file_size_from_this_file
642
+ # ======================================================================= #
643
+ # Keep track of the song's duration as well.
644
+ # ======================================================================= #
645
+ sync_the_duration_of_the_file_onto_the_correct_entry
646
+ sync_title_onto_entry
647
+ sync_year_onto_entry
648
+ sync_artist_onto_entry
649
+ sync_genre_onto_entry
650
+ sync_comment_onto_entry
651
+ set_the_bitrate(
652
+ MultimediaParadise.bitrate_of_this_song(this_file)
653
+ )
654
+ set_the_samplerate(
655
+ MultimediaParadise.samplerate_of_this_song(this_file)
656
+ )
657
+ update_the_MPEG_entry(this_file)
658
+ end; alias sync_everything set_all_relevant_entries_based_on_the_metadata # === sync_everything
659
+
660
+ # ========================================================================= #
661
+ # === context_menu?
662
+ # ========================================================================= #
663
+ def context_menu?
664
+ @context_menu
665
+ end
666
+
667
+ # ========================================================================= #
668
+ # === create_the_context_menu
669
+ # ========================================================================= #
670
+ def create_the_context_menu
671
+ # ======================================================================= #
672
+ # === @context_menu
673
+ # ======================================================================= #
674
+ @context_menu = create_context_menu(self) {{
675
+ # n_entries: 12,
676
+ actions: {
677
+ '• Play the selected audio file via mpv': :play_the_selected_audio_file,
678
+ '• Display some debug-information on the commandline': :show_debug_information
679
+ }
680
+ }}
681
+ @context_menu.use_this_font(main_font?)
682
+ @context_menu.css_class('bblack3_and_padding')
683
+ end
684
+
685
+ # ========================================================================= #
686
+ # === create_the_status_bar
687
+ # ========================================================================= #
688
+ def create_the_status_bar
689
+ # ======================================================================= #
690
+ # === @status_bar
691
+ # ======================================================================= #
692
+ @status_bar = create_status_bar
693
+ @status_bar.bblack1
694
+ @status_bar_context_id = @status_bar.get_context_id('tag_mp3_files')
695
+ end
696
+
697
+ # ========================================================================= #
698
+ # === create_the_labels (labels tag, label tag)
699
+ # ========================================================================= #
700
+ def create_the_labels
701
+ # ======================================================================= #
702
+ # === @label_description
703
+ #
704
+ # This is the very top-level description.
705
+ # ======================================================================= #
706
+ @label_description = bold_label(
707
+ 'This widget can be used to modify the ID3 tags of .mp3 files'
708
+ )
709
+ @label_description.css_class('indigo')
710
+ @label_description.font_to_use = FONT_TO_USE_FOR_THE_MAIN_DESCRIPTION
711
+ end
712
+
713
+ # ========================================================================= #
714
+ # === create_the_settings
715
+ # ========================================================================= #
716
+ def create_the_settings
717
+ # ======================================================================= #
718
+ # === @settings_vbox
719
+ # ======================================================================= #
720
+ @settings_vbox = create_vbox
721
+ # ======================================================================= #
722
+ # Style this vbox a bit:
723
+ # ======================================================================= #
724
+ @settings_vbox.pad5px
725
+ @settings_vbox.set_border_width(20)
726
+ @settings_vbox.use_this_font = main_font?
727
+ _ = text("Settings\n")
728
+ _.use_this_font = main_font?
729
+ @settings_vbox.add(_)
730
+ @settings_vbox.add_hspacer
731
+ hbox = create_hbox
732
+ hbox.minimal(left_aligned_text('Specify the directory to the local songs here: '), 2)
733
+ hbox.minimal(@entry_use_this_as_the_directory_to_the_local_songs)
734
+ @settings_vbox << hbox
735
+ @settings_vbox.add_hspacer
736
+ @settings_vbox.use_this_font = main_font?
737
+ @settings_vbox.width_height(800, 300)
738
+ @settings_vbox.show_all
739
+ end
740
+
741
+ # ========================================================================= #
742
+ # === create_the_entries (entries tag, entry tag)
743
+ #
744
+ # This method will bundle - and create - all relevant gtk-entries
745
+ # in this application.
746
+ # ========================================================================= #
747
+ def create_the_entries
748
+ # ======================================================================= #
749
+ # === @entry_use_this_as_the_directory_to_the_local_songs
750
+ #
751
+ # This entry can be used to designate another directory for the
752
+ # local .mp3 files.
753
+ # ======================================================================= #
754
+ @entry_use_this_as_the_directory_to_the_local_songs = create_entry
755
+ @entry_use_this_as_the_directory_to_the_local_songs.set_text(
756
+ PATH_TO_THE_LOCAL_AUDIO_DIRECTORY
757
+ )
758
+ # ======================================================================= #
759
+ # === @entry_for_the_comment
760
+ # ======================================================================= #
761
+ @entry_for_the_comment = create_entry
762
+ @entry_for_the_comment.set_max_length(120)
763
+ @entry_for_the_comment.set_size_request(680, 40)
764
+ # ======================================================================= #
765
+ # === @entry_for_the_title_of_the_song
766
+ #
767
+ # The next entry contains the title of the song.
768
+ # ======================================================================= #
769
+ @entry_for_the_title_of_the_song = create_entry
770
+ @entry_for_the_title_of_the_song.set_max_length(150)
771
+ # ======================================================================= #
772
+ # === @entry_file_path
773
+ #
774
+ # The next entry, stored in @entry_file_path, will keep track
775
+ # of our audio-file. This is the most important entry.
776
+ # ======================================================================= #
777
+ @entry_file_path = create_entry
778
+ @entry_file_path.clear_background
779
+ @entry_file_path.pad5px
780
+ @entry_file_path.css_class('mar4px')
781
+ @entry_file_path.yellow_background
782
+ @entry_file_path.bblack2
783
+ @entry_file_path.make_bold
784
+ @entry_file_path.width_height(620, 42)
785
+ @entry_file_path.ghost_text('Provide the path to the .mp3 file here ...')
786
+ array = Dir['/home/x/songs/*'].map {|entry| File.basename(entry) }
787
+ @entry_file_path.simple_completion(array)
788
+ @entry_file_path.on_enter {
789
+ do_analyse_the_mp3_file
790
+ }
791
+ @entry_file_path.make_scrollable
792
+ @entry_file_path.signal_connect(:event) {|widget, event|
793
+ if scroll_up_event?(event)
794
+ do_assign_a_new_random_audio_file
795
+ elsif scroll_down_event?(event)
796
+ do_assign_a_new_random_audio_file
797
+ end
798
+ }
799
+ # ======================================================================= #
800
+ # If the user double-clicks on the file path then a new local file
801
+ # will be added.
802
+ # ======================================================================= #
803
+ @entry_file_path.on_mouse_double_click_event {|widget, event|
804
+ do_open_a_local_file_and_then_set_the_proper_entry
805
+ }
806
+ end
807
+
808
+ # ========================================================================= #
809
+ # === create_the_combo_box_entry_for_genre_select
810
+ # ========================================================================= #
811
+ def create_the_combo_box_entry_for_genre_select
812
+ if File.exist? FILE_MUSIC_GENRES
813
+ array_available_genres = YAML.load_file(FILE_MUSIC_GENRES)
814
+ else
815
+ array_available_genres = [] # Safeguard.
816
+ end
817
+ # ======================================================================= #
818
+ # === @combo_box_entry_for_genre_select
819
+ # ======================================================================= #
820
+ @combo_box_entry_for_genre_select = create_combo_box_entry(
821
+ with_these_elements: array_available_genres
822
+ )
823
+ @combo_box_entry_for_genre_select.clear_background
824
+ # @combo_box_entry_for_genre_select.bblack1
825
+ @combo_box_entry_for_genre_select.pad1px
826
+ @combo_box_entry_for_genre_select.css_class('fancy_combobox')
827
+ @combo_box_entry_for_genre_select.six_per_row
828
+ # ======================================================================= #
829
+ # Next we will unify the background for these entries here. Disabled
830
+ # in August 2021.
831
+ # ======================================================================= #
832
+ # set_a_uniform_background_for_the_entries_on_the_right_side_widget
833
+ # ======================================================================= #
834
+ # Next add the combo-box that contains all genres. We will not set
835
+ # an active entry, so that we can determine that this depends on
836
+ # the user having set it (or not, accordingly).
837
+ #
838
+ # @combo_box_entry_for_genre_select.active = 0
839
+ #
840
+ # ======================================================================= #
841
+ @combo_box_entry_for_genre_select.on_changed {
842
+ if @combo_box_entry_for_genre_select.active_iter
843
+ text = @combo_box_entry_for_genre_select.active_text
844
+ sync_this_text_onto_the_genre_entry(text)
845
+ end
846
+ }
847
+ end
848
+
849
+ # ========================================================================= #
850
+ # === header_bar?
851
+ # ========================================================================= #
852
+ def header_bar?
853
+ @header_bar
854
+ end; alias header? header_bar? # === header?
855
+
856
+ # ========================================================================= #
857
+ # === do_focus_on_the_entry_containing_the_title_of_the_song
858
+ # ========================================================================= #
859
+ def do_focus_on_the_entry_containing_the_title_of_the_song
860
+ @entry_for_the_title_of_the_song.do_focus
861
+ end
862
+
863
+ # ========================================================================= #
864
+ # === return_all_entries_on_the_right_side_widget
865
+ # ========================================================================= #
866
+ def return_all_entries_on_the_right_side_widget
867
+ [ # Let's keep them ordered how we want to display them.
868
+ @entry_for_the_artist,
869
+ @entry_for_the_year,
870
+ @entry_for_the_album,
871
+ @entry_for_the_comment,
872
+ @entry_for_the_track_number,
873
+ @entry_for_the_genre,
874
+ @entry_for_the_title_of_the_song
875
+ ]
876
+ end
877
+
878
+ # ========================================================================= #
879
+ # === reset_all_main_entries
880
+ # ========================================================================= #
881
+ def reset_all_main_entries
882
+ @entry_for_the_title_of_the_song.set_text('')
883
+ @entry_for_the_year.set_text('')
884
+ @entry_for_the_artist.set_text('')
885
+ @entry_for_the_genre.set_text('')
886
+ @entry_for_the_comment.set_text('')
887
+ end
888
+
889
+ # ========================================================================= #
890
+ # === song_name?
891
+ # ========================================================================= #
892
+ def song_name?
893
+ @entry_for_the_title_of_the_song.text?
894
+ end
895
+
896
+ # ========================================================================= #
897
+ # === sync_artist_onto_entry
898
+ # ========================================================================= #
899
+ def sync_artist_onto_entry
900
+ if metadata?
901
+ @entry_for_the_artist.set_text(metadata?.artist.to_s)
902
+ end
903
+ end
904
+
905
+ # ========================================================================= #
906
+ # === sync_year_onto_entry
907
+ # ========================================================================= #
908
+ def sync_year_onto_entry
909
+ dataset = dataset?
910
+ if dataset
911
+ @entry_for_the_year.set_text(dataset.year.to_s)
912
+ end
913
+ end
914
+
915
+ # ========================================================================= #
916
+ # === do_empty_the_tags
917
+ #
918
+ # This method will empty our tags.
919
+ # ========================================================================= #
920
+ def do_empty_the_tags
921
+ dataset = dataset?
922
+ if dataset
923
+ dataset.album = ''
924
+ dataset.artist = ''
925
+ dataset.comment = ''
926
+ dataset.genre = ''
927
+ dataset.title = ''
928
+ dataset.track = ''
929
+ dataset.year = ''
930
+ @button_save.sensitive = true
931
+ end
932
+ end; alias empty_tags do_empty_the_tags # === empty_tags
933
+
934
+ # ========================================================================= #
935
+ # === artist?
936
+ # ========================================================================= #
937
+ def artist?
938
+ @entry_for_the_artist.text?
939
+ end
940
+
941
+ # ========================================================================= #
942
+ # === artist_of_the_song?
943
+ # ========================================================================= #
944
+ def artist_of_the_song?
945
+ @artist
946
+ end; alias artist artist_of_the_song? # === artist
947
+
948
+ # ========================================================================= #
949
+ # === show_debug_information
950
+ #
951
+ # This method can be used to show some debug-related information.
952
+ # ========================================================================= #
953
+ def show_debug_information
954
+ e
955
+ e '@width and @height are: '+
956
+ @width.to_s+', '+
957
+ @height.to_s
958
+ e
959
+ end
960
+
961
+ # ========================================================================= #
962
+ # === parent_widget=
963
+ # ========================================================================= #
964
+ def parent_widget=(i = ARGV)
965
+ i = i.first if i.is_a? Array
966
+ @parent_widget = i
967
+ end
968
+
969
+ # ========================================================================= #
970
+ # === menu (menu tag)
971
+ #
972
+ # The most important aspect for menu() is to allow the user to load
973
+ # up a specific .mp3 file, on startup of this application.
974
+ #
975
+ # Usage example for this on my home system:
976
+ #
977
+ # tag_mp3_files /home/x/songs/Womack_and_Womack_Teardrops.mp3
978
+ # gtkidrenamer /home/x/songs/2Pac_GhettoGospel.mp3
979
+ #
980
+ # ========================================================================= #
981
+ def menu(
982
+ i = commandline_arguments?
983
+ )
984
+ if i and i.is_a?(Array)
985
+ i.each {|entry| menu(entry) }
986
+ else
987
+ unless i.start_with?('--')
988
+ _ = i.to_s
989
+ _ = File.expand_path(i).to_s.delete('-')
990
+ @entry_file_path.set_text(_)
991
+ if File.exist? _ # Safeguard.
992
+ do_analyse_the_default_file
993
+ set_all_relevant_entries_based_on_the_metadata
994
+ end
995
+ end
996
+ end
997
+ end; alias parse_the_commandline_arguments menu # === parse_the_commandline_arguments
998
+
999
+ # ========================================================================= #
1000
+ # === return_entry_from_the_combo_box_entry_for_genre_select_or_from_the_entry_for_the_genre
1001
+ # ========================================================================= #
1002
+ def return_entry_from_the_combo_box_entry_for_genre_select_or_from_the_entry_for_the_genre
1003
+ _ = @combo_box_entry_for_genre_select.active_text
1004
+ unless @entry_for_the_genre.text.to_s.empty?
1005
+ _ = @entry_for_the_genre.text.to_s
1006
+ end
1007
+ return _
1008
+ end
1009
+
1010
+ # ========================================================================= #
1011
+ # === do_perform_a_google_search
1012
+ #
1013
+ # This method will "perform" a browser search. We will simply open
1014
+ # the browser; on linux this works fine. :-)
1015
+ # ========================================================================= #
1016
+ def do_perform_a_google_search(
1017
+ song_name = song_name?,
1018
+ artist_name = artist?
1019
+ )
1020
+ unless Object.const_defined? :Open
1021
+ begin
1022
+ require 'open'
1023
+ rescue LoadError; end
1024
+ end
1025
+ _ = ''.dup
1026
+ _ << 'https://www.google.com/search?q='
1027
+ _ << 'wikipedia+' # Preface with "wikipedia".
1028
+ _ << song_name.to_s.tr(' ','+')+'+'
1029
+ _ << artist_name.to_s.tr(' ','+')
1030
+ _ << '&ie=utf-8&oe=utf-8'
1031
+ Open.in_browser(_)
1032
+ end
1033
+
1034
+ # ========================================================================= #
1035
+ # === return_button_play
1036
+ # ========================================================================= #
1037
+ def return_button_play
1038
+ button_play = create_button_with_label(' _Play')
1039
+ use_this_icon_name = 'video-x-generic-symbolic.symbolic'
1040
+ icon = load_icon(use_this_icon_name, 32, 0) # Use the video-x-generic button.
1041
+ button_play.set_image(icon)
1042
+ button_play.set_always_show_image(true)
1043
+ button_play.set_border_width(1)
1044
+ button_play.set_size_request(120, 65)
1045
+ button_play.on_hover(:lightgreen)
1046
+ button_play.hint =
1047
+ 'Click this button in order to <b>play</b> the selected song. '\
1048
+ 'Make sure to select a .mp3 file that exists locally as well.'
1049
+ button_play.on_clicked {
1050
+ do_play_the_selected_song
1051
+ }
1052
+ return button_play
1053
+ end
1054
+
1055
+ # ========================================================================= #
1056
+ # === widget_second_row
1057
+ #
1058
+ # This widget contains the "File path: " string.
1059
+ # ========================================================================= #
1060
+ def widget_second_row
1061
+ _ = create_hbox
1062
+ _.pad2px
1063
+ # ======================================================================= #
1064
+ # === The label containing "Input the path to a local .mp3 file: "
1065
+ # ======================================================================= #
1066
+ label = label('File path: ')
1067
+ label.align_to_the_left
1068
+ label.hint = 'To the right of this text you can designate the '\
1069
+ 'path to the local .mp3 file.'
1070
+ _.minimal(label, 6)
1071
+ eventbox_arrow_right = create_eventbox(emoji(:arrow_right))
1072
+ eventbox_arrow_right.on_clicked {
1073
+ do_open_a_local_file_and_then_set_the_proper_entry
1074
+ }
1075
+ _.minimal(eventbox_arrow_right, 2)
1076
+ _.maximal(entry_file_path?, 4)
1077
+ button_open_local_file = create_button('Open local file')
1078
+ button_open_local_file.use_this_font = slightly_smaller_font?
1079
+ button_open_local_file.hint = 'Click on this button to open a local file.'
1080
+ button_open_local_file.on_clicked {
1081
+ do_look_for_a_local_file_and_then_analyse_the_audio_file_at_once
1082
+ }
1083
+ _.minimal(button_open_local_file, 4)
1084
+ _.minimal(create_vspacer)
1085
+ _.minimal(create_vspacer, 1)
1086
+ return _
1087
+ end
1088
+
1089
+ # ========================================================================= #
1090
+ # === do_popup_the_settings_widget (settings tag)
1091
+ # ========================================================================= #
1092
+ def do_popup_the_settings_widget
1093
+ if @settings_popover.nil?
1094
+ @settings_popover = return_popover(
1095
+ '',
1096
+ header?, # This is the header bar actually.
1097
+ :default_padding, # This is the default padding to use.
1098
+ @settings_vbox
1099
+ )
1100
+ end
1101
+ @settings_popover.popup
1102
+ end
1103
+
1104
+ # ========================================================================= #
1105
+ # === widget_right
1106
+ #
1107
+ # This is the large vbox that appears on the very left side, containing
1108
+ # tons of buttons.
1109
+ # ========================================================================= #
1110
+ def widget_right
1111
+ vbox_containing_the_buttons = create_vbox
1112
+ vbox_containing_the_buttons.border_width = 6
1113
+ vbox_containing_the_buttons.minimal(@button_debug, 1)
1114
+ vbox_containing_the_buttons.minimal(@box_for_the_button_analyse_the_mp3_file, 1)
1115
+ vbox_containing_the_buttons.minimal(@event_box_open_file, 1)
1116
+ vbox_containing_the_buttons.minimal(@button_perform_a_google_search, 1)
1117
+ vbox_containing_the_buttons.minimal(@button_open_a_random_audio_file, 1)
1118
+ vbox_containing_the_buttons.minimal(@button_delete_the_tags, 1)
1119
+ vbox_containing_the_buttons.minimal(@button_update, 1)
1120
+ vbox_containing_the_buttons.minimal(@button_play, 1)
1121
+ vbox_containing_the_buttons.minimal(@button_save, 1)
1122
+ vbox_containing_the_buttons.minimal(@button_close, 1)
1123
+ vbox_containing_the_buttons.use_this_font = slightly_smaller_font?
1124
+ return vbox_containing_the_buttons
1125
+ end; alias return_vbox_containing_the_buttons widget_right # === return_vbox_containing_the_buttons
1126
+
1127
+ # ========================================================================= #
1128
+ # === entry_for_the_title_of_the_song?
1129
+ # ========================================================================= #
1130
+ def entry_for_the_title_of_the_song?
1131
+ @entry_for_the_title_of_the_song
1132
+ end; alias entry_for_the_song_name? entry_for_the_title_of_the_song? # === entry_for_the_song_name?
1133
+
1134
+ # ========================================================================= #
1135
+ # === label_duration_content?
1136
+ # ========================================================================= #
1137
+ def label_duration_content?
1138
+ @information_about_a_mp3_file.label_duration_content?
1139
+ end
1140
+
1141
+ # ========================================================================= #
1142
+ # === set_the_bitrate
1143
+ #
1144
+ # This method will update the appropriate label in the grid, containing
1145
+ # the bitrate of the .mp3 file.
1146
+ # ========================================================================= #
1147
+ def set_the_bitrate(i)
1148
+ @information_about_a_mp3_file.label_bit_rate_content?.set_text(i.to_s)
1149
+ end
1150
+
1151
+ # ========================================================================= #
1152
+ # === set_the_samplerate
1153
+ # ========================================================================= #
1154
+ def set_the_samplerate(i)
1155
+ @information_about_a_mp3_file.label_sample_rate_content.set_text(i.to_s)
1156
+ end
1157
+
1158
+ # ========================================================================= #
1159
+ # === sync_genre_onto_entry
1160
+ # ========================================================================= #
1161
+ def sync_genre_onto_entry(
1162
+ this_text = :infer_from_the_dataset
1163
+ )
1164
+ if dataset?
1165
+ this_text = dataset?.genre.to_s
1166
+ @entry_for_the_genre.set_text(this_text)
1167
+ end
1168
+ end
1169
+
1170
+ # ========================================================================= #
1171
+ # === comment_entry?
1172
+ # ========================================================================= #
1173
+ def comment_entry?
1174
+ @entry_for_the_comment
1175
+ end
1176
+
1177
+ # ========================================================================= #
1178
+ # === widget_left
1179
+ # ========================================================================= #
1180
+ def widget_left
1181
+ _ = @widget_on_the_right_side_including_the_title_of_the_song = create_vbox
1182
+ _.set_border_width(2)
1183
+ # ======================================================================= #
1184
+ # === Title of the song
1185
+ # ======================================================================= #
1186
+ label_title = modify_bold_label(
1187
+ 'Title of the song:',
1188
+ 'black', :right
1189
+ ).center # align_left_and_center_in_the_middle
1190
+ # label_with_the_audio_symbol = create_label(:three_stars) # This used to be :audio3
1191
+ another_tiny_hbox = create_hbox
1192
+ use_this_icon_name = 'audio-speakers'
1193
+ begin
1194
+ icon = icon_theme?.load_icon(use_this_icon_name, 32, 0)
1195
+ rescue ::Gtk::IconThemeError::NotFound => error
1196
+ e tomato('An error occurred:')
1197
+ e
1198
+ pp error
1199
+ e
1200
+ icon = icon_theme?.load_icon('audio-x-generic', 32, 0)
1201
+ end
1202
+
1203
+ label_with_the_audio_symbol = create_image_pixbuf(icon)
1204
+
1205
+ another_tiny_hbox.minimal(label_with_the_audio_symbol)
1206
+ another_tiny_hbox.maximal(label_title)
1207
+ mini_vbox = create_vbox
1208
+ # mini_vbox.pack_start(label_title)
1209
+ mini_vbox.maximal(another_tiny_hbox)
1210
+ arrow_to_the_right = create_label('→ ')
1211
+ tiny_hbox = create_hbox
1212
+ tiny_hbox.minimal(arrow_to_the_right)
1213
+ # ======================================================================= #
1214
+ # Add the entry for the title of the song next.
1215
+ # ======================================================================= #
1216
+ tiny_hbox.maximal(@entry_for_the_title_of_the_song)
1217
+ mini_vbox.maximal(tiny_hbox)
1218
+ _.minimal(mini_vbox, 0)
1219
+ # ======================================================================= #
1220
+ # Create the grid next that is situated on the right side.
1221
+ # ======================================================================= #
1222
+ @grid_on_the_right_side = create_grid
1223
+ @grid_on_the_right_side.set_column_spacing(1)
1224
+ @grid_on_the_right_side.set_row_spacing(1)
1225
+ _.minimal(@grid_on_the_right_side, 2)
1226
+ # ======================================================================= #
1227
+ # === Artist
1228
+ # ======================================================================= #
1229
+ label_the_artist = create_label('Artist: ').align_left_and_center_in_the_middle
1230
+ label_the_artist.make_bold
1231
+ # ======================================================================= #
1232
+ # === @entry_for_the_artist
1233
+ # ======================================================================= #
1234
+ @entry_for_the_artist = create_entry
1235
+ @entry_for_the_artist.set_max_length(40)
1236
+ @entry_for_the_artist.set_size_request(300, 40)
1237
+
1238
+ @grid_on_the_right_side.attach(label_the_artist, 0, 0, 1, 1) # left, top, width, height
1239
+ @grid_on_the_right_side.attach(@entry_for_the_artist, 1, 0, 1, 1)
1240
+
1241
+ # ======================================================================= #
1242
+ # === Year
1243
+ # ======================================================================= #
1244
+ label_for_the_year = create_label('Year: ').align_left_and_center_in_the_middle
1245
+ label_for_the_year.make_bold
1246
+ label_for_the_year.hint = 'The <b>Year</b> entry should ideally '\
1247
+ 'indicate when a song was released officially. Some songs were '\
1248
+ 'recorded in a year before, but the more relevant entry will '\
1249
+ 'be the year in which that song was released.'
1250
+ # ======================================================================= #
1251
+ # === @entry_for_the_year
1252
+ # ======================================================================= #
1253
+ @entry_for_the_year = create_entry
1254
+ @entry_for_the_year.set_max_length(50)
1255
+ @entry_for_the_year.allow_only_numbers
1256
+ @entry_for_the_year.focus_on_click_event
1257
+
1258
+ @grid_on_the_right_side.attach(label_for_the_year, 0, 1, 1, 1) # left, top, width, height
1259
+ hbox = create_hbox
1260
+ hbox.maximal(@entry_for_the_year, 1)
1261
+ combo_box_entry = create_combo_box_entry
1262
+ combo_box_entry.populate(
1263
+ (1960 .. 2050).to_a.map(&:to_s)
1264
+ )
1265
+ combo_box_entry.this_entry_is_active(30)
1266
+ combo_box_entry.on_changed {
1267
+ @entry_for_the_year.set_text(combo_box_entry.text?)
1268
+ }
1269
+ hbox.minimal(combo_box_entry)
1270
+ @grid_on_the_right_side.attach(hbox, 1, 1, 1, 1)
1271
+
1272
+ # ======================================================================= #
1273
+ # === Album
1274
+ # ======================================================================= #
1275
+ label_for_the_album = create_label('Album: ').align_left_and_center_in_the_middle
1276
+ label_for_the_album.make_bold
1277
+ # ======================================================================= #
1278
+ # === @entry_for_the_album
1279
+ # ======================================================================= #
1280
+ @entry_for_the_album = create_entry
1281
+ @entry_for_the_album.set_max_length(75)
1282
+
1283
+ @grid_on_the_right_side.attach(label_for_the_album, 0, 2, 1, 1) # left, top, width, height
1284
+ @grid_on_the_right_side.attach(@entry_for_the_album, 1, 2, 1, 1)
1285
+
1286
+ # ======================================================================= #
1287
+ # === Comment (comment tag)
1288
+ #
1289
+ # Personally I use this field as a placeholder for a remote URL,
1290
+ # to some of the .mp3 songs. This may be useful in other graphical
1291
+ # applications.
1292
+ # ======================================================================= #
1293
+ label_for_the_comment = create_label('Comment: ').align_left_and_center_in_the_middle
1294
+ label_for_the_comment.make_bold
1295
+ event_box_with_left_arrow = create_eventbox(emoji(:left_arrow))
1296
+ event_box_with_left_arrow.hint = 'Click on this arrow '\
1297
+ 'to open the URL (if there is one), via the browser.'
1298
+ event_box_with_left_arrow.on_clicked {
1299
+ require 'open' unless Object.const_defined? :Open
1300
+ Open.in_browser('"'+comment_entry?.text.to_s.strip+'"') unless comment_entry?.text.to_s.empty?
1301
+ }
1302
+ mini_hbox = create_hbox
1303
+ mini_hbox.maximal(@entry_for_the_comment)
1304
+ mini_hbox.minimal(event_box_with_left_arrow)
1305
+ @grid_on_the_right_side.attach(label_for_the_comment, 0, 3, 1, 1) # left, top, width, height
1306
+ @grid_on_the_right_side.attach(mini_hbox, 1, 3, 1, 1)
1307
+
1308
+ # ======================================================================= #
1309
+ # === Track number
1310
+ # ======================================================================= #
1311
+ label_for_the_track_number = create_label('Track number: ').align_left_and_center_in_the_middle
1312
+ label_for_the_track_number.make_bold
1313
+ # ======================================================================= #
1314
+ # === @entry_for_the_track_number
1315
+ # ======================================================================= #
1316
+ @entry_for_the_track_number = create_entry
1317
+ @entry_for_the_track_number.set_max_length(3)
1318
+ @entry_for_the_track_number.set_size_request(50, -1)
1319
+ @entry_for_the_track_number.allow_only_numbers
1320
+ unless @dataset.nil?
1321
+ save_this = @dataset
1322
+ save_this = @dataset.track if @dataset.respond_to? :track
1323
+ @entry_for_the_track_number.set_text(save_this)
1324
+ do_activate_the_save_button
1325
+ end
1326
+
1327
+ @grid_on_the_right_side.attach(label_for_the_track_number, 0, 4, 1, 1) # left, top, width, height
1328
+ @grid_on_the_right_side.attach(@entry_for_the_track_number, 1, 4, 1, 1)
1329
+
1330
+ # ======================================================================= #
1331
+ # === Genre
1332
+ # ======================================================================= #
1333
+ label_for_the_genre = create_label('Genre: ').align_left_and_center_in_the_middle
1334
+ label_for_the_genre.make_bold
1335
+ label_for_the_genre.set_size_request(125, 40)
1336
+ @entry_for_the_genre = create_entry
1337
+ @entry_for_the_genre.set_size_request(125, 40)
1338
+ @entry_for_the_genre.make_bold
1339
+
1340
+ @grid_on_the_right_side.attach(label_for_the_genre, 0, 5, 1, 1) # left, top, width, height
1341
+ @grid_on_the_right_side.attach(@entry_for_the_genre, 1, 5, 1, 1)
1342
+ # ======================================================================= #
1343
+ # === Genre (select)
1344
+ #
1345
+ # This will load the file called "music_genres.yml", which in turn
1346
+ # contains the most commonly used genre entries. Note that this file
1347
+ # will NOT contain all genres, as that would lead to a very long
1348
+ # dropdown listing.
1349
+ # ======================================================================= #
1350
+ label_for_the_genre_select = create_label('Genre (select): ').align_left_and_center_in_the_middle
1351
+ label_for_the_genre_select.make_bold
1352
+ create_the_combo_box_entry_for_genre_select
1353
+ # ======================================================================= #
1354
+ # Next build up the hbox that contains the label and the combo box:
1355
+ # ======================================================================= #
1356
+ hbox_for_the_genre_select_components = create_hbox(
1357
+ label_for_the_genre_select,
1358
+ @combo_box_entry_for_genre_select
1359
+ )
1360
+ hbox_for_the_genre_select_components.pack_end(
1361
+ @button_assume, padding: 2
1362
+ )
1363
+ _.minimal(
1364
+ hbox_for_the_genre_select_components, 0
1365
+ )
1366
+ _.minimal(return_the_save_button, 2)
1367
+ return _
1368
+ end; alias create_and_return_the_pane_holding_various_file_related_entries widget_left # === create_and_return_the_pane_holding_various_file_related_entries
1369
+
1370
+ # ========================================================================= #
1371
+ # === report_artist
1372
+ # ========================================================================= #
1373
+ def report_artist(i = @artist)
1374
+ e sfancy(' Artist: '.ljust(N_PAD))+
1375
+ right_colour(i.to_s)
1376
+ end
1377
+
1378
+ # ========================================================================= #
1379
+ # === report_album
1380
+ # ========================================================================= #
1381
+ def report_album(i = @album)
1382
+ e sfancy(' Album: '.ljust(N_PAD))+
1383
+ right_colour(i.to_s)
1384
+ end
1385
+
1386
+ # ========================================================================= #
1387
+ # === report_comment
1388
+ # ========================================================================= #
1389
+ def report_comment(i = @comment)
1390
+ e sfancy(' Comment: '.ljust(N_PAD))+
1391
+ right_colour(i.to_s)
1392
+ end
1393
+
1394
+ # ========================================================================= #
1395
+ # === report_genre
1396
+ #
1397
+ # To get all available genres, see:
1398
+ #
1399
+ # https://en.wikipedia.org/wiki/ID3#Genre_list_in_ID3v1[12]
1400
+ #
1401
+ # ========================================================================= #
1402
+ def report_genre(i = @genre)
1403
+ e sfancy(' Genre: '.ljust(N_PAD))+
1404
+ right_colour(i.to_s)
1405
+ end
1406
+
1407
+ # ========================================================================= #
1408
+ # === report_year
1409
+ # ========================================================================= #
1410
+ def report_year(i = @year)
1411
+ e sfancy(' Year: '.ljust(N_PAD))+
1412
+ right_colour(i.to_s)
1413
+ end
1414
+
1415
+ # ========================================================================= #
1416
+ # === report_length_in_seconds
1417
+ # ========================================================================= #
1418
+ def report_length_in_seconds(
1419
+ i = @length_in_seconds
1420
+ )
1421
+ e sfancy(' Duration (in seconds): '.ljust(N_PAD))+
1422
+ right_colour(i.to_s)
1423
+ end
1424
+
1425
+ # ========================================================================= #
1426
+ # === tag?
1427
+ # ========================================================================= #
1428
+ def tag?
1429
+ @tag
1430
+ end; alias tag tag? # === tag
1431
+
1432
+ # ========================================================================= #
1433
+ # === title?
1434
+ # ========================================================================= #
1435
+ def title?
1436
+ @title
1437
+ end; alias title title? # === title?
1438
+
1439
+ # ========================================================================= #
1440
+ # === date?
1441
+ # ========================================================================= #
1442
+ def date?
1443
+ @date
1444
+ end; alias date date? # === date
1445
+
1446
+ # ========================================================================= #
1447
+ # === disc?
1448
+ # ========================================================================= #
1449
+ def disc?
1450
+ @disc
1451
+ end; alias disc disc? # === disc
1452
+
1453
+ # ========================================================================= #
1454
+ # === bpm?
1455
+ # ========================================================================= #
1456
+ def bpm?
1457
+ @bpm
1458
+ end; alias bpm bpm? # === bpm
1459
+
1460
+ # ========================================================================= #
1461
+ # === album?
1462
+ # ========================================================================= #
1463
+ def album?
1464
+ @album
1465
+ end; alias album album? # === album
1466
+
1467
+ # ========================================================================= #
1468
+ # === year?
1469
+ # ========================================================================= #
1470
+ def year?
1471
+ @year
1472
+ end; alias year year? # === year
1473
+
1474
+ # ========================================================================= #
1475
+ # === band?
1476
+ # ========================================================================= #
1477
+ def band?
1478
+ @band
1479
+ end; alias band band? # === band
1480
+
1481
+ # ========================================================================= #
1482
+ # === genre?
1483
+ # ========================================================================= #
1484
+ def genre?
1485
+ @genre
1486
+ end; alias genre genre? # === genre
1487
+
1488
+ # ========================================================================= #
1489
+ # === performer?
1490
+ # ========================================================================= #
1491
+ def performer?
1492
+ @performer
1493
+ end; alias performer performer? # === performer
1494
+
1495
+ # ========================================================================= #
1496
+ # === comment?
1497
+ # ========================================================================= #
1498
+ def comment?
1499
+ @comment
1500
+ end; alias comment comment? # === comment
1501
+
1502
+ # ========================================================================= #
1503
+ # === track?
1504
+ # ========================================================================= #
1505
+ def track?
1506
+ @track
1507
+ end; alias track track? # === track
1508
+
1509
+ # ========================================================================= #
1510
+ # === set_title
1511
+ # ========================================================================= #
1512
+ def set_title(i)
1513
+ @title = i.to_s
1514
+ end; alias title= set_title # === title=
1515
+
1516
+ # ========================================================================= #
1517
+ # === set_album
1518
+ # ========================================================================= #
1519
+ def set_album(i)
1520
+ @album = i.to_s
1521
+ end; alias album= set_album # === album=
1522
+
1523
+ # ========================================================================= #
1524
+ # === set_artist
1525
+ # ========================================================================= #
1526
+ def set_artist(i)
1527
+ @artist = i.to_s
1528
+ end; alias artist= set_artist # === artist=
1529
+
1530
+ # ========================================================================= #
1531
+ # === set_comment
1532
+ # ========================================================================= #
1533
+ def set_comment(i)
1534
+ @comment = i.to_s
1535
+ end; alias comment= set_comment # === comment=
1536
+
1537
+ # ========================================================================= #
1538
+ # === set_track
1539
+ # ========================================================================= #
1540
+ def set_track(i)
1541
+ @track = i.to_i # This must be an Integer.
1542
+ end; alias track= set_track # === track=
1543
+
1544
+ # ========================================================================= #
1545
+ # === set_year
1546
+ # ========================================================================= #
1547
+ def set_year(i)
1548
+ @year = i.to_i # This must be an Integer.
1549
+ end; alias year= set_year # === year=
1550
+
1551
+ # ========================================================================= #
1552
+ # === set_genre
1553
+ # ========================================================================= #
1554
+ def set_genre(i)
1555
+ @genre = i.to_s
1556
+ end; alias genre= set_genre # === genre=
1557
+
1558
+ # ========================================================================= #
1559
+ # === set_new_year
1560
+ # ========================================================================= #
1561
+ def set_new_year(i)
1562
+ @tag.year = i.to_i # This must be an Integer.
1563
+ end
1564
+
1565
+ # ========================================================================= #
1566
+ # === set_new_album
1567
+ # ========================================================================= #
1568
+ def set_new_album(i)
1569
+ @tag.album = i
1570
+ end
1571
+
1572
+ # ========================================================================= #
1573
+ # === report_everything
1574
+ #
1575
+ # This method will report as much as possible about the song at hand.
1576
+ #
1577
+ # "track" would also be a tag that could be shown. Personally I don't
1578
+ # really need it, though.
1579
+ # ========================================================================= #
1580
+ def report_everything
1581
+ report_title
1582
+ report_artist
1583
+ report_album
1584
+ report_year
1585
+ report_genre
1586
+ report_comment
1587
+ report_length_in_seconds
1588
+ end; alias report report_everything # === report
1589
+
1590
+ # ========================================================================= #
1591
+ # === work_on_this_file
1592
+ # ========================================================================= #
1593
+ def work_on_this_file(
1594
+ this_file = @this_file
1595
+ )
1596
+ unless Object.const_defined? :TagLib
1597
+ text = <<-EOF
1598
+ Please install TagLib first via:
1599
+
1600
+ gem install taglib
1601
+
1602
+ EOF
1603
+ e text
1604
+ return text
1605
+ end
1606
+ # ======================================================================= #
1607
+ # Load up the file via TagLib first.
1608
+ # ======================================================================= #
1609
+ TagLib::FileRef.open(this_file) { |fileref| # of class TagLib::FileRef.
1610
+ unless fileref.null?
1611
+ @tag = fileref.tag # Keep a reference to it here.
1612
+ @title = @tag.title # => "Wake Up"
1613
+ @artist = @tag.artist # => "DJ Punk"
1614
+ @album = @tag.album # => "Funeral"
1615
+ @year = @tag.year # => 2004
1616
+ @genre = @tag.genre # => "Indie Rock"
1617
+ @comment = @tag.comment
1618
+ @track = @tag.track
1619
+ # @band = @tag.band # Does not exist.
1620
+ @performer = @tag.performer if @tag.respond_to? :performer
1621
+ @properties = fileref.audio_properties
1622
+ do_determine_the_length_of_the_song(this_file)
1623
+ if @may_we_report
1624
+ e
1625
+ report_everything
1626
+ e
1627
+ end
1628
+ end
1629
+ } # File is automatically closed at block end.
1630
+ end
1631
+
1632
+ # ========================================================================= #
1633
+ # === this_file?
1634
+ # ========================================================================= #
1635
+ def this_file?
1636
+ @this_file
1637
+ end; alias main_file? this_file? # === main_file?
1638
+
1639
+ # ========================================================================= #
1640
+ # === length_in_seconds?
1641
+ # ========================================================================= #
1642
+ def length_in_seconds?
1643
+ @length_in_seconds
1644
+ end
1645
+
1646
+ # ========================================================================= #
1647
+ # === do_determine_the_length_of_the_song
1648
+ #
1649
+ # The first argument should be the name of the .mp3 file at hand.
1650
+ # ========================================================================= #
1651
+ def do_determine_the_length_of_the_song(
1652
+ this_file = main_file?
1653
+ )
1654
+ # ======================================================================= #
1655
+ # The way for the mp3-library goes like this:
1656
+ # @length_in_seconds = @properties.length_in_seconds # => 335 (song length)
1657
+ # But this was buggy for me, so we query ffmpeg instead. The code above
1658
+ # is retained, though, in the event that we may want to re-instate it
1659
+ # one day. (Noted this down at 26.10.2021)
1660
+ # ======================================================================= #
1661
+ @length_in_seconds = ::MultimediaParadise.length_of?(this_file)
1662
+ end
1663
+
1664
+ # ========================================================================= #
1665
+ # === try_to_require_the_tablib_gem
1666
+ # ========================================================================= #
1667
+ def try_to_require_the_tablib_gem
1668
+ begin
1669
+ # ===================================================================== #
1670
+ # Require the taglib-ruby gem.
1671
+ #
1672
+ # See: https://rubygems.org/gems/taglib-ruby
1673
+ # ===================================================================== #
1674
+ require 'taglib'
1675
+ rescue LoadError; end
1676
+ end
1677
+
1678
+ # ========================================================================= #
1679
+ # === update!
1680
+ #
1681
+ # This method can be used to update the tags.
1682
+ # ========================================================================= #
1683
+ def update!(
1684
+ this_file = @this_file
1685
+ )
1686
+ TagLib::FileRef.open(this_file) { |fileref| # of class TagLib::FileRef.
1687
+ unless fileref.null?
1688
+ @tag = fileref.tag # Keep a reference to it here.
1689
+ set_new_album(@album) # Use the old reference.
1690
+ set_new_year(@year) # Use the old reference, via @year.
1691
+ @tag.title = @title.to_s
1692
+ @tag.artist = @artist.to_s
1693
+ @tag.genre = @genre.to_s
1694
+ @tag.comment = @comment.to_s
1695
+ if @track
1696
+ @tag.track = @track.to_i # This must be an integer.
1697
+ end
1698
+ fileref.save
1699
+ end
1700
+ }
1701
+ end
1702
+
1703
+ # ========================================================================= #
1704
+ # === save
1705
+ # ========================================================================= #
1706
+ def save
1707
+ @tag.save
1708
+ end
1709
+
1710
+ # ========================================================================= #
1711
+ # === set_this_file
1712
+ # ========================================================================= #
1713
+ def set_this_file(i)
1714
+ @this_file = i
1715
+ end
1716
+
1717
+ # ========================================================================= #
1718
+ # === report_title
1719
+ # ========================================================================= #
1720
+ def report_title(i = @title)
1721
+ e sfancy(' Title: '.ljust(N_PAD))+
1722
+ right_colour(i.to_s)
1723
+ end
1724
+
1725
+ # ========================================================================= #
1726
+ # === right_colour
1727
+ # ========================================================================= #
1728
+ def right_colour(i)
1729
+ mediumpurple(i)
1730
+ end
1731
+
1732
+ # ========================================================================= #
1733
+ # === instantiate_the_metadata_from_this_file
1734
+ #
1735
+ # This will set @metadata_for_the_song.
1736
+ # ========================================================================= #
1737
+ def instantiate_the_metadata_from_this_file(this_file)
1738
+ # ======================================================================= #
1739
+ # === @metadata_for_the_song
1740
+ # ======================================================================= #
1741
+ @metadata_for_the_song = MultimediaParadise.analyse_tags_of_this_mp3_file(this_file)
1742
+ end
1743
+
1744
+ # ========================================================================= #
1745
+ # === metadata_for_the_song?
1746
+ # ========================================================================= #
1747
+ def metadata_for_the_song?
1748
+ @metadata_for_the_song
1749
+ end; alias metadata? metadata_for_the_song? # === metadata?
1750
+ alias dataset? metadata_for_the_song? # === dataset?
1751
+
1752
+ # ========================================================================= #
1753
+ # === sync_the_duration_of_the_file_onto_the_correct_entry
1754
+ # ========================================================================= #
1755
+ def sync_the_duration_of_the_file_onto_the_correct_entry(
1756
+ _ = @metadata_for_the_song
1757
+ )
1758
+ if _
1759
+ use_this_duration = _.length_in_seconds?.round(5).to_s
1760
+ label_duration_content?.set_text(use_this_duration)
1761
+ label_duration_content?.make_bold
1762
+ end
1763
+ end
1764
+
1765
+ # ========================================================================= #
1766
+ # === header_bar_height?
1767
+ # ========================================================================= #
1768
+ def header_bar_height?
1769
+ 54
1770
+ end
1771
+
1772
+ # ========================================================================= #
1773
+ # === return_widget_information_about_a_mp3_file
1774
+ # ========================================================================= #
1775
+ def return_widget_information_about_a_mp3_file
1776
+ @information_about_a_mp3_file = MultimediaParadise::GUI::Gtk::InformationAboutAMp3File.new
1777
+ @information_about_a_mp3_file.set_parent_widget(self)
1778
+ return @information_about_a_mp3_file
1779
+ end; alias return_the_grid_containing_the_bitrate_and_associated_values return_widget_information_about_a_mp3_file # === return_the_grid_containing_the_bitrate_and_associated_values
1780
+ alias create_table1 return_widget_information_about_a_mp3_file # === create_table1
1781
+
1782
+ # ========================================================================= #
1783
+ # === do_open_a_random_audio_file_and_then_set_the_proper_entry
1784
+ #
1785
+ # The first argument to this method must be the file path to the
1786
+ # local .mp3 file at hand.
1787
+ # ========================================================================= #
1788
+ def do_open_a_random_audio_file_and_then_set_the_proper_entry(
1789
+ _ = return_random_audio_file
1790
+ )
1791
+ clear_old_entries # We must clear old entries as well.
1792
+ entry_file_path?.set_text(_.to_s)
1793
+ # ======================================================================= #
1794
+ # If the file exists we will also update the file size label.
1795
+ # ======================================================================= #
1796
+ if File.exist? _
1797
+ do_analyse_the_audio_file(entry_file_path_text?)
1798
+ set_all_relevant_entries_based_on_the_metadata
1799
+ # ===================================================================== #
1800
+ # Empty the status bar as well whenever a new file is assigned.
1801
+ # ===================================================================== #
1802
+ add_this_text_to_the_status_bar('')
1803
+ end
1804
+ end
1805
+
1806
+ # ========================================================================= #
1807
+ # === set_main_entry
1808
+ # ========================================================================= #
1809
+ def set_main_entry(i)
1810
+ @entry_file_path.set_text(i)
1811
+ if File.exist? i # Safeguard.
1812
+ do_analyse_the_default_file
1813
+ set_all_relevant_entries_based_on_the_metadata
1814
+ end
1815
+ end
1816
+
1817
+ # ========================================================================= #
1818
+ # === do_analyse_the_mp3_file (analyse tag, load tag)
1819
+ #
1820
+ # This is closely related to the method called
1821
+ # do_analyse_the_audio_file_then_set_all_relevant_entries().
1822
+ #
1823
+ # This is the method that should be called when the user wants to obtain
1824
+ # information about the metadata of the song (.mp3 file) at hand.
1825
+ #
1826
+ # It will not do anything with the different gtk-widgets in use, so you
1827
+ # have to call other methods if you want to sync towards the gtk-widgets.
1828
+ #
1829
+ # We also need to check whether ID3Lib is available if this is used; in
1830
+ # particular windows-users may often not have it installed by default.
1831
+ # ========================================================================= #
1832
+ def do_analyse_the_mp3_file(
1833
+ _ = entry_file_path_text?
1834
+ )
1835
+ if _ and !_.empty? and File.exist?(_)
1836
+ # ===================================================================== #
1837
+ # First we have to honour the correct id-tagger to use, as specified
1838
+ # by the user.
1839
+ # ===================================================================== #
1840
+ case ID_TAGGER_TO_USE_FOR_THE_MP3_FILES
1841
+ # ===================================================================== #
1842
+ # === :taglib
1843
+ #
1844
+ # We must notify the user that taglib is mandatory - otherwise the
1845
+ # application will not work properly.
1846
+ # ===================================================================== #
1847
+ when :taglib
1848
+ unless Object.const_defined? :TagLib
1849
+ text_to_display = <<-EOF
1850
+ Please install TagLib first via:
1851
+
1852
+ <b>gem install taglib-ruby</b>
1853
+
1854
+ EOF
1855
+ custom_widget = create_vbox
1856
+ custom_widget.pad12px
1857
+ custom_widget.bblack1
1858
+ button_to_use = button('gem install taglib-ruby')
1859
+ button_to_use.on_clicked {
1860
+ esystem(button_to_use.text?)
1861
+ }
1862
+ custom_widget.maximal(button_to_use)
1863
+ custom_widget.show_all
1864
+ a_new_popup = return_popover(
1865
+ text_to_display,
1866
+ entry_on_top?,
1867
+ 25, # Padding is 25 px.
1868
+ custom_widget
1869
+ )
1870
+ a_new_popup.popup
1871
+ end
1872
+ # ===================================================================== #
1873
+ # === :id3lib
1874
+ # ===================================================================== #
1875
+ when :id3lib
1876
+ if Object.const_defined? :ID3Lib
1877
+ this_song = ID3Lib::Tag.new(this_song)
1878
+ # Enable the save-button functionality. This is currently (2021)
1879
+ # not needed anymore.
1880
+ # the_save_button_is_available_again
1881
+ else
1882
+ e 'ID3Lib is not available/installed. Please install '\
1883
+ 'it for your platform / operating-system.'
1884
+ end
1885
+ end
1886
+ @spinner.start_spinning
1887
+ Thread.new {
1888
+ # =================================================================== #
1889
+ # Clear the old status-bar message first.
1890
+ # =================================================================== #
1891
+ add_this_text_to_the_status_bar('')
1892
+ # =================================================================== #
1893
+ # The class here is: MultimediaParadise::AudioTagReader.
1894
+ # The old code used MultimediaParadise::AudioTagReader.new()
1895
+ # directly. In September 2021 this was changed to a
1896
+ # toplevel-method instead.
1897
+ # =================================================================== #
1898
+ clear_old_entries
1899
+ e steelblue('-' * 60)
1900
+ instantiate_the_metadata_from_this_file(_)# This will set @metadata_for_the_song
1901
+ @metadata_for_the_song.report # This can be used for debugging.
1902
+ @information_about_a_mp3_file.set_duration(@metadata_for_the_song.length_in_seconds?.to_s)
1903
+ file_size = File.size(_)
1904
+ @information_about_a_mp3_file.set_filesize(file_size.to_s)
1905
+ # entry_for_the_title_of_the_song?.set_text(@metadata_for_the_song.title?.to_s)
1906
+ do_assume_the_name_of_the_song_and_the_artist_from_the_filename_directly
1907
+ sync_everything
1908
+ @spinner.stop_spinning # Now we can stop spinning.
1909
+ if is_on_roebe?
1910
+ display_information_about_the_song
1911
+ e 'Next, as we are on roebe, we will display the '+
1912
+ ::Colours.steelblue('@dataset')+' variable.'
1913
+ pp dataset?
1914
+ end
1915
+ }
1916
+ else
1917
+ e 'First assign a path, please.'
1918
+ end
1919
+ end; alias load_the_id3_dataset_from_this_file do_analyse_the_mp3_file # === load_the_id3_dataset_from_this_file
1920
+ alias do_analyse_the_audio_file_at_once do_analyse_the_mp3_file # === do_analyse_the_audio_file_at_once
1921
+ alias do_open_a_local_file_and_then_set_the_proper_entry do_analyse_the_mp3_file # === do_open_a_local_file_and_then_set_the_proper_entry
1922
+ alias do_analyse_the_audio_file do_analyse_the_mp3_file # === do_analyse_the_audio_file
1923
+ alias determine_the_metadata_for_this_song do_analyse_the_mp3_file # === determine_the_metadata_for_this_song
1924
+ alias analyse_this_song do_analyse_the_mp3_file # === analyse_this_song
1925
+ alias do_analyse_the_audio_file do_analyse_the_mp3_file # === do_analyse_the_audio_file
1926
+ alias do_analyse_the_default_file do_analyse_the_mp3_file # === do_analyse_the_default_file
1927
+ alias ensure_that_we_have_a_dataset do_analyse_the_mp3_file # === ensure_that_we_have_a_dataset
1928
+
1929
+ # ========================================================================= #
1930
+ # === return_event_box_for_opening_a_file
1931
+ #
1932
+ # The first two arguments can designate a certain width and height
1933
+ # for the widget.
1934
+ # ========================================================================= #
1935
+ def return_event_box_for_opening_a_file(
1936
+ width = 110,
1937
+ height = 65
1938
+ )
1939
+ event_box_open_file = create_event_box
1940
+ # ======================================================================= #
1941
+ # In April 2021 the code below this comment was changed to an image.
1942
+ #
1943
+ # Relying on an emoji is unfortunately not that reliable, because
1944
+ # we depend on the emojis available on the local computer. This
1945
+ # may not always work, whereas with an image we can depend on
1946
+ # more reliably so.
1947
+ #
1948
+ # event_box_open_file << emoji(:open_file)
1949
+ #
1950
+ # ======================================================================= #
1951
+ # event_box_open_file << create_image(:open_local_file)
1952
+ event_box_open_file << text('Open a local file')
1953
+ event_box_open_file.set_border_width(1)
1954
+ event_box_open_file.set_size_request(125, 68)
1955
+ event_box_open_file.css_class('button_class')
1956
+ event_box_open_file.pad5px
1957
+ event_box_open_file.on_clicked {
1958
+ do_look_for_a_local_file_and_then_analyse_the_audio_file_at_once
1959
+ }
1960
+ event_box_open_file.hint =
1961
+ 'Click on this button to open '\
1962
+ '<b>a local .mp3 file</b>.'
1963
+ return event_box_open_file
1964
+ end
1965
+
1966
+ # ========================================================================= #
1967
+ # === do_look_for_a_local_file_and_then_analyse_the_audio_file_at_once (open tag)
1968
+ # ========================================================================= #
1969
+ def do_look_for_a_local_file_and_then_analyse_the_audio_file_at_once
1970
+ look_for_a_local_file {{
1971
+ filter_for: %w( .mp3 .ogg ),
1972
+ start_directory: return_path_to_the_local_songs_directory_or_return_pwd,
1973
+ additional_directories: [return_pwd] # Always add the current working directory.
1974
+ }}
1975
+ @entry_file_path.set_text(::Gtk.main_file?.to_s)
1976
+ do_analyse_the_audio_file_at_once
1977
+ end; alias do_open_the_file_opener_dialog do_look_for_a_local_file_and_then_analyse_the_audio_file_at_once # === do_open_the_file_opener_dialog
1978
+ alias do_open_file do_look_for_a_local_file_and_then_analyse_the_audio_file_at_once # === do_open_file
1979
+ alias open_local_file do_look_for_a_local_file_and_then_analyse_the_audio_file_at_once # === open_local_file
1980
+
1981
+ # ========================================================================= #
1982
+ # === return_button_open_a_random_audio_file
1983
+ # ========================================================================= #
1984
+ def return_button_open_a_random_audio_file(
1985
+ options = nil
1986
+ )
1987
+ use_this_icon_name = 'folder-music'
1988
+ icon = load_icon(use_this_icon_name, 32, 0)
1989
+ case options
1990
+ # ======================================================================= #
1991
+ # === :no_text
1992
+ # ======================================================================= #
1993
+ when :no_text
1994
+ button = create_button
1995
+ button.set_image(icon)
1996
+ else
1997
+ button = create_button_with_label('Open _random audio-file')
1998
+ button.set_image(icon)
1999
+ button.set_always_show_image(true)
2000
+ button.set_border_width(1)
2001
+ button.set_size_request(125, 68)
2002
+ end
2003
+ button.hint =
2004
+ "This button can, upon being clicked, be used to <b>open a random</b> .mp3 "\
2005
+ "file.\n\n"\
2006
+ "Take note that the path to these random audio files default to "\
2007
+ "a hardcoded path specified based on a constant, "\
2008
+ "so you may have to change this to fit to your own "\
2009
+ "computer system.\n\n"+
2010
+ "The constant that determines this is called "\
2011
+ "<span weight=\"bold\" foreground=\"gold\" background=\"black\">PATH_TO_THE_LOCAL_AUDIO_DIRECTORY</span>,"\
2012
+ "and it currently points at "\
2013
+ "<span weight=\"bold\" foreground=\"gold\" background=\"black\">#{@entry_use_this_as_the_directory_to_the_local_songs.text?}</span>,"\
2014
+ ", so make sure that you adapt this to your own setup.\n"
2015
+ button.on_clicked {
2016
+ do_open_a_random_audio_file_and_then_set_the_proper_entry
2017
+ }
2018
+ return button
2019
+ end
2020
+
2021
+ # ========================================================================= #
2022
+ # === add_this_text_to_the_status_bar
2023
+ # ========================================================================= #
2024
+ def add_this_text_to_the_status_bar(this_text)
2025
+ @status_bar.push(
2026
+ @status_bar_context_id, this_text
2027
+ )
2028
+ end
2029
+
2030
+ # ========================================================================= #
2031
+ # === return_random_audio_file
2032
+ #
2033
+ # This method can be used to return a random audio file from a given
2034
+ # (local) directory. This will only work, evidently, when the directory
2035
+ # contains audio files.
2036
+ # ========================================================================= #
2037
+ def return_random_audio_file(
2038
+ from_this_directory =
2039
+ @entry_use_this_as_the_directory_to_the_local_songs.text?
2040
+ )
2041
+ if File.directory? from_this_directory.delete('*')
2042
+ _ = from_this_directory.delete('*')+'*'
2043
+ this_file = Dir[_].select {|entry|
2044
+ entry.end_with?('.mp3')
2045
+ }.sample
2046
+ return this_file
2047
+ end
2048
+ return nil
2049
+ end
2050
+
2051
+ # ========================================================================= #
2052
+ # === widget_top
2053
+ # ========================================================================= #
2054
+ def widget_top
2055
+ # ======================================================================= #
2056
+ # === @header_bar
2057
+ #
2058
+ # This is the widget that will be found on the very top of this
2059
+ # application.
2060
+ # ======================================================================= #
2061
+ _ = default_header_bar
2062
+ _.show_close_button = true
2063
+ _.has_close_button
2064
+ _.set_name('header_bar_on_top')
2065
+ _.set_custom_title(@label_description)
2066
+ _.spacing = 2
2067
+ _.use_this_font = normal_font?
2068
+
2069
+ button_open_a_random_audio_file = return_button_open_a_random_audio_file(:no_text)
2070
+ button_open_a_random_audio_file.hint =
2071
+ 'Click this button to <b>open and assign a random .mp3 file</b>.'
2072
+ _.pack_start(button_open_a_random_audio_file)
2073
+
2074
+ # ======================================================================= #
2075
+ # === Open a local file, as part of the menu-interface
2076
+ # ======================================================================= #
2077
+ open_a_local_file = create_button('Open a local file')
2078
+ open_a_local_file.on_activate {
2079
+ do_open_a_local_file_and_then_set_the_proper_entry
2080
+ }
2081
+ _.pack_start(open_a_local_file)
2082
+
2083
+ # ======================================================================= #
2084
+ # === Show about-information, as part of the top header-bar
2085
+ #
2086
+ # It will be appended, aka .pack_end() is used.
2087
+ # ======================================================================= #
2088
+ button_about = button('About')
2089
+ button_about.hint = 'This will display information about this '\
2090
+ 'application.'
2091
+ button_about.on_clicked {
2092
+ do_show_information_about_this_application
2093
+ }
2094
+ _.pack_end(button_about)
2095
+
2096
+ # ======================================================================= #
2097
+ # === button_show_the_settings
2098
+ # ======================================================================= #
2099
+ button_show_the_settings = create_button('Settings')
2100
+ button_show_the_settings.hint = 'This button allows you to change a few '\
2101
+ 'settings of this application.'
2102
+ button_show_the_settings.on_clicked {
2103
+ do_popup_the_settings_widget
2104
+ }
2105
+ _.pack_start(button_show_the_settings)
2106
+ return _
2107
+ end; alias return_the_header_bar widget_top # === create_the_header_bar
2108
+
2109
+ # ========================================================================= #
2110
+ # === do_assign_a_new_random_audio_file
2111
+ # ========================================================================= #
2112
+ def do_assign_a_new_random_audio_file
2113
+ _ = return_random_audio_file.to_s
2114
+ unless _.empty? # Check against no results.
2115
+ entry_file_path?.set_text(_)
2116
+ do_open_a_random_audio_file_and_then_set_the_proper_entry
2117
+ end
2118
+ end
2119
+
2120
+ # ========================================================================= #
2121
+ # === do_save_the_tags (save tag)
2122
+ #
2123
+ # This method applies all changes by invoking metadata?.update!
2124
+ # ========================================================================= #
2125
+ def do_save_the_tags
2126
+ metadata = metadata?
2127
+ return unless metadata
2128
+ display_information_about_the_song if is_on_roebe?
2129
+ # ======================================================================= #
2130
+ # Squeeze the entry for the comment, so that leading and trailing ' '
2131
+ # are not retained.
2132
+ # ======================================================================= #
2133
+ _ = @entry_for_the_comment.text?
2134
+ if _ and !_.empty?
2135
+ @entry_for_the_comment.set_text(_.strip)
2136
+ end
2137
+ metadata.title = @entry_for_the_title_of_the_song.text
2138
+ metadata.album = @entry_for_the_album.text
2139
+ metadata.artist = @entry_for_the_artist.text
2140
+ metadata.comment = @entry_for_the_comment.text
2141
+ metadata.track = @entry_for_the_track_number.text
2142
+ metadata.year = @entry_for_the_year.text
2143
+ metadata.genre = return_entry_from_the_combo_box_entry_for_genre_select_or_from_the_entry_for_the_genre
2144
+ # ======================================================================= #
2145
+ # Next we will update the dataset and apply the changes made above.
2146
+ # ======================================================================= #
2147
+ metadata.update!
2148
+ display_information_about_the_song if is_on_roebe?
2149
+ # ======================================================================= #
2150
+ # Update the status-bar as well.
2151
+ # ======================================================================= #
2152
+ add_this_text_to_the_status_bar(
2153
+ 'The meta-data was saved (on '+
2154
+ ::MultimediaParadise.return_ss_mm_hh.to_s+').'
2155
+ )
2156
+ end; alias do_save_tags do_save_the_tags # === do_save_tags
2157
+
2158
+ # ========================================================================= #
2159
+ # === update_the_MPEG_entry
2160
+ # ========================================================================= #
2161
+ def update_the_MPEG_entry(
2162
+ i = which_file?
2163
+ )
2164
+ _ = MultimediaParadise.return_the_mpeg_layer_from_this_mp3_file(i)
2165
+ @information_about_a_mp3_file.label_mpeg_level_content.set_text(_.to_s)
2166
+ end
2167
+
2168
+ # ========================================================================= #
2169
+ # === sync_this_text_onto_the_genre_entry
2170
+ #
2171
+ # This method can be used to "sync" onto the entry containing the genre
2172
+ # tag. We won't do any additional sanitizing or checks within this
2173
+ # method, excluding .to_s, so you have to ensure that the given
2174
+ # input is valid, as-is.
2175
+ # ========================================================================= #
2176
+ def sync_this_text_onto_the_genre_entry(i)
2177
+ @entry_for_the_genre.set_text(i.to_s)
2178
+ @entry_for_the_genre.make_bold
2179
+ end
2180
+
2181
+ require 'multimedia_paradise/version/version.rb'
2182
+ # ========================================================================= #
2183
+ # === do_show_information_about_this_application (about tag)
2184
+ # ========================================================================= #
2185
+ def do_show_information_about_this_application
2186
+ dialog = create_about_dialog
2187
+ authors = ['Robert A. Heiler']
2188
+ documenters = ['Robert A. Heiler']
2189
+ begin
2190
+ logo = GdkPixbuf::Pixbuf.new(
2191
+ pixbuf: return_pixbuf_based_on_the_default_icon_theme(:inode_directory)
2192
+ )
2193
+ dialog.set_logo(logo)
2194
+ rescue Exception => error
2195
+ pp error
2196
+ end
2197
+ dialog.set_name('tag_mp3_files')
2198
+ dialog.set_version(::MultimediaParadise::VERSION)
2199
+ dialog.set_copyright("(C) 2022 Robert A. Heiler")
2200
+ dialog.set_comments(
2201
+ "This application can be used to rename the meta-tags "\
2202
+ "of .mp3 files. It is recommended to install the "\
2203
+ "taglib-ruby gem, in order to use this application."
2204
+ )
2205
+ dialog.license = 'GPL-2.0'
2206
+ dialog.set_website(
2207
+ 'https://rubygems.org/gems/multimedia_paradise'
2208
+ )
2209
+ dialog.set_website_label('https://rubygems.org/gems/multimedia_paradise')
2210
+ dialog.set_authors(authors)
2211
+ dialog.set_documenters(documenters)
2212
+ dialog.set_program_name('tag_mp3_files')
2213
+ dialog.signal_connect(:response) {
2214
+ dialog.destroy
2215
+ }
2216
+ dialog.run
2217
+ end
2218
+
2219
+ # ========================================================================= #
2220
+ # === do_analyse_the_audio_file_then_set_all_relevant_entries
2221
+ # ========================================================================= #
2222
+ def do_analyse_the_audio_file_then_set_all_relevant_entries
2223
+ do_analyse_the_audio_file(entry_file_path_text?)
2224
+ set_all_relevant_entries_based_on_the_metadata
2225
+ end
2226
+
2227
+ # ========================================================================= #
2228
+ # === return_the_save_button
2229
+ # ========================================================================= #
2230
+ def return_the_save_button(
2231
+ make_use_of_special_CSS_rules = true
2232
+ )
2233
+ case make_use_of_special_CSS_rules
2234
+ # ======================================================================= #
2235
+ # === :do_not_make_use_of_a_special_CSS_class
2236
+ # ======================================================================= #
2237
+ when :do_not_make_use_of_a_special_CSS_class
2238
+ make_use_of_special_CSS_rules = false
2239
+ end
2240
+ button_save = create_button_with_label(' _Save', use_underline: true) # (:save) # (save tag)
2241
+ use_this_icon_name = 'folder-download'
2242
+ icon = load_icon(use_this_icon_name, 32, 0) # Use the window-close example.
2243
+ button_save.set_image(icon)
2244
+ button_save.set_border_width(1)
2245
+ button_save.set_size_request(125, 68)
2246
+ button_save.set_always_show_image(true)
2247
+ if make_use_of_special_CSS_rules
2248
+ button_save.css_class('save_button')
2249
+ end
2250
+ button_save.hint = 'Click this button in order to <b>save</b> the id3 '\
2251
+ 'tags into the .mp3 file at hand.'
2252
+ button_save.on_clicked {
2253
+ do_save_tags
2254
+ }
2255
+ return button_save
2256
+ end
2257
+
2258
+ # ========================================================================= #
2259
+ # === widget_in_the_middle
2260
+ # ========================================================================= #
2261
+ def widget_in_the_middle
2262
+ @information_about_a_mp3_file
2263
+ end
2264
+
2265
+ # ========================================================================= #
2266
+ # === connect_the_skeleton (connect tag)
2267
+ # ========================================================================= #
2268
+ def connect_the_skeleton
2269
+ abort_on_exception
2270
+
2271
+ outer_vbox = create_vbox
2272
+
2273
+ # ======================================================================= #
2274
+ # The first dragger is the one close to the top of the main application.
2275
+ # It will contain the header bar, as well as the widget that is on
2276
+ # the second row.
2277
+ # ======================================================================= #
2278
+ first_dragger = drag_via_up_and_down_movement(
2279
+ header_bar?, # The header bar on top.
2280
+ widget_second_row # The main entry below the header bar.
2281
+ )
2282
+ first_dragger.position = header_bar_height? # This height should be the height of the header bar.
2283
+ outer_vbox.minimal(first_dragger)
2284
+
2285
+ # ======================================================================= #
2286
+ # Next the middle box - this one will contain several widgets at the
2287
+ # same time.
2288
+ # ======================================================================= #
2289
+ middle_box = drag_via_left_and_right_movement(
2290
+ widget_left,
2291
+ widget_in_the_middle
2292
+ )
2293
+ middle_box.set_name('fancy_separator')
2294
+ middle_box.position = 880
2295
+
2296
+ second_dragger = drag_via_left_and_right_movement(middle_box, widget_right)
2297
+ use_this_position = 1600
2298
+ if use_this_position > max_width?
2299
+ use_this_position = max_width? * 0.8
2300
+ end
2301
+ second_dragger.position = use_this_position
2302
+ # ======================================================================= #
2303
+ # The last dragger will add the status bar to the top.
2304
+ # ======================================================================= #
2305
+ third_dragger = drag_via_up_and_down_movement(
2306
+ second_dragger,
2307
+ @status_bar # The middle vbox contains the three main widgets.
2308
+ )
2309
+ third_dragger.position = 1300
2310
+ outer_vbox.minimal(third_dragger)
2311
+
2312
+ window = create_window_or_runner(nil, width?, height?, title?)
2313
+ window << outer_vbox
2314
+
2315
+ properly_prepare_this_window(window,
2316
+ {
2317
+ title: title?,
2318
+ font: font?,
2319
+ width: width?,
2320
+ height: height?,
2321
+ padding: padding?,
2322
+ border_size: border_size?
2323
+ }
2324
+ )
2325
+ window.show_all
2326
+ window.top_left
2327
+ do_all_startup_related_actions
2328
+ handle_CSS_rules if use_gtk3?
2329
+ run_main
2330
+ end
2331
+
2332
+ # ========================================================================= #
2333
+ # === song_directory?
2334
+ # ========================================================================= #
2335
+ def song_directory?
2336
+ '/home/x/songs/'
2337
+ end
2338
+
2339
+ # ========================================================================= #
2340
+ # === do_play_the_selected_song
2341
+ #
2342
+ # This method can be used to play the currently selected song.
2343
+ #
2344
+ # If no song is selected, a pop-over message should appear.
2345
+ # ========================================================================= #
2346
+ def do_play_the_selected_song(
2347
+ _ = entry_file_path_text?
2348
+ )
2349
+ if _ and File.exist?(_)
2350
+ Thread.new {
2351
+ esystem "mpv #{_}"
2352
+ }
2353
+ else
2354
+ e 'No file exists at '+_.to_s+'.'
2355
+ return_popover(
2356
+ 'Please first select a .mp3 file that exists. You '\
2357
+ 'can double-click on this entry above.',
2358
+ entry_file_path?, 20
2359
+ ).popup
2360
+ end
2361
+ end; alias play_the_selected_audio_file do_play_the_selected_song # === play_the_selected_audio_file
2362
+
2363
+ # ========================================================================= #
2364
+ # === main_file_text?
2365
+ # ========================================================================= #
2366
+ def main_file_text?
2367
+ @entry_file_path.text?
2368
+ end; alias which_file? main_file_text? # === which_file?
2369
+ alias main_file? main_file_text? # === main_file?
2370
+ alias entry_file_path_text? main_file_text? # === entry_file_path_text?
2371
+
2372
+ # ========================================================================= #
2373
+ # === create_the_skeleton (create tag, skeleton tag)
2374
+ #
2375
+ # The order for the various create-method calls is important.
2376
+ # ========================================================================= #
2377
+ def create_the_skeleton
2378
+ create_the_labels # (1) Must come before create_the_header_bar()
2379
+ create_the_entries # (4) Must come before we create the header, the main grid and the buttons.
2380
+ create_the_header_bar # (2)
2381
+ create_the_context_menu # (3) Ok to call it here.
2382
+ create_the_buttons # (5) Must come after we create_the_entries().
2383
+ create_the_status_bar # (6)
2384
+ create_the_status_icon # (7)
2385
+ create_the_settings # (8)
2386
+ end
2387
+
2388
+ # ========================================================================= #
2389
+ # === run (run tag)
2390
+ # ========================================================================= #
2391
+ def run
2392
+ run_super
2393
+ end
2394
+
2395
+ # ========================================================================= #
2396
+ # === MultimediaParadise::GUI::UniversalWidgets::TagMp3Files.run
2397
+ # ========================================================================= #
2398
+ def self.run(
2399
+ i = ARGV
2400
+ )
2401
+ require 'gtk_paradise/run'
2402
+ _ = ::MultimediaParadise::GUI::Gtk::TagMp3Files.new(i)
2403
+ r = ::Gtk.run
2404
+ _.set_parent_widget(r) # Must come before we enable the key-combinations.
2405
+ r << _
2406
+ r.automatic_size_then_automatic_title
2407
+ r.enable_quick_exit
2408
+ r.set_background :white
2409
+ r.on_button_press_event { |widget, event|
2410
+ if ::Gtk.right_mouse_click?(event) # right mouse click event.
2411
+ _.context_menu?.popup_based_on_this_event(event)
2412
+ end
2413
+ }
2414
+ _.parent_widget = r
2415
+ r.top_left_then_run
2416
+ end; self.instance_eval { alias start_gui_application run } # === Roebe::GUI::Gtk::TagMp3Files.start_gui_application
2417
+
2418
+ # ========================================================================= #
2419
+ # === MultimediaParadise::GUI::UniversalWidgets::TagMp3Files[]
2420
+ # ========================================================================= #
2421
+ def self.[](i = ARGV)
2422
+ new(i)
2423
+ end
2424
+
2425
+ end; end; end; end
2426
+
2427
+ if __FILE__ == $PROGRAM_NAME
2428
+ MultimediaParadise::GUI::UniversalWidgets::TagMp3Files.new(ARGV)
2429
+ end # guishell