def update_community(self, community_id, args) -> (dict, MassEnergizeAPIError): try: logo = args.pop('logo', None) community = Community.objects.filter(id=community_id) if not community: return None, InvalidResourceError() if not args.get('is_geographically_focused', False): args['location'] = None community.update(**args) new_community = community.first() # if logo and new_community.logo: # can't update the logo if the community doesn't have one already? # # new_community.logo.file = logo # # new_community.logo.save() if logo: cLogo = Media(file=logo, name=f"{args.get('name', '')} CommunityLogo") cLogo.save() new_community.logo = cLogo new_community.save() return new_community, None except Exception as e: return None, CustomMassenergizeError(e)
def update_vendor(self, context: Context, args) -> Tuple[dict, MassEnergizeAPIError]: try: vendor_id = args.pop('vendor_id', None) communities = args.pop('communities', []) onboarding_contact_email = args.pop('onboarding_contact_email', None) website = args.pop('website', None) key_contact_name = args.pop('key_contact_name', None) key_contact_email = args.pop('key_contact_email', None) key_contact = { "name": key_contact_name, "email": key_contact_email } image = args.pop('image', None) tags = args.pop('tags', []) have_address = args.pop('have_address', False) if not have_address: args['location'] = None vendor = Vendor.objects.filter(id=vendor_id) if not vendor: return None, InvalidResourceError() vendor.update(**args) vendor = vendor.first() if communities: vendor.communities.set(communities) if onboarding_contact_email: vendor.onboarding_contact_email = onboarding_contact_email if key_contact: if vendor.key_contact: vendor.key_contact.update(key_contact) else: vendor.key_contact = key_contact if image: logo = Media(name=f"Logo-{slugify(vendor.name)}", file=image) logo.save() vendor.logo = logo if onboarding_contact_email: onboarding_contact = UserProfile.objects.filter(email=onboarding_contact_email).first() if onboarding_contact: vendor.onboarding_contact = onboarding_contact if tags: vendor.tags.set(tags) if website: vendor.more_info = {'website': website} vendor.save() return vendor, None except Exception as e: capture_message(str(e), level="error") return None, CustomMassenergizeError(e)
def update_vendor(self, context: Context, args) -> (dict, MassEnergizeAPIError): try: vendor_id = args.pop('vendor_id', None) vendor = Vendor.objects.get(id=vendor_id) if not vendor: return None, InvalidResourceError() print(vendor) have_address = args.pop('have_address', False) if not have_address: args['location'] = None communities = args.pop('communities', []) if communities: vendor.communities.set(communities) onboarding_contact_email = args.pop('onboarding_contact_email', None) if onboarding_contact_email: vendor.onboarding_contact_email = onboarding_contact_email key_contact = args.pop('key_contact', {}) if key_contact: if vendor.key_contact: vendor.key_contact.update(key_contact) else: vendor.key_contact = args.pop('key_contact', key_contact) image = args.pop('image', None) if image: logo = Media(name=f"Logo-{slugify(vendor.name)}", file=image) logo.save() vendor.logo = logo if onboarding_contact_email: onboarding_contact = UserProfile.objects.filter( email=onboarding_contact_email).first() if onboarding_contact: vendor.onboarding_contact = onboarding_contact tags = args.pop('tags', []) if tags: vendor.tags.set(tags) vendor.save() updated = Vendor.objects.filter(id=vendor_id).update(**args) return vendor, None except Exception as e: print(e) return None, CustomMassenergizeError(e)
async def upload_file(background_tasks: BackgroundTasks, response: Response, file: UploadFile = File(...), folder: Optional[str] = Form(''), childOf: Optional[str] = Form(''), identity: str = Depends(get_jwt_identity)): try: if file.filename == '' or (len(folder) > 0 and folder[0] == '/'): raise SchemaValidationError User.objects.get(id=identity) # make sure the user exists if allowed_file(file.filename): # Handle filename collisions filename = file.filename counter = 2 while True: try: Media.objects.get(filename=filename, folder=folder) newFilename = filename filenameSplit = newFilename.rsplit('.', 1) filename = filenameSplit[0] + '_' + str(counter) + '.' + filenameSplit[1] counter += 1 except DoesNotExist: break mimetype = file.content_type if not mimetype: mimetype = mimetypes.guess_type(filename) if FileSettings.ENABLE_FFMPEG and FileSettings.ENABLE_FILE_PROCESSING and (mimetype[:6] == 'video/' or mimetype[:6] == 'audio/' or mimetype == 'application/x-subrip' or mimetype == 'application/ttml+xml'): # Process the file splitFilename = filename.rsplit('.', 1) media = Media(owner=identity, filename=splitFilename[0], folder=folder, container=True, processing=True) media.save() background_tasks.add_task(processMedia, media, file, splitFilename[1]) #processMedia(media, file) response.status_code = 202 else: media = Media(owner=identity, filename=filename, folder=folder) media.file.put(file.file, content_type=mimetype) media.save() for parent in childOf.split(','): try: if parent: Media.objects.get(id=parent).update(push__associatedMedia=media) except DoesNotExist: pass return media.serialize() raise SchemaValidationError except SchemaValidationError as e: raise SchemaValidationError().http_exception except DoesNotExist: raise UnauthorizedError().http_exception except MediaProcessingError: raise MediaProcessingError().http_exception except Exception as e: raise e
def create_vendor(self, ctx: Context, args) -> (Vendor, MassEnergizeAPIError): try: tags = args.pop('tags', []) communities = args.pop('communities', []) image = args.pop('image', None) onboarding_contact_email = args.pop('onboarding_contact_email', None) key_contact_full_name = args.pop('key_contact_full_name', None) key_contact_email = args.pop('key_contact_email', None) website = args.pop('website', None) args["key_contact"] = { "name": key_contact_full_name, "email": key_contact_email } have_address = args.pop('have_address', False) if not have_address: args['location'] = None new_vendor = Vendor.objects.create(**args) if image: logo = Media(name=f"Logo-{slugify(new_vendor.name)}", file=image) logo.save() new_vendor.logo = logo if onboarding_contact_email: onboarding_contact = UserProfile.objects.filter( email=onboarding_contact_email).first() if onboarding_contact: new_vendor.onboarding_contact = onboarding_contact if website: new_vendor.more_info = {'website': website} new_vendor.save() if communities: new_vendor.communities.set(communities) if tags: new_vendor.tags.set(tags) new_vendor.save() return new_vendor, None except Exception as e: capture_message(str(e), level="error") return None, CustomMassenergizeError(e)
def add_media(request, cid): capsule = Capsule.objects.filter(cid=cid, deleted=False).get() authorized = check_authorized(Capsule, cid, request.user.username, 'add') if isinstance(authorized, JsonResponse): return authorized owner = request.user media = Media(owner=owner, capsule=capsule) media.save() media.file = request.FILES['file'] media.save() return JsonResponse({ "status": "resource created", "mid": media.mid }, status=200)
def update_user(self, context: Context, args) -> Tuple[dict, MassEnergizeAPIError]: try: user_id = args.get('id', None) email = args.get('email', None) if not self._has_access(context, user_id, email): return None, CustomMassenergizeError("permission_denied") if context.user_is_logged_in and ((context.user_id == user_id) or (context.user_is_admin())): users = UserProfile.objects.filter(id=user_id) if not users: return None, InvalidResourceError() profile_picture = args.pop("profile_picture", None) users.update(**args) user = users.first() if profile_picture: if profile_picture == "reset": user.profile_picture = None user.save() else: pic = Media() pic.name = f'{user.full_name} profpic' pic.file = profile_picture pic.media_type = 'image' pic.save() user.profile_picture = pic user.save() return user, None else: return None, CustomMassenergizeError('permission_denied') except Exception as e: capture_message(str(e), level="error") return None, CustomMassenergizeError(e)
def create_community(self, context: Context, args) -> (dict, MassEnergizeAPIError): try: logo = args.pop('logo', None) new_community = Community.objects.create(**args) have_address = args.get('is_geographically_focused', False) if not have_address: args['location'] = None if logo: cLogo = Media(file=logo, name=f"{args.get('name', '')} CommunityLogo") cLogo.save() new_community.logo = cLogo #create a goal for this community community_goal = Goal.objects.create( name=f"{new_community.name}-Goal") new_community.goal = community_goal new_community.save() #now create all the pages aboutUsPage = AboutUsPageSettings.objects.filter( is_template=True).first() if aboutUsPage: aboutUsPage.pk = None aboutUsPage.title = f"About {new_community.name}" aboutUsPage.community = new_community aboutUsPage.is_template = False aboutUsPage.save() actionsPage = ActionsPageSettings.objects.filter( is_template=True).first() if actionsPage: actionsPage.pk = None actionsPage.title = f"Actions for {new_community.name}" actionsPage.community = new_community actionsPage.is_template = False actionsPage.save() contactUsPage = ContactUsPageSettings.objects.filter( is_template=True).first() if contactUsPage: contactUsPage.pk = None contactUsPage.title = f"Contact Us - {new_community.name}" contactUsPage.community = new_community contactUsPage.is_template = False contactUsPage.save() donatePage = DonatePageSettings.objects.filter( is_template=True).first() if donatePage: donatePage.pk = None donatePage.title = f"Take Actions - {new_community.name}" donatePage.community = new_community donatePage.is_template = False donatePage.save() homePage = HomePageSettings.objects.filter( is_template=True).first() images = homePage.images.all() #TODO: make a copy of the images instead, then in the home page, you wont have to create new files everytime if homePage: homePage.pk = None homePage.title = f"Welcome to Massenergize, {new_community.name}!" homePage.community = new_community homePage.is_template = False homePage.save() homePage.images.set(images) impactPage = ImpactPageSettings.objects.filter( is_template=True).first() if impactPage: impactPage.pk = None impactPage.title = f"See our Impact - {new_community.name}" impactPage.community = new_community impactPage.is_template = False impactPage.save() comm_admin: CommunityAdminGroup = CommunityAdminGroup.objects.create( name=f"{new_community.name}-Admin-Group", community=new_community) comm_admin.save() if context.user_id: user = UserProfile.objects.filter(pk=context.user_id).first() if user: comm_admin.members.add(user) comm_admin.save() owner_email = args.get('owner_email', None) if owner_email: owner = UserProfile.objects.filter(email=owner_email).first() if owner: comm_admin.members.add(owner) comm_admin.save() #also clone all global actions for this community global_actions = Action.objects.filter(is_global=True) for action_to_copy in global_actions: old_tags = action_to_copy.tags.all() old_vendors = action_to_copy.vendors.all() new_action = action_to_copy new_action.pk = None new_action.community = None new_action.is_published = False new_action.title = action_to_copy.title + f' Copy {random.randint(1,10000)}' new_action.save() new_action.tags.set(old_tags) new_action.vendors.set(old_vendors) return new_community, None except Exception as e: return None, CustomMassenergizeError(e)
def update_home_page_setting(self, args) -> (dict, MassEnergizeAPIError): try: home_page_id = args.get('id', None) home_page_setting = HomePageSettings.objects.filter(id=home_page_id).first() if not home_page_setting: return None, InvalidResourceError() #events if (args.get('show_featured_events', None)): featured_events = args.pop('featured_events', []) home_page_setting.featured_events.set(featured_events) else: args.pop('featured_events', []) #stats goal_updates = args.pop('goal', None) if (args.get('show_featured_stats', None)): if goal_updates and home_page_setting and home_page_setting.community and home_page_setting.community.goal: community_goal = home_page_setting.community.goal attained_number_of_actions = goal_updates.get('attained_number_of_actions', None) if attained_number_of_actions != None: community_goal.attained_number_of_actions = attained_number_of_actions target_number_of_actions = goal_updates.get('target_number_of_actions', None) if target_number_of_actions != None: community_goal.target_number_of_actions = target_number_of_actions attained_number_of_households= goal_updates.get('attained_number_of_households', None) if attained_number_of_households != None: community_goal.attained_number_of_households = attained_number_of_households target_number_of_households = goal_updates.get('target_number_of_households', None) if target_number_of_actions != None: community_goal.target_number_of_households = target_number_of_households attained_carbon_footprint_reduction = goal_updates.get('attained_carbon_footprint_reduction', None) if attained_carbon_footprint_reduction != None: community_goal.attained_carbon_footprint_reduction = attained_carbon_footprint_reduction target_carbon_footprint_reduction = goal_updates.get('target_carbon_footprint_reduction', None) if target_carbon_footprint_reduction != None: community_goal.target_carbon_footprint_reduction = target_carbon_footprint_reduction community_goal.save() #featured links if (not args.get('show_featured_links', False)): featured_links = args.pop('featured_links', None) #images current_images = home_page_setting.images.all() image_1 = args.pop('image_1', None) image_2 = args.pop('image_2', None) image_3 = args.pop('image_3', None) current_image_1 = current_images[0] current_image_2 = current_images[1] current_image_3 = current_images[2] if image_1: current_image_1 = Media(file=image_1, name=f"FeaturedImage1-{home_page_setting.community.name}", order=1) current_image_1.save() if image_2: current_image_2 = Media(file=image_2, name=f"FeaturedImage2-{home_page_setting.community.name}", order=2) current_image_2.save() if image_3: current_image_3 = Media(file=image_3, name=f"FeaturedImage3-{home_page_setting.community.name}", order=3) current_image_3.save() home_page_setting.images.set([current_image_1, current_image_2, current_image_3]) home_page_setting.save() #now, update the other fields in one swing HomePageSettings.objects.filter(id=home_page_id).update(**args) return home_page_setting, None except Exception as e: capture_message(str(e), level="error") return None, CustomMassenergizeError(e)
def create_user(self, context: Context, args) -> Tuple[dict, MassEnergizeAPIError]: try: email = args.get('email', None) community = get_community_or_die(context, args) # allow home address to be passed in location = args.pop('location', '') profile_picture = args.pop("profile_picture", None) if not email: return None, CustomMassenergizeError( "email required for sign up") user = UserProfile.objects.filter(email=email).first() if not user: new_user: UserProfile = UserProfile.objects.create( full_name=args.get('full_name'), preferred_name=args.get('preferred_name', None), email=args.get('email'), is_vendor=args.get('is_vendor', False), accepts_terms_and_conditions=args.pop( 'accepts_terms_and_conditions', False), preferences={'color': args.get('color', '')}) if profile_picture: pic = Media() pic.name = f'{new_user.full_name} profpic' pic.file = profile_picture pic.media_type = 'image' pic.save() new_user.profile_picture = pic new_user.save() else: new_user: UserProfile = user # if user was imported but profile incomplete, updates user with info submitted in form if not new_user.accepts_terms_and_conditions: new_user.accepts_terms_and_conditions = args.pop( 'accepts_terms_and_conditions', False) is_vendor = args.get('is_vendor', False) preferences = {'color': args.get('color')} community_member_exists = CommunityMember.objects.filter( user=new_user, community=community).exists() if not community_member_exists: # add them as a member to community CommunityMember.objects.create(user=new_user, community=community) # create their first household household = RealEstateUnit.objects.create( name="Home", unit_type="residential", community=community, location=location) new_user.real_estate_units.add(household) res = {"user": new_user, "community": community} return res, None except Exception as e: capture_message(str(e), level="error") return None, CustomMassenergizeError(e)
def update_community(self, context: Context, args) -> Tuple[dict, MassEnergizeAPIError]: try: community_id = args.pop("community_id", None) website = args.pop("website", None) logo = args.pop("logo", None) # The set of zipcodes, stored as Location models, are what determines a boundary for a geograpically focussed community # This will work for the large majority of cases, but there may be some where a zip code overlaps a town or state boundary # These we can deal with by having the Location include city and or state fields locations = args.pop("locations", None) favicon = args.pop("favicon", None) filter_set = Community.objects.filter(id=community_id) if not filter_set: return None, InvalidResourceError() # let's make sure we can use this subdomain subdomain = args.get('subdomain', None) if subdomain and not can_use_this_subdomain( subdomain, filter_set.first()): raise Exception( "Subdomain is already reserved. Please, choose a different subdomain" ) filter_set.update(**args) community = filter_set.first() # TODO: check that locations have changed before going through the effort of geographic = args.get("is_geographically_focused", False) if geographic: geography_type = args.get("geography_type", None) if self._are_locations_updated(geography_type, locations, community): self._update_locations(geography_type, locations, community) self._update_real_estate_units_with_community(community) if logo: cLogo = Media(file=logo, name=f"{args.get('name', '')} CommunityLogo") cLogo.save() community.logo = cLogo community.save() if favicon: cFavicon = Media( file=favicon, name=f"{args.get('name', '')} CommunityFavicon") cFavicon.save() community.favicon = cFavicon community.save() # let's make sure we reserve this subdomain if subdomain: reserve_subdomain(subdomain, community) # save custom website if specified if website: ret, err = self.add_custom_website(context, { "community_id": community.id, "website": website }) if err: raise Exception("Failed to save custom website: " + str(err)) return community, None except Exception as e: capture_exception(e) return None, CustomMassenergizeError(e)
def create_community(self, context: Context, args) -> Tuple[dict, MassEnergizeAPIError]: community = None try: subdomain = args.get('subdomain') if not can_use_this_subdomain(subdomain): raise Exception( "Subdomain is already reserved. Please, choose a different subdomain" ) logo = args.pop("logo", None) # custom website url can be included website = args.pop("website", None) # The set of locations (zipcodes, cities, counties, states), stored as Location models, are what determines a boundary for a geograpically focussed community # This will work for the large majority of cases, but there may be some where a zip code overlaps a town or state boundary # These we can deal with by having the Location include city and or state fields locations = args.pop("locations", None) favicon = args.pop("favicon", None) community = Community.objects.create(**args) community.save() geographic = args.get("is_geographically_focused", False) if geographic: geography_type = args.get("geography_type", None) self._update_locations(geography_type, locations, community) self._update_real_estate_units_with_community(community) if logo: cLogo = Media(file=logo, name=f"{args.get('name', '')} CommunityLogo") cLogo.save() community.logo = cLogo if favicon: cFav = Media(file=favicon, name=f"{args.get('name', '')} CommunityFavicon") cFav.save() community.favicon = cFav # create a goal for this community community_goal = Goal.objects.create(name=f"{community.name}-Goal") community.goal = community_goal community.save() # do this before all the cloning in case of failure reserve_subdomain(subdomain, community) # save custom website if specified if website: ret, err = self.add_custom_website(context, { "community_id": community.id, "website": website }) if err: raise Exception("Failed to save custom website: " + str(err)) # clone everything for this community homePage = HomePageSettings.objects.filter( is_template=True).first() images = homePage.images.all() # TODO: make a copy of the images instead, then in the home page, you wont have to create new files everytime if homePage: homePage.pk = None homePage.title = f"Welcome to Massenergize, {community.name}!" homePage.community = community homePage.is_template = False homePage.save() homePage.images.set(images) # now create all the pages if not _clone_page_settings(AboutUsPageSettings, f"About {community.name}", community): raise Exception("Failed to clone settings for AboutUs page") if not _clone_page_settings(ActionsPageSettings, f"Actions for {community.name}", community): raise Exception("Failed to clone settings for Actions page") if not _clone_page_settings(ContactUsPageSettings, f"Contact Us - {community.name}", community): raise Exception("Failed to clone settings for ContactUs page") if not _clone_page_settings(DonatePageSettings, f"Support {community.name}", community): raise Exception("Failed to clone settings for Donate page") if not _clone_page_settings(ImpactPageSettings, f"Our Impact - {community.name}", community): raise Exception("Failed to clone settings for Impact page") if not _clone_page_settings(TeamsPageSettings, f"Teams in this community", community): raise Exception("Failed to clone settings for Teams page") if not _clone_page_settings(VendorsPageSettings, f"Service Providers", community): raise Exception("Failed to clone settings for Vendors page") if not _clone_page_settings(EventsPageSettings, f"Events and Campaigns", community): raise Exception("Failed to clone settings for Events page") if not _clone_page_settings(TestimonialsPageSettings, f"Testimonials", community): raise Exception( "Failed to clone settings for Testimonials page") admin_group_name = f"{community.name}-{community.subdomain}-Admin-Group" comm_admin: CommunityAdminGroup = CommunityAdminGroup.objects.create( name=admin_group_name, community=community) comm_admin.save() if context.user_id: user = UserProfile.objects.filter(pk=context.user_id).first() if user: comm_admin.members.add(user) comm_admin.save() owner_email = args.get("owner_email", None) if owner_email: owner = UserProfile.objects.filter(email=owner_email).first() if owner: comm_admin.members.add(owner) comm_admin.save() # Also clone all template actions for this community # 11/1/20 BHN: Add protection against excessive copying in case of too many actions marked as template # Also don't copy the ones marked as deleted! global_actions = Action.objects.filter(is_deleted=False, is_global=True) MAX_TEMPLATE_ACTIONS = 25 num_copied = 0 actions_copied = set() for action_to_copy in global_actions: old_tags = action_to_copy.tags.all() old_vendors = action_to_copy.vendors.all() new_action: Action = action_to_copy new_action.pk = None new_action.is_published = False new_action.is_global = False old_title = new_action.title new_title = get_new_title(community, old_title) # first check that we have not copied an action with the same name if new_title in actions_copied: continue else: actions_copied.add(new_title) new_action.title = new_title new_action.save() new_action.tags.set(old_tags) new_action.vendors.set(old_vendors) new_action.community = community new_action.save() num_copied += 1 if num_copied >= MAX_TEMPLATE_ACTIONS: break return community, None except Exception as e: if community: # if we did not succeed creating the community we should delete it community.delete() capture_exception(e) return None, CustomMassenergizeError(e)
def processMedia(mainMedia: Media, file: UploadFile, container: str) -> None: # TODO: better Media filename checking for sub media objects as there can be collisions right now which cause the decomposition to fail try: filename = None requestedFileName = mainMedia.filename createdMedia = [] mainMedia.percentDone = 1 mainMedia.save() # Use ffmpeg to move metadata to the front while filename == None: filename = str(uuid4()) if path.isfile(f'media_processing/{filename}.{container}'): filename == None filename2 = None while filename2 == None: filename2 = str(uuid4()) if path.isfile(f'media_processing/{filename2}.{container}'): filename2 == None if not path.isdir('media_processing'): mkdir('media_processing') with open(f'media_processing/{filename}.{container}', 'wb+') as file_obj: file_obj.write(file.file.read()) file_obj.close() args = ['ffmpeg', '-i', f'media_processing/{filename}.{container}', '-c', 'copy', '-movflags', '+faststart', '-v', 'quiet', f'media_processing/{filename2}.{container}'] p = Popen(args) if p.wait() != 0: raise MediaProcessingError remove(f'media_processing/{filename}.{container}') mkdir(f'media_processing/{filename}') args = ['ffprobe', '-show_streams', '-show_entries', 'format=duration', '-of', 'json', '-v', 'quiet', f'media_processing/{filename2}.{container}'] p = Popen(args, stdout=PIPE) out = p.communicate()[0] if p.wait() != 0: raise SubprocessError streams = loads(out) duration = float(streams['format']['duration']) # in seconds streams = streams['streams'] audioCount = 0 subtitleCount = 0 videoPass = False for i in range(len(streams)): # Create sub media objects (audio/subtitles) if 'codec_type' in streams[i] and 'index' in streams[i]: if streams[i]['codec_type'] == 'video': # Video generation if not videoPass: videoPass = True dimensions = [] args = ['ffprobe', '-show_streams', '-show_entries', 'stream=width,height', '-of', 'json', '-v', 'quiet', f'media_processing/{filename2}.{container}'] p = Popen(args, stdout=PIPE) out = p.communicate()[0] if p.wait() != 0: raise SubprocessError width = loads(out)['streams'][streams[i]['index']] height = width['height'] width = width['width'] if FileSettings.FORCE_DIMENSION: for dimension in FileSettings.VIDEO_DIMENSIONS: if width >= dimension[0] and height >= dimension[1]: dimensions.append(dimension) if not FileSettings.CREATE_SMALLER_DIMENSIONS: break if len(dimensions) == 0: dimensions.append(FileSettings.VIDEO_DIMENSIONS[-1]) largestVideoFilename = None for j in range(len(dimensions)): if j == 0: metadata = { 'default': True } else: metadata = { } metadata['label'] = f'{dimensions[j][1]}p' media = Media(owner=mainMedia.owner, filename=f'{requestedFileName.rsplit(".", 1)[0]}_{j}_{metadata["label"]}.{FileSettings.VIDEO_EXTENSION}', folder=mainMedia.folder, private=True, processing=True, metadata=metadata) media.save() createdMedia.append(media) video_filename = str(uuid4()) args = ['ffmpeg', '-hide_banner', '-i', '-map', f'0:{str(streams[i]["index"])}', '-s', f'{dimensions[j][0]}x{dimensions[j][1]}', '-c:v', '-progress', 'pipe:1', f'media_processing/{filename}/{video_filename}.{FileSettings.VIDEO_EXTENSION}'] if largestVideoFilename: args.insert(args.index('-i') + 1, f'media_processing/{filename}/{largestVideoFilename}.{FileSettings.VIDEO_EXTENSION}') args.insert(args.index('-c:v') + 1, 'copy') else: args.insert(args.index('-i') + 1, f'media_processing/{filename2}.{container}') if 'codec_name' in streams[i] and streams[i]['codec_name'] == FileSettings.VIDEO_CODEC: args.insert(args.index('-c:v') + 1, 'copy') else: args.insert(args.index('-c:v') + 1, FileSettings.VIDEO_CODEC) p = Popen(args, stdout=PIPE, stderr=PIPE, universal_newlines=True) for stdout_line in p.stdout: stdout_line = stdout_line.split('=') if stdout_line[0] == 'out_time_us': updateTime = float(stdout_line[1]) / 10000 if updateTime > 0: media.percentDone = min(updateTime / duration, 100) media.save() if p.wait() != 0: raise SubprocessError with open(f'media_processing/{filename}/{video_filename}.{FileSettings.VIDEO_EXTENSION}', 'rb') as video_file_obj: mimetype = mimetypes.guess_type(f'{video_filename}.{FileSettings.VIDEO_EXTENSION}')[0] if mimetype == None: mimetype = f'video/{FileSettings.VIDEO_EXTENSION}' media.file.put(video_file_obj, content_type=mimetype) media.percentDone = 100 media.save() media.processing = False media.save() mainMedia.update(push__associatedMedia=media) percentDone = (j + 1) / len(dimensions) / (i + 1) / (len(streams) + 1) * 100 mainMedia.percentDone = percentDone mainMedia.save() if largestVideoFilename: remove(f'media_processing/{filename}/{video_filename}.{FileSettings.VIDEO_EXTENSION}') else: largestVideoFilename = video_filename remove(f'media_processing/{filename}/{largestVideoFilename}.{FileSettings.VIDEO_EXTENSION}') elif streams[i]['codec_type'] == 'audio': # Audio generation if audioCount > 0: metadata = {} else: metadata = { 'default': True } if 'tags' in streams[i] and 'language' in streams[i]['tags']: metadata['srclang'] = str(streams[i]['tags']['language']) metadata['label'] = str(streams[i]['tags']['language']).title() media = Media(owner=mainMedia.owner, filename=f'{requestedFileName.rsplit(".", 1)[0]}_{audioCount}.{FileSettings.AUDIO_EXTENSION}', folder=mainMedia.folder, private=True, processing=True, metadata=metadata, percentDone=1) media.save() createdMedia.append(media) audio_filename = str(uuid4()) args = ['ffmpeg', '-i', f'media_processing/{filename2}.{container}', '-map', f'0:{str(streams[i]["index"])}', '-c:a', '-progress', 'pipe:1', f'media_processing/{filename}/{audio_filename}.{FileSettings.AUDIO_EXTENSION}'] if 'codec_name' in streams[i] and streams[i]['codec_name'] == FileSettings.AUDIO_CODEC: args.insert(args.index('-c:a') + 1, 'copy') else: args.insert(args.index('-c:a') + 1, FileSettings.AUDIO_CODEC) p = Popen(args, stdout=PIPE, stderr=PIPE, universal_newlines=True) for stdout_line in p.stdout: stdout_line = stdout_line.split('=') if stdout_line[0] == 'out_time_us': updateTime = float(stdout_line[1]) / 10000 if updateTime > 0: media.percentDone = min(updateTime / duration, 100) media.save() if p.wait() != 0: raise SubprocessError with open(f'media_processing/{filename}/{audio_filename}.{FileSettings.AUDIO_EXTENSION}', 'rb') as audio_file_obj: mimetype = mimetypes.guess_type(f'{audio_filename}.{FileSettings.AUDIO_EXTENSION}')[0] if mimetype == None: mimetype = f'audio/{FileSettings.AUDIO_EXTENSION}' media.file.put(audio_file_obj, content_type=mimetype) media.percentDone = 100 media.save() media.processing = False media.save() mainMedia.update(push__associatedMedia=media) percentDone = (i + 1) / (len(streams) + 1) * 100 mainMedia.percentDone = percentDone mainMedia.save() mainMedia.update(set__percentDone=percentDone) remove(f'media_processing/{filename}/{audio_filename}.{FileSettings.AUDIO_EXTENSION}') audioCount += 1 elif streams[i]['codec_type'] == 'subtitle': # Subtitle generation if subtitleCount > 0: metadata = {} else: metadata = { 'default': True } if 'tags' in streams[i] and 'language' in streams[i]['tags']: metadata['srclang'] = str(streams[i]['tags']['language']) metadata['label'] = str(streams[i]['tags']['language']).title() media = Media(owner=mainMedia.owner, filename=f'{requestedFileName.rsplit(".", 1)[0]}_{subtitleCount}.{FileSettings.SUBTITLE_EXTENSION}', folder=mainMedia.folder, private=True, processing=True, metadata=metadata) media.save() createdMedia.append(media) subtitle_filename = str(uuid4()) args = ['ffmpeg', '-i', f'media_processing/{filename2}.{container}', '-map', f'0:{str(streams[i]["index"])}', '-progress', 'pipe:1', f'media_processing/{filename}/{subtitle_filename}.{FileSettings.SUBTITLE_EXTENSION}'] p = Popen(args, stdout=PIPE, stderr=PIPE, universal_newlines=True) for stdout_line in p.stdout: stdout_line = stdout_line.split('=') if stdout_line[0] == 'out_time_us': updateTime = float(stdout_line[1]) / 10000 if updateTime > 0: media.percentDone = min(updateTime / duration, 100) media.save() if p.wait() != 0: raise SubprocessError with open(f'media_processing/{filename}/{subtitle_filename}.{FileSettings.SUBTITLE_EXTENSION}', 'rb') as subtitle_file_obj: mimetype = mimetypes.guess_type(f'{subtitle_filename}.{FileSettings.SUBTITLE_EXTENSION}')[0] if mimetype == None: mimetype = f'text/{FileSettings.SUBTITLE_EXTENSION}' media.file.put(subtitle_file_obj, content_type=mimetype) media.percentDone = 100 media.save() media.processing = False media.save() mainMedia.update(push__associatedMedia=media) percentDone = (i + 1) / (len(streams) + 1) * 100 mainMedia.percentDone = percentDone mainMedia.save() remove(f'media_processing/{filename}/{subtitle_filename}.{FileSettings.SUBTITLE_EXTENSION}') subtitleCount += 1 # Poster generation if videoPass and len(dimensions) > 0: media = Media(owner=mainMedia.owner, filename=f'{requestedFileName.rsplit(".", 1)[0]}_poster.png', folder=mainMedia.folder, private=True) media.save() createdMedia.append(media) random.seed(int(duration * 100)) # use duration to make thumbnails generate at the same point for the same file. Collisions shouldn't matter args = ['ffmpeg', '-ss', f'{round(random.random() * duration, 2)}', '-i', f'media_processing/{filename2}.{container}', '-vframes', '1', '-s', f'{dimensions[0][0]}x{dimensions[0][1]}', '-progress', 'pipe:1', '-f', 'image2', f'media_processing/{filename}/poster.png'] random.seed() # reset seed p = Popen(args, stdout=PIPE, stderr=PIPE, universal_newlines=True) for stdout_line in p.stdout: stdout_line = stdout_line.split('=') if stdout_line[0] == 'out_time_us': updateTime = float(stdout_line[1]) / 10000 if updateTime > 0: media.percentDone = min(updateTime / duration, 100) media.save() if p.wait() != 0: raise SubprocessError with open(f'media_processing/{filename}/poster.png', 'rb') as poster_file_obj: media.file.put(poster_file_obj, content_type='image/png') media.processing = False media.save() mainMedia.update(push__associatedMedia=media) media.percentDone = 100 media.save() mainMedia.percentDone = 100 mainMedia.save() mainMedia.update(unset__percentDone=True) remove(f'media_processing/{filename}/poster.png') # Manually reload and save to get signal to fire mainMedia.reload() mainMedia.processing = False mainMedia.save() except Exception as e: # Clean up media and sub-media objects for created in createdMedia: created.delete() mainMedia.reload() for subMedia in mainMedia.associatedMedia: subMedia = subMedia.fetch() if subMedia.private: subMedia.delete() mainMedia.delete() print_exc(e) # TODO: send websocket to client to inform media player that the file failed to be created finally: # Clean up temp files for f in listdir(f'media_processing/{filename}'): remove(f'media_processing/{filename}/{f}') rmdir(f'media_processing/{filename}') remove(f'media_processing/{filename2}.{container}') if len(listdir('media_processing')) == 0: rmdir('media_processing')