utilrecorder.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. Copyright (C) 2013 Borsato Ivano
  3. The JavaScript code in this page is free software: you can
  4. redistribute it and/or modify it under the terms of the GNU
  5. General Public License (GNU GPL) as published by the Free Software
  6. Foundation, either version 3 of the License, or (at your option)
  7. any later version. The code is distributed WITHOUT ANY WARRANTY;
  8. without even the implied warranty of MERCHANTABILITY or FITNESS
  9. FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
  10. */
  11. 'use strict';
  12. import GObject from 'gi://GObject';
  13. import Gio from 'gi://Gio';
  14. import GLib from 'gi://GLib';
  15. import {loadInterfaceXML} from 'resource:///org/gnome/shell/misc/dbusUtils.js';
  16. const ScreencastIface = loadInterfaceXML('org.gnome.Shell.Screencast');
  17. import * as Lib from './convenience.js';
  18. import * as Settings from './settings.js';
  19. import * as Selection from './selection.js';
  20. import * as UtilGSP from './utilgsp.js';
  21. import * as Ext from './extension.js';
  22. /**
  23. * @type {CaptureVideo}
  24. */
  25. export const CaptureVideo = GObject.registerClass({
  26. GTypeName: 'EasyScreenCast_CaptureVideo',
  27. }, class CaptureVideo extends GObject.Object {
  28. /**
  29. * Create a video recorder
  30. */
  31. constructor() {
  32. super();
  33. Lib.TalkativeLog('-&-init recorder');
  34. this.AreaSelected = null;
  35. // connect to d-bus service
  36. const ScreenCastProxy = Gio.DBusProxy.makeProxyWrapper(ScreencastIface);
  37. this._screenCastService = new ScreenCastProxy(
  38. Gio.DBus.session,
  39. 'org.gnome.Shell.Screencast',
  40. '/org/gnome/Shell/Screencast',
  41. (proxy, error) => {
  42. if (error)
  43. Lib.TalkativeLog(`-&-ERROR(d-bus proxy connected) - ${error.message}`);
  44. else
  45. Lib.TalkativeLog('-&-d-bus proxy connected');
  46. }
  47. );
  48. }
  49. /**
  50. * start recording
  51. */
  52. start() {
  53. Lib.TalkativeLog('-&-start video recording');
  54. this.recordingActive = false;
  55. let fileExt = UtilGSP.getFileExtension(
  56. Ext.Indicator.getSettings().getOption('i', Settings.FILE_CONTAINER_SETTING_KEY)
  57. );
  58. // prepare variable for screencast
  59. let fileRec = Ext.Indicator.getSettings().getOption('s', Settings.FILE_NAME_SETTING_KEY);
  60. let folderRec = '';
  61. if (Ext.Indicator.getSettings().getOption('s', Settings.FILE_FOLDER_SETTING_KEY) !== '')
  62. folderRec = Ext.Indicator.getSettings().getOption('s', Settings.FILE_FOLDER_SETTING_KEY);
  63. let pipelineRec = '';
  64. if (Ext.Indicator.getSettings().getOption('b', Settings.ACTIVE_CUSTOM_GSP_SETTING_KEY)) {
  65. pipelineRec = Ext.Indicator.getSettings().getOption(
  66. 's',
  67. Settings.PIPELINE_REC_SETTING_KEY
  68. );
  69. } else {
  70. // compose GSP
  71. pipelineRec = UtilGSP.composeGSP(Ext.Indicator.getSettings(), Ext.Indicator.CtrlAudio);
  72. }
  73. Lib.TalkativeLog(`-&-file template : ${fileRec}`);
  74. fileRec = this._generateFileName(fileRec);
  75. Lib.TalkativeLog(`-&-file final : ${fileRec}`);
  76. const completeFileRecPath = folderRec !== ''
  77. ? `${folderRec}/${fileRec}`
  78. : fileRec;
  79. Lib.TalkativeLog(`-&-file rec path complete : ${completeFileRecPath}${fileExt}`);
  80. // prefix with a videoconvert element
  81. // see DEFAULT_PIPELINE in https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/dbusServices/screencast/screencastService.js#L26
  82. // this videoconvert element was added always previously (< Gnome 40) and needs to be added now explicitly
  83. // https://gitlab.gnome.org/GNOME/gnome-shell/-/commit/51bf7ec17617a9ed056dd563afdb98e17da07373
  84. pipelineRec = `videoconvert chroma-mode=GST_VIDEO_CHROMA_MODE_NONE dither=GST_VIDEO_DITHER_NONE matrix-mode=GST_VIDEO_MATRIX_MODE_OUTPUT_ONLY n-threads=%T ! queue ! ${pipelineRec}`;
  85. Lib.TalkativeLog(`-&-pipeline : pipeline: ${pipelineRec}`);
  86. var optionsRec = {
  87. 'draw-cursor': new GLib.Variant(
  88. 'b',
  89. Ext.Indicator.getSettings().getOption('b', Settings.DRAW_CURSOR_SETTING_KEY)
  90. ),
  91. framerate: new GLib.Variant(
  92. 'i',
  93. Ext.Indicator.getSettings().getOption('i', Settings.FPS_SETTING_KEY)
  94. ),
  95. pipeline: new GLib.Variant('s', pipelineRec),
  96. };
  97. if (Ext.Indicator.getSettings().getOption('i', Settings.AREA_SCREEN_SETTING_KEY) === 0) {
  98. this._screenCastService.ScreencastRemote(
  99. completeFileRecPath,
  100. optionsRec,
  101. (result, error) => {
  102. if (error) {
  103. Lib.TalkativeLog(`-&-ERROR(screencast execute) - ${error.message}`);
  104. this.stop();
  105. Ext.Indicator.doRecResult(false);
  106. } else {
  107. Lib.TalkativeLog(`-&-screencast execute - ${result[0]} - ${result[1]}`);
  108. let resultingFilePath = result[1];
  109. if (resultingFilePath.endsWith('.undefined')) {
  110. resultingFilePath = resultingFilePath.substring(0, resultingFilePath.length - '.undefined'.length);
  111. resultingFilePath = `${resultingFilePath}${fileExt}`;
  112. }
  113. this._originalFilePath = result[1];
  114. this._filePathWithExtension = resultingFilePath;
  115. Ext.Indicator.doRecResult(result[0], resultingFilePath);
  116. }
  117. }
  118. );
  119. } else {
  120. this._screenCastService.ScreencastAreaRemote(
  121. Ext.Indicator.getSettings().getOption('i', Settings.X_POS_SETTING_KEY),
  122. Ext.Indicator.getSettings().getOption('i', Settings.Y_POS_SETTING_KEY),
  123. Ext.Indicator.getSettings().getOption('i', Settings.WIDTH_SETTING_KEY),
  124. Ext.Indicator.getSettings().getOption('i', Settings.HEIGHT_SETTING_KEY),
  125. completeFileRecPath,
  126. optionsRec,
  127. (result, error) => {
  128. if (error) {
  129. Lib.TalkativeLog(`-&-ERROR(screencast execute) - ${error.message}`);
  130. this.stop();
  131. Ext.Indicator.doRecResult(false);
  132. } else {
  133. Lib.TalkativeLog(`-&-screencast execute - ${result[0]} - ${result[1]}`);
  134. // draw area recording
  135. if (Ext.Indicator.getSettings().getOption('b', Settings.SHOW_AREA_REC_SETTING_KEY))
  136. this.AreaSelected = new Selection.AreaRecording();
  137. let resultingFilePath = result[1];
  138. if (resultingFilePath.endsWith('.undefined')) {
  139. resultingFilePath = resultingFilePath.substring(0, resultingFilePath.length - '.undefined'.length);
  140. resultingFilePath = `${resultingFilePath}${fileExt}`;
  141. }
  142. this._originalFilePath = result[1];
  143. this._filePathWithExtension = resultingFilePath;
  144. Ext.Indicator.doRecResult(result[0], resultingFilePath);
  145. }
  146. }
  147. );
  148. }
  149. }
  150. /**
  151. * Stop recording
  152. *
  153. * @returns {boolean}
  154. */
  155. stop() {
  156. Lib.TalkativeLog('-&-stop video recording');
  157. this._screenCastService.StopScreencastRemote((result, error) => {
  158. if (error) {
  159. Lib.TalkativeLog(`-&-ERROR(screencast stop) - ${error.message}`);
  160. return false;
  161. } else {
  162. Lib.TalkativeLog(`-&-screencast stop - ${result[0]}`);
  163. // rename the file...
  164. Lib.TalkativeLog(`-&-screencast: rename ${this._originalFilePath} to ${this._filePathWithExtension}`);
  165. const sourceFile = Gio.File.new_for_path(this._originalFilePath);
  166. const destFile = Gio.File.new_for_path(this._filePathWithExtension);
  167. sourceFile.move(destFile, 0, null, null);
  168. }
  169. // clear area recording
  170. if (this.AreaSelected !== null && this.AreaSelected.isVisible())
  171. this.AreaSelected.clearArea();
  172. return true;
  173. });
  174. }
  175. // without file extension
  176. _generateFileName(template) {
  177. template = template.replaceAll('%d', '%0x').replaceAll('%t', '%0X');
  178. const datetime = GLib.DateTime.new_now_local();
  179. let result = datetime.format(template);
  180. result = result.replaceAll(' ', '_'); // remove white space
  181. result = result.replaceAll('/', '_'); // remove path separators
  182. return result;
  183. }
  184. });