How to add custom fields to images for image source text and URL

There are a few different ways you could achieve this.

The most obvious one would be to use two existing image fields- are you already using Title, caption, alt text and description? If not, these are available, and you could simply add some code to your template to use (say) the caption as the link and the description as the source.

If this seems messy or if you are already using the image fields, you could also use a plugin to make categories available to media items. I recently used this one with some success:
https://wordpress.org/plugins/wp-media-library-categories/

With the addition of a couple of lines of code, this lets you store image categories separately from ordinary post categories:

/**
* separate media categories from post categories
* use a custom category called 'category_media' for the categories in the media library
*/
add_filter( 'wpmediacategory_taxonomy', function(){ return 'category_media'; } );

This way you could store the sources as media categories, putting the URL in the category description field. The advantage of this approach would be that you’d be able to globally change source URLs without changing individual image data.

Hope this helps.

EDIT

The functions.php hook you found is working just fine. To output the values it’s storing, you just need to pass the ID of the image to the get_post_meta function along with the keys for the values stored. In the snippet below I’m assuming the image is the thumbnail of the current post.

$source_url = get_post_meta(get_post_thumbnail_id(get_the_ID()),'_wp_attachment_source_url', true);
$source_name = get_post_meta(get_post_thumbnail_id(get_the_ID()),'_wp_attachment_source_name', true); ?>
<a href="https://wordpress.stackexchange.com/questions/240666/<?php echo $source_url; ?>"><?php echo $source_name; ?></a>

Let me know if you have any trouble.

EDIT 2

In response to the original poster’s request, here is a filter to incorporate the newly stored source information into images inserted via the WordPress editor. This function and filter need to be put in the functions.php file as well. This checks that both a link and source name exist. If they do, it outputs a line break and a link to the source, opening in a new window.

function image_add_caption_with_source( $html, $id, $caption, $title, $align, $url, $size, $alt="" ) {

  /**
   * Filters the caption text and adds source info.
   *
   * Note: If the caption text is empty, the caption shortcode will not be appended
   * to the image HTML when inserted into the editor.
   *
   * Passing an empty value also prevents the {@see 'image_add_caption_shortcode'}
   * Filters from being evaluated at the end of image_add_caption().
   *
   * @since 4.1.0
   *
   * @param string $caption The original caption text.
   * @param int    $id      The attachment ID.
   */
  $caption = apply_filters( 'image_add_caption_text', $caption, $id );
  $source_url = get_post_meta($id,'_wp_attachment_source_url', true);
  $source_name = get_post_meta($id,'_wp_attachment_source_name', true);
  /**
   * Filters whether to disable captions.
   *
   * Prevents image captions from being appended to image HTML when inserted into the editor.
   *
   * @since 2.6.0
   *
   * @param bool $bool Whether to disable appending captions. Returning true to the filter
   *                   will disable captions. Default empty string.
   */
  if ( empty($caption) || apply_filters( 'disable_captions', '' ) )
    return $html;

  $id = ( 0 < (int) $id ) ? 'attachment_' . $id : '';

  if ( ! preg_match( '/width=["\']([0-9]+)/', $html, $matches ) )
    return $html;

  $width = $matches[1];

  $caption = str_replace( array("\r\n", "\r"), "\n", $caption);
  $caption = preg_replace_callback( '/<[a-zA-Z0-9]+(?: [^<>]+>)*/', '_cleanup_image_add_caption', $caption );

  // Convert any remaining line breaks to <br>.
  $caption = preg_replace( '/[ \n\t]*\n[ \t]*/', '<br />', $caption );

  $html = preg_replace( '/(class=["\'][^\'"]*)align(none|left|right|center)\s?/', '$1', $html );
  if($source_url && $source_name):
   $source="<br /><a target="_blank" href="".$source_url.'">'.$source_name.'</a>';
  else:
   $source = "";
  endif;
  if ( empty($align) )
    $align = 'none';

  $shcode="' . $html . ' ' . $caption . $source . '';

  /**
   * Filters the image HTML markup including the caption shortcode.
   *
   * @since 2.6.0
   *
   * @param string $shcode The image HTML markup with caption shortcode.
   * @param string $html   The image HTML markup.
   */

  return apply_filters( 'image_add_caption_shortcode', $shcode, $html );
}


// remove the existing filter
remove_filter( 'image_send_to_editor', 'image_add_caption', 20, 8 );
// add the new filter
add_filter( 'image_send_to_editor', 'image_add_caption_with_source', 20, 8 );