prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Organizer video tab shows errors if there are video rooms not using BBB
  Organizer video tab shows errors if there are video rooms not using BBB.
  
  ![Screenshot from 2021-11-10 23-44-19](https://user-images.githubusercontent.com/1583873/141205891-371c9945-1294-450b-8b35-3904f41c6af3.png)
  ![Screenshot from 2021-11-10 23-43-45](https://user-images.githubusercontent.com/1583873/141205894-141314cb-58cb-4525-9a1a-f48c2b77f522.png)
  
  Compare https://eventyay.com/events/3ea940a8/video/all (only for organizer accessible)
  
  Related to https://github.com/fossasia/open-event-frontend/pull/7927
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `app/api/video_recordings.py`
  Content:
  ```
  1 from datetime import datetime
  2 
  3 from flask_rest_jsonapi import ResourceDetail, ResourceList
  4 from flask_rest_jsonapi.resource import ResourceRelationship
  5 
  6 from app.api.helpers.db import get_or_create, safe_query_kwargs
  7 from app.api.helpers.errors import ForbiddenError, UnprocessableEntityError
  8 from app.api.helpers.permission_manager import has_access
  9 from app.api.helpers.permissions import jwt_required
  10 from app.api.schema.video_recordings import VideoRecordingSchema
  11 from app.api.video_channels.bbb import BigBlueButton
  12 from app.models import db
  13 from app.models.video_recording import VideoRecording
  14 from app.models.video_stream import VideoStream
  15 
  16 
  17 class VideoRecordingList(ResourceList):
  18     def before_get(self, args, kwargs):
  19         if kwargs.get('video_stream_id'):
  20             stream = safe_query_kwargs(VideoStream, kwargs, 'video_stream_id', 'id')
  21 
  22             if not has_access('is_organizer', event_id=stream.event_id):
  23                 raise ForbiddenError(
  24                     {'pointer': 'event_id'},
  25                     'You need to be the event organizer to access video recordings.',
  26                 )
  27 
  28             params = dict(
  29                 meetingID=stream.extra['response']['meetingID'],
  30             )
  31             channel = stream.channel
  32             bbb = BigBlueButton(channel.api_url, channel.api_key)
  33             result = bbb.request('getRecordings', params)
  34 
  35             if result.data['response']['recordings']:
  36                 recordings = []
  37                 if type(result.data['response']['recordings']['recording']) is list:
  38                     recordings = result.data['response']['recordings']['recording']
  39                 else:
  40                     recordings.append(result.data['response']['recordings']['recording'])
  41                 for recording in recordings:
  42                     get_or_create(
  43                         VideoRecording,
  44                         bbb_record_id=recording['recordID'],
  45                         participants=recording['participants'],
  46                         url=recording['playback']['format']['url'],
  47                         start_time=datetime.fromtimestamp(
  48                             int(int(recording['startTime']) / 1000)
  49                         ),
  50                         end_time=datetime.fromtimestamp(
  51                             int(int(recording['endTime']) / 1000)
  52                         ),
  53                         video_stream=stream,
  54                     )
  55 
  56     def query(self, view_kwargs):
  57         query_ = VideoRecording.query
  58         if view_kwargs.get('video_stream_id'):
  59             stream = safe_query_kwargs(VideoStream, view_kwargs, 'video_stream_id')
  60             query_ = VideoRecording.query.filter(
  61                 VideoRecording.video_stream_id == stream.id
  62             )
  63         else:
  64             if not has_access('is_admin'):
  65                 raise ForbiddenError(
  66                     {'pointer': 'user'},
  67                     'You need to be the admin to access video recordings.',
  68                 )
  69 
  70         return query_
  71 
  72     methods = ['GET']
  73     view_kwargs = True
  74     decorators = (jwt_required,)
  75     schema = VideoRecordingSchema
  76     data_layer = {
  77         'session': db.session,
  78         'model': VideoRecording,
  79         'methods': {
  80             'query': query,
  81             'before_get': before_get,
  82         },
  83     }
  84 
  85 
  86 class VideoRecordingDetail(ResourceDetail):
  87     def before_get_object(self, view_kwargs):
  88         if view_kwargs.get('video_stream_id'):
  89             video_stream = safe_query_kwargs(
  90                 VideoStream,
  91                 view_kwargs,
  92                 'video_stream_id',
  93             )
  94             view_kwargs['id'] = video_stream.id
  95 
  96     def after_get_object(self, video_recording, view_kwargs):
  97         if not has_access('is_organizer', event_id=video_recording.video_stream.event_id):
  98             raise ForbiddenError(
  99                 {'pointer': 'event_id'},
  100                 'You need to be the event organizer to access video recordings.',
  101             )
  102 
  103     def before_delete_object(self, video_recording, kwargs):
  104         """
  105         before delete object method for recording detail
  106         :param obj:
  107         :param kwargs:
  108         :return:
  109         """
  110         if not has_access('is_admin'):
  111             raise ForbiddenError(
  112                 {'source': 'User'}, 'You are not authorized to access this.'
  113             )
  114         stream = video_recording.video_stream
  115         params = dict(
  116             recordID=video_recording.bbb_record_id,
  117         )
  118         channel = stream.channel
  119         bbb = BigBlueButton(channel.api_url, channel.api_key)
  120         result = bbb.request('deleteRecordings', params)
  121 
  122         if not result.success:
  123             raise UnprocessableEntityError(
  124                 {'source': 'recording_id'}, 'error while deleting recording'
  125             )
  126 
  127     methods = ['GET', 'DELETE']
  128     schema = VideoRecordingSchema
  129     decorators = (jwt_required,)
  130     data_layer = {
  131         'session': db.session,
  132         'model': VideoRecording,
  133         'methods': {
  134             'before_get_object': before_get_object,
  135             'after_get_object': after_get_object,
  136             'before_delete_object': before_delete_object,
  137         },
  138     }
  139 
  140 
  141 class VideoRecordingRelationship(ResourceRelationship):
  142     schema = VideoRecordingSchema
  143     methods = ['GET']
  144     data_layer = {'session': db.session, 'model': VideoRecording}
  145
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/app/api/video_recordings.py b/app/api/video_recordings.py
  --- a/app/api/video_recordings.py
  +++ b/app/api/video_recordings.py
  @@ -19,39 +19,48 @@
           if kwargs.get('video_stream_id'):
               stream = safe_query_kwargs(VideoStream, kwargs, 'video_stream_id', 'id')
   
  -            if not has_access('is_organizer', event_id=stream.event_id):
  -                raise ForbiddenError(
  -                    {'pointer': 'event_id'},
  -                    'You need to be the event organizer to access video recordings.',
  -                )
  +            if stream.channel and stream.channel.provider == 'bbb':
  +                if not has_access('is_organizer', event_id=stream.event_id):
  +                    raise ForbiddenError(
  +                        {'pointer': 'event_id'},
  +                        'You need to be the event organizer to access video recordings.',
  +                    )
   
  -            params = dict(
  -                meetingID=stream.extra['response']['meetingID'],
  -            )
  -            channel = stream.channel
  -            bbb = BigBlueButton(channel.api_url, channel.api_key)
  -            result = bbb.request('getRecordings', params)
  -
  -            if result.data['response']['recordings']:
  -                recordings = []
  -                if type(result.data['response']['recordings']['recording']) is list:
  -                    recordings = result.data['response']['recordings']['recording']
  -                else:
  -                    recordings.append(result.data['response']['recordings']['recording'])
  -                for recording in recordings:
  -                    get_or_create(
  -                        VideoRecording,
  -                        bbb_record_id=recording['recordID'],
  -                        participants=recording['participants'],
  -                        url=recording['playback']['format']['url'],
  -                        start_time=datetime.fromtimestamp(
  -                            int(int(recording['startTime']) / 1000)
  -                        ),
  -                        end_time=datetime.fromtimestamp(
  -                            int(int(recording['endTime']) / 1000)
  -                        ),
  -                        video_stream=stream,
  +                if stream.extra is not None:
  +                    params = dict(
  +                        meetingID=stream.extra['response']['meetingID'],
                       )
  +                    channel = stream.channel
  +                    bbb = BigBlueButton(channel.api_url, channel.api_key)
  +                    result = bbb.request('getRecordings', params)
  +
  +                    if result.data['response']['recordings']:
  +                        recordings = []
  +                        if (
  +                            type(result.data['response']['recordings']['recording'])
  +                            is list
  +                        ):
  +                            recordings = result.data['response']['recordings'][
  +                                'recording'
  +                            ]
  +                        else:
  +                            recordings.append(
  +                                result.data['response']['recordings']['recording']
  +                            )
  +                        for recording in recordings:
  +                            get_or_create(
  +                                VideoRecording,
  +                                bbb_record_id=recording['recordID'],
  +                                participants=recording['participants'],
  +                                url=recording['playback']['format']['url'],
  +                                start_time=datetime.fromtimestamp(
  +                                    int(int(recording['startTime']) / 1000)
  +                                ),
  +                                end_time=datetime.fromtimestamp(
  +                                    int(int(recording['endTime']) / 1000)
  +                                ),
  +                                video_stream=stream,
  +                            )
   
       def query(self, view_kwargs):
           query_ = VideoRecording.query
  
verification_info: {"golden_diff": "diff --git a/app/api/video_recordings.py b/app/api/video_recordings.py\n--- a/app/api/video_recordings.py\n+++ b/app/api/video_recordings.py\n@@ -19,39 +19,48 @@\n         if kwargs.get('video_stream_id'):\n             stream = safe_query_kwargs(VideoStream, kwargs, 'video_stream_id', 'id')\n \n-            if not has_access('is_organizer', event_id=stream.event_id):\n-                raise ForbiddenError(\n-                    {'pointer': 'event_id'},\n-                    'You need to be the event organizer to access video recordings.',\n-                )\n+            if stream.channel and stream.channel.provider == 'bbb':\n+                if not has_access('is_organizer', event_id=stream.event_id):\n+                    raise ForbiddenError(\n+                        {'pointer': 'event_id'},\n+                        'You need to be the event organizer to access video recordings.',\n+                    )\n \n-            params = dict(\n-                meetingID=stream.extra['response']['meetingID'],\n-            )\n-            channel = stream.channel\n-            bbb = BigBlueButton(channel.api_url, channel.api_key)\n-            result = bbb.request('getRecordings', params)\n-\n-            if result.data['response']['recordings']:\n-                recordings = []\n-                if type(result.data['response']['recordings']['recording']) is list:\n-                    recordings = result.data['response']['recordings']['recording']\n-                else:\n-                    recordings.append(result.data['response']['recordings']['recording'])\n-                for recording in recordings:\n-                    get_or_create(\n-                        VideoRecording,\n-                        bbb_record_id=recording['recordID'],\n-                        participants=recording['participants'],\n-                        url=recording['playback']['format']['url'],\n-                        start_time=datetime.fromtimestamp(\n-                            int(int(recording['startTime']) / 1000)\n-                        ),\n-                        end_time=datetime.fromtimestamp(\n-                            int(int(recording['endTime']) / 1000)\n-                        ),\n-                        video_stream=stream,\n+                if stream.extra is not None:\n+                    params = dict(\n+                        meetingID=stream.extra['response']['meetingID'],\n                     )\n+                    channel = stream.channel\n+                    bbb = BigBlueButton(channel.api_url, channel.api_key)\n+                    result = bbb.request('getRecordings', params)\n+\n+                    if result.data['response']['recordings']:\n+                        recordings = []\n+                        if (\n+                            type(result.data['response']['recordings']['recording'])\n+                            is list\n+                        ):\n+                            recordings = result.data['response']['recordings'][\n+                                'recording'\n+                            ]\n+                        else:\n+                            recordings.append(\n+                                result.data['response']['recordings']['recording']\n+                            )\n+                        for recording in recordings:\n+                            get_or_create(\n+                                VideoRecording,\n+                                bbb_record_id=recording['recordID'],\n+                                participants=recording['participants'],\n+                                url=recording['playback']['format']['url'],\n+                                start_time=datetime.fromtimestamp(\n+                                    int(int(recording['startTime']) / 1000)\n+                                ),\n+                                end_time=datetime.fromtimestamp(\n+                                    int(int(recording['endTime']) / 1000)\n+                                ),\n+                                video_stream=stream,\n+                            )\n \n     def query(self, view_kwargs):\n         query_ = VideoRecording.query\n", "issue": "Organizer video tab shows errors if there are video rooms not using BBB\nOrganizer video tab shows errors if there are video rooms not using BBB.\r\n\r\n![Screenshot from 2021-11-10 23-44-19](https://user-images.githubusercontent.com/1583873/141205891-371c9945-1294-450b-8b35-3904f41c6af3.png)\r\n![Screenshot from 2021-11-10 23-43-45](https://user-images.githubusercontent.com/1583873/141205894-141314cb-58cb-4525-9a1a-f48c2b77f522.png)\r\n\r\nCompare https://eventyay.com/events/3ea940a8/video/all (only for organizer accessible)\r\n\r\nRelated to https://github.com/fossasia/open-event-frontend/pull/7927\n", "before_files": [{"content": "from datetime import datetime\n\nfrom flask_rest_jsonapi import ResourceDetail, ResourceList\nfrom flask_rest_jsonapi.resource import ResourceRelationship\n\nfrom app.api.helpers.db import get_or_create, safe_query_kwargs\nfrom app.api.helpers.errors import ForbiddenError, UnprocessableEntityError\nfrom app.api.helpers.permission_manager import has_access\nfrom app.api.helpers.permissions import jwt_required\nfrom app.api.schema.video_recordings import VideoRecordingSchema\nfrom app.api.video_channels.bbb import BigBlueButton\nfrom app.models import db\nfrom app.models.video_recording import VideoRecording\nfrom app.models.video_stream import VideoStream\n\n\nclass VideoRecordingList(ResourceList):\n    def before_get(self, args, kwargs):\n        if kwargs.get('video_stream_id'):\n            stream = safe_query_kwargs(VideoStream, kwargs, 'video_stream_id', 'id')\n\n            if not has_access('is_organizer', event_id=stream.event_id):\n                raise ForbiddenError(\n                    {'pointer': 'event_id'},\n                    'You need to be the event organizer to access video recordings.',\n                )\n\n            params = dict(\n                meetingID=stream.extra['response']['meetingID'],\n            )\n            channel = stream.channel\n            bbb = BigBlueButton(channel.api_url, channel.api_key)\n            result = bbb.request('getRecordings', params)\n\n            if result.data['response']['recordings']:\n                recordings = []\n                if type(result.data['response']['recordings']['recording']) is list:\n                    recordings = result.data['response']['recordings']['recording']\n                else:\n                    recordings.append(result.data['response']['recordings']['recording'])\n                for recording in recordings:\n                    get_or_create(\n                        VideoRecording,\n                        bbb_record_id=recording['recordID'],\n                        participants=recording['participants'],\n                        url=recording['playback']['format']['url'],\n                        start_time=datetime.fromtimestamp(\n                            int(int(recording['startTime']) / 1000)\n                        ),\n                        end_time=datetime.fromtimestamp(\n                            int(int(recording['endTime']) / 1000)\n                        ),\n                        video_stream=stream,\n                    )\n\n    def query(self, view_kwargs):\n        query_ = VideoRecording.query\n        if view_kwargs.get('video_stream_id'):\n            stream = safe_query_kwargs(VideoStream, view_kwargs, 'video_stream_id')\n            query_ = VideoRecording.query.filter(\n                VideoRecording.video_stream_id == stream.id\n            )\n        else:\n            if not has_access('is_admin'):\n                raise ForbiddenError(\n                    {'pointer': 'user'},\n                    'You need to be the admin to access video recordings.',\n                )\n\n        return query_\n\n    methods = ['GET']\n    view_kwargs = True\n    decorators = (jwt_required,)\n    schema = VideoRecordingSchema\n    data_layer = {\n        'session': db.session,\n        'model': VideoRecording,\n        'methods': {\n            'query': query,\n            'before_get': before_get,\n        },\n    }\n\n\nclass VideoRecordingDetail(ResourceDetail):\n    def before_get_object(self, view_kwargs):\n        if view_kwargs.get('video_stream_id'):\n            video_stream = safe_query_kwargs(\n                VideoStream,\n                view_kwargs,\n                'video_stream_id',\n            )\n            view_kwargs['id'] = video_stream.id\n\n    def after_get_object(self, video_recording, view_kwargs):\n        if not has_access('is_organizer', event_id=video_recording.video_stream.event_id):\n            raise ForbiddenError(\n                {'pointer': 'event_id'},\n                'You need to be the event organizer to access video recordings.',\n            )\n\n    def before_delete_object(self, video_recording, kwargs):\n        \"\"\"\n        before delete object method for recording detail\n        :param obj:\n        :param kwargs:\n        :return:\n        \"\"\"\n        if not has_access('is_admin'):\n            raise ForbiddenError(\n                {'source': 'User'}, 'You are not authorized to access this.'\n            )\n        stream = video_recording.video_stream\n        params = dict(\n            recordID=video_recording.bbb_record_id,\n        )\n        channel = stream.channel\n        bbb = BigBlueButton(channel.api_url, channel.api_key)\n        result = bbb.request('deleteRecordings', params)\n\n        if not result.success:\n            raise UnprocessableEntityError(\n                {'source': 'recording_id'}, 'error while deleting recording'\n            )\n\n    methods = ['GET', 'DELETE']\n    schema = VideoRecordingSchema\n    decorators = (jwt_required,)\n    data_layer = {\n        'session': db.session,\n        'model': VideoRecording,\n        'methods': {\n            'before_get_object': before_get_object,\n            'after_get_object': after_get_object,\n            'before_delete_object': before_delete_object,\n        },\n    }\n\n\nclass VideoRecordingRelationship(ResourceRelationship):\n    schema = VideoRecordingSchema\n    methods = ['GET']\n    data_layer = {'session': db.session, 'model': VideoRecording}\n", "path": "app/api/video_recordings.py"}], "after_files": [{"content": "from datetime import datetime\n\nfrom flask_rest_jsonapi import ResourceDetail, ResourceList\nfrom flask_rest_jsonapi.resource import ResourceRelationship\n\nfrom app.api.helpers.db import get_or_create, safe_query_kwargs\nfrom app.api.helpers.errors import ForbiddenError, UnprocessableEntityError\nfrom app.api.helpers.permission_manager import has_access\nfrom app.api.helpers.permissions import jwt_required\nfrom app.api.schema.video_recordings import VideoRecordingSchema\nfrom app.api.video_channels.bbb import BigBlueButton\nfrom app.models import db\nfrom app.models.video_recording import VideoRecording\nfrom app.models.video_stream import VideoStream\n\n\nclass VideoRecordingList(ResourceList):\n    def before_get(self, args, kwargs):\n        if kwargs.get('video_stream_id'):\n            stream = safe_query_kwargs(VideoStream, kwargs, 'video_stream_id', 'id')\n\n            if stream.channel and stream.channel.provider == 'bbb':\n                if not has_access('is_organizer', event_id=stream.event_id):\n                    raise ForbiddenError(\n                        {'pointer': 'event_id'},\n                        'You need to be the event organizer to access video recordings.',\n                    )\n\n                if stream.extra is not None:\n                    params = dict(\n                        meetingID=stream.extra['response']['meetingID'],\n                    )\n                    channel = stream.channel\n                    bbb = BigBlueButton(channel.api_url, channel.api_key)\n                    result = bbb.request('getRecordings', params)\n\n                    if result.data['response']['recordings']:\n                        recordings = []\n                        if (\n                            type(result.data['response']['recordings']['recording'])\n                            is list\n                        ):\n                            recordings = result.data['response']['recordings'][\n                                'recording'\n                            ]\n                        else:\n                            recordings.append(\n                                result.data['response']['recordings']['recording']\n                            )\n                        for recording in recordings:\n                            get_or_create(\n                                VideoRecording,\n                                bbb_record_id=recording['recordID'],\n                                participants=recording['participants'],\n                                url=recording['playback']['format']['url'],\n                                start_time=datetime.fromtimestamp(\n                                    int(int(recording['startTime']) / 1000)\n                                ),\n                                end_time=datetime.fromtimestamp(\n                                    int(int(recording['endTime']) / 1000)\n                                ),\n                                video_stream=stream,\n                            )\n\n    def query(self, view_kwargs):\n        query_ = VideoRecording.query\n        if view_kwargs.get('video_stream_id'):\n            stream = safe_query_kwargs(VideoStream, view_kwargs, 'video_stream_id')\n            query_ = VideoRecording.query.filter(\n                VideoRecording.video_stream_id == stream.id\n            )\n        else:\n            if not has_access('is_admin'):\n                raise ForbiddenError(\n                    {'pointer': 'user'},\n                    'You need to be the admin to access video recordings.',\n                )\n\n        return query_\n\n    methods = ['GET']\n    view_kwargs = True\n    decorators = (jwt_required,)\n    schema = VideoRecordingSchema\n    data_layer = {\n        'session': db.session,\n        'model': VideoRecording,\n        'methods': {\n            'query': query,\n            'before_get': before_get,\n        },\n    }\n\n\nclass VideoRecordingDetail(ResourceDetail):\n    def before_get_object(self, view_kwargs):\n        if view_kwargs.get('video_stream_id'):\n            video_stream = safe_query_kwargs(\n                VideoStream,\n                view_kwargs,\n                'video_stream_id',\n            )\n            view_kwargs['id'] = video_stream.id\n\n    def after_get_object(self, video_recording, view_kwargs):\n        if not has_access('is_organizer', event_id=video_recording.video_stream.event_id):\n            raise ForbiddenError(\n                {'pointer': 'event_id'},\n                'You need to be the event organizer to access video recordings.',\n            )\n\n    def before_delete_object(self, video_recording, kwargs):\n        \"\"\"\n        before delete object method for recording detail\n        :param obj:\n        :param kwargs:\n        :return:\n        \"\"\"\n        if not has_access('is_admin'):\n            raise ForbiddenError(\n                {'source': 'User'}, 'You are not authorized to access this.'\n            )\n        stream = video_recording.video_stream\n        params = dict(\n            recordID=video_recording.bbb_record_id,\n        )\n        channel = stream.channel\n        bbb = BigBlueButton(channel.api_url, channel.api_key)\n        result = bbb.request('deleteRecordings', params)\n\n        if not result.success:\n            raise UnprocessableEntityError(\n                {'source': 'recording_id'}, 'error while deleting recording'\n            )\n\n    methods = ['GET', 'DELETE']\n    schema = VideoRecordingSchema\n    decorators = (jwt_required,)\n    data_layer = {\n        'session': db.session,\n        'model': VideoRecording,\n        'methods': {\n            'before_get_object': before_get_object,\n            'after_get_object': after_get_object,\n            'before_delete_object': before_delete_object,\n        },\n    }\n\n\nclass VideoRecordingRelationship(ResourceRelationship):\n    schema = VideoRecordingSchema\n    methods = ['GET']\n    data_layer = {'session': db.session, 'model': VideoRecording}\n", "path": "app/api/video_recordings.py"}]}