diff --git a/api/go.mod b/api/go.mod
index fb8a77b..7d5ebbe 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -5,7 +5,7 @@ go 1.16
 require (
 	github.com/99designs/gqlgen v0.13.0
 	github.com/Kagami/go-face v0.0.0-20200825065730-3dd2d74dccfb
-	github.com/barasher/go-exiftool v1.3.2
+	github.com/barasher/go-exiftool v1.3.2 // indirect
 	github.com/disintegration/imaging v1.6.2
 	github.com/go-sql-driver/mysql v1.5.0
 	github.com/gorilla/handlers v1.5.1
@@ -13,6 +13,7 @@ require (
 	github.com/gorilla/websocket v1.4.2
 	github.com/h2non/filetype v1.1.1
 	github.com/joho/godotenv v1.3.0
+	github.com/kjeldgaard/go-exiftool v1.3.3
 	github.com/pkg/errors v0.9.1
 	github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f
 	github.com/vektah/gqlparser/v2 v2.1.0
diff --git a/api/go.sum b/api/go.sum
index a103eea..f17d24d 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -117,6 +117,8 @@ github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
 github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
 github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kjeldgaard/go-exiftool v1.3.3 h1:tDlkcHtTuduu7UgopqGS/ooh1tmEgtIhlBjmTXzjxbg=
+github.com/kjeldgaard/go-exiftool v1.3.3/go.mod h1:Nmi1VkXBbr48Kq5ZJjo29RD+a3PdWVGlL+8bSssxFbU=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
diff --git a/api/graphql/generated.go b/api/graphql/generated.go
index 4a771fa..3f937ad 100644
--- a/api/graphql/generated.go
+++ b/api/graphql/generated.go
@@ -1890,8 +1890,8 @@ type MediaEXIF {
   focalLength: Float
   "A formatted description of the flash settings, when the image was taken"
   flash: String
-  "A formated description of the mode for adjusting the exposure of the image"
-  exposureProgram: String
+  "An index describing the mode for adjusting the exposure of the image"
+  exposureProgram: Int
 }
 
 type VideoMetadata {
@@ -4616,9 +4616,9 @@ func (ec *executionContext) _MediaEXIF_exposureProgram(ctx context.Context, fiel
 	if resTmp == nil {
 		return graphql.Null
 	}
-	res := resTmp.(*string)
+	res := resTmp.(*int64)
 	fc.Result = res
-	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
+	return ec.marshalOInt2ᚖint64(ctx, field.Selections, res)
 }
 
 func (ec *executionContext) _MediaURL_url(ctx context.Context, field graphql.CollectedField, obj *models.MediaURL) (ret graphql.Marshaler) {
diff --git a/api/graphql/models/media_exif.go b/api/graphql/models/media_exif.go
index f86d552..3b15fe5 100644
--- a/api/graphql/models/media_exif.go
+++ b/api/graphql/models/media_exif.go
@@ -16,7 +16,7 @@ type MediaEXIF struct {
 	FocalLength     *float64
 	Flash           *string
 	Orientation     *int64
-	ExposureProgram *string
+	ExposureProgram *int64
 	GPSLatitude     *float64
 	GPSLongitude    *float64
 }
diff --git a/api/graphql/schema.graphql b/api/graphql/schema.graphql
index de7eb2f..15042ad 100644
--- a/api/graphql/schema.graphql
+++ b/api/graphql/schema.graphql
@@ -313,8 +313,8 @@ type MediaEXIF {
   focalLength: Float
   "A formatted description of the flash settings, when the image was taken"
   flash: String
-  "A formated description of the mode for adjusting the exposure of the image"
-  exposureProgram: String
+  "An index describing the mode for adjusting the exposure of the image"
+  exposureProgram: Int
 }
 
 type VideoMetadata {
diff --git a/api/scanner/exif/exif.go b/api/scanner/exif/exif.go
index 903f6e4..be2a026 100644
--- a/api/scanner/exif/exif.go
+++ b/api/scanner/exif/exif.go
@@ -6,6 +6,7 @@ import (
 	"github.com/pkg/errors"
 	"gorm.io/gorm"
 
+	"github.com/kjeldgaard/go-exiftool"
 	"github.com/photoview/photoview/api/graphql/models"
 )
 
@@ -31,7 +32,16 @@ func SaveEXIF(tx *gorm.DB, media *models.Media) (*models.MediaEXIF, error) {
 		}
 	}
 
-	var parser exifParser = &externalExifParser{}
+	// Decide between internal or external Exif parser
+	et, err := exiftool.NewExiftool()
+	et.Close()
+	var parser exifParser
+	if err != nil {
+		parser = &internalExifParser{}
+	} else {
+		parser = &externalExifParser{}
+	}
+
 
 	exif, err := parser.ParseExif(media)
 	if err != nil {
diff --git a/api/scanner/exif/exif_parser_external.go b/api/scanner/exif/exif_parser_external.go
index 226924f..3111171 100644
--- a/api/scanner/exif/exif_parser_external.go
+++ b/api/scanner/exif/exif_parser_external.go
@@ -6,7 +6,7 @@ import (
 	"strconv"
 	"time"
 
-	"github.com/barasher/go-exiftool"
+	"github.com/kjeldgaard/go-exiftool"
 	"github.com/photoview/photoview/api/graphql/models"
 )
 
@@ -14,7 +14,10 @@ type externalExifParser struct{}
 
 func (p *externalExifParser) ParseExif(media *models.Media) (returnExif *models.MediaEXIF, returnErr error) {
 	// Init ExifTool
-	et, err := exiftool.NewExiftool()
+
+	et2 := exiftool.Charset("-n")
+
+	et, err := 																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																			.NewExiftool(et2)
 	if err != nil {
 		log.Printf("Error initializing ExifTool: %s\n", err)
 		return nil, err
@@ -60,7 +63,6 @@ func (p *externalExifParser) ParseExif(media *models.Media) (returnExif *models.
 			if err == nil {
 				newExif.DateShot = &dateTime
 			}
-
 		}
 
 		// Get exposure time
@@ -88,7 +90,7 @@ func (p *externalExifParser) ParseExif(media *models.Media) (returnExif *models.
 		focalLen, err := fileInfo.GetString("FocalLength")
 		if err == nil {
 			log.Printf("Focal length: %s", focalLen)
-			reg, err := regexp.Compile("[0-9.]+")
+			reg, _ := regexp.Compile("[0-9.]+")
 			focalLenStr := reg.FindString(focalLen)
 			focalLenFloat, err := strconv.ParseFloat(focalLenStr, 64)
 			if err == nil {
@@ -104,44 +106,19 @@ func (p *externalExifParser) ParseExif(media *models.Media) (returnExif *models.
 		}
 
 		// Get orientation
-		orientation, err := fileInfo.GetString("Orientation")
-		log.Printf("Orientation: %s", orientation)
+		orientation, err := fileInfo.GetInt("Orientation")
 		if err == nil {
-			if orientation == "Horizontal (normal)" {
-				var orientationInt int64 = 0
-				newExif.Orientation = &orientationInt
-			} else {
-				reg, err := regexp.Compile("CCW")
-				if err == nil {
-					if reg.MatchString(orientation) {
-						//Counter clockwise
-						reg, err := regexp.Compile("[0-9.]+")
-						orientationStr := reg.FindString(orientation)
-						orientationInt, err := strconv.ParseInt(orientationStr, 10, 64)
-						if err == nil {
-							log.Printf("Orientation: %d", orientationInt)
-							newExif.Orientation = &orientationInt
-						}
-					} else {
-						// Clockwise
-						reg, err := regexp.Compile("[0-9.]+")
-						orientationStr := reg.FindString(orientation)
-						orientationInt, err := strconv.ParseInt(orientationStr, 10, 64)
-						if err == nil {
-							orientationInt = orientationInt * -1
-							log.Printf("Orientation: %d", orientationInt)
-							newExif.Orientation = &orientationInt
-						}
-					}
-				}
-			}
+			log.Printf("Orientation: %d", orientation)
+			newExif.Orientation = &orientation
 		}
 
 		// Get exposure program
-		expProgram, err := fileInfo.GetString("ExposureProgram")
+		expProgram, err := fileInfo.GetStrings("ExposureProgram")
 		if err == nil {
-			log.Printf("Exposure Program: %s", expProgram)
-			newExif.ExposureProgram = &expProgram
+			for _, value := range expProgram {
+				log.Printf("%s", value)
+			}
+			//log.Printf("Exposure Program: %d", expProgram)
 		}
 
 		// GPS coordinates - longitude
diff --git a/api/scanner/exif/exif_parser_internal.go b/api/scanner/exif/exif_parser_internal.go
new file mode 100644
index 0000000..654ec5f
--- /dev/null
+++ b/api/scanner/exif/exif_parser_internal.go
@@ -0,0 +1,191 @@
+package exif
+
+import (
+	"fmt"
+	"log"
+	"math/big"
+	"os"
+
+	"github.com/photoview/photoview/api/graphql/models"
+	"github.com/pkg/errors"
+	"github.com/xor-gate/goexif2/exif"
+	"github.com/xor-gate/goexif2/mknote"
+)
+
+// internalExifParser is an exif parser that parses the media without the use of external tools
+type internalExifParser struct{}
+
+func (p *internalExifParser) ParseExif(media *models.Media) (returnExif *models.MediaEXIF, returnErr error) {
+	photoFile, err := os.Open(media.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	exif.RegisterParsers(mknote.All...)
+
+	// Recover if exif.Decode panics
+	defer func() {
+		if err := recover(); err != nil {
+			log.Printf("Recovered from panic: Exif decoding: %s\n", err)
+			returnErr = errors.New(fmt.Sprintf("Exif decoding panicked: %s\n", err))
+		}
+	}()
+
+	exifTags, err := exif.Decode(photoFile)
+	if err != nil {
+		return nil, errors.Wrap(err, "Could not decode EXIF")
+	}
+
+	newExif := models.MediaEXIF{}
+
+	model, err := p.readStringTag(exifTags, exif.Model, media)
+	if err == nil {
+		newExif.Camera = model
+	}
+
+	maker, err := p.readStringTag(exifTags, exif.Make, media)
+	if err == nil {
+		newExif.Maker = maker
+	}
+
+	lens, err := p.readStringTag(exifTags, exif.LensModel, media)
+	if err == nil {
+		newExif.Lens = lens
+	}
+
+	date, err := exifTags.DateTime()
+	if err == nil {
+		newExif.DateShot = &date
+	}
+
+	exposure, err := p.readRationalTag(exifTags, exif.ExposureTime, media)
+	if err == nil {
+		exposureStr := exposure.RatString()
+		newExif.Exposure = &exposureStr
+	}
+
+	apertureRat, err := p.readRationalTag(exifTags, exif.FNumber, media)
+	if err == nil {
+		aperture, _ := apertureRat.Float64()
+		newExif.Aperture = &aperture
+	}
+
+	isoTag, err := exifTags.Get(exif.ISOSpeedRatings)
+	if err != nil {
+		log.Printf("WARN: Could not read ISOSpeedRatings from EXIF: %s\n", media.Title)
+	} else {
+		iso, err := isoTag.Int(0)
+		if err != nil {
+			log.Printf("WARN: Could not parse EXIF ISOSpeedRatings as integer: %s\n", media.Title)
+		} else {
+			iso64 := int64(iso)
+			newExif.Iso = &iso64
+		}
+	}
+
+	focalLengthTag, err := exifTags.Get(exif.FocalLength)
+	if err == nil {
+		focalLengthRat, err := focalLengthTag.Rat(0)
+		if err == nil {
+			focalLength, _ := focalLengthRat.Float64()
+			newExif.FocalLength = &focalLength
+
+		} else {
+			// For some photos, the focal length cannot be read as a rational value,
+			// but is instead the second value read as an integer
+
+			if err == nil {
+				focalLength, err := focalLengthTag.Int(1)
+				if err != nil {
+					log.Printf("WARN: Could not parse EXIF FocalLength as rational or integer: %s\n%s\n", media.Title, err)
+				} else {
+					focalLenFloat := float64(focalLength)
+					newExif.FocalLength = &focalLenFloat
+				}
+			}
+		}
+	}
+
+	flash, err := exifTags.Flash()
+	if err == nil {
+		newExif.Flash = &flash
+	}
+
+	orientation, err := p.readIntegerTag(exifTags, exif.Orientation, media)
+	if err == nil {
+		orientation64 := int64(*orientation)
+		newExif.Orientation = &orientation64
+	}
+
+	exposureProgram, err := p.readIntegerTag(exifTags, exif.ExposureProgram, media)
+	if err == nil {
+		exposureProgram64 := int64(*exposureProgram)
+		newExif.ExposureProgram = &exposureProgram64
+	}
+
+	lat, long, err := exifTags.LatLong()
+	if err == nil {
+		newExif.GPSLatitude = &lat
+		newExif.GPSLongitude = &long
+	}
+
+	returnExif = &newExif
+	return
+}
+
+func (p *internalExifParser) readStringTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*string, error) {
+	tag, err := tags.Get(name)
+	if err != nil {
+		return nil, errors.Wrapf(err, "could not read %s from EXIF: %s", name, media.Title)
+	}
+
+	if tag != nil {
+		value, err := tag.StringVal()
+		if err != nil {
+			return nil, errors.Wrapf(err, "could not parse %s from EXIF as string: %s", name, media.Title)
+		}
+
+		return &value, nil
+	}
+
+	log.Printf("WARN: EXIF tag %s returned null: %s\n", name, media.Title)
+	return nil, errors.New("exif tag returned null")
+}
+
+func (p *internalExifParser) readRationalTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*big.Rat, error) {
+	tag, err := tags.Get(name)
+	if err != nil {
+		return nil, errors.Wrapf(err, "could not read %s from EXIF: %s", name, media.Title)
+	}
+
+	if tag != nil {
+		value, err := tag.Rat(0)
+		if err != nil {
+			return nil, errors.Wrapf(err, "could not parse %s from EXIF as rational: %s", name, media.Title)
+		}
+
+		return value, nil
+	}
+
+	log.Printf("WARN: EXIF tag %s returned null: %s\n", name, media.Title)
+	return nil, errors.New("exif tag returned null")
+}
+
+func (p *internalExifParser) readIntegerTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*int, error) {
+	tag, err := tags.Get(name)
+	if err != nil {
+		return nil, errors.Wrapf(err, "could not read %s from EXIF: %s", name, media.Title)
+	}
+
+	if tag != nil {
+		value, err := tag.Int(0)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Could not parse %s from EXIF as integer: %s", name, media.Title)
+		}
+
+		return &value, nil
+	}
+
+	log.Printf("WARN: EXIF tag %s returned null: %s\n", name, media.Title)
+	return nil, errors.New("exif tag returned null")
+}
diff --git a/ui/src/components/sidebar/MediaSidebar.js b/ui/src/components/sidebar/MediaSidebar.js
index 9548eaa..321569b 100644
--- a/ui/src/components/sidebar/MediaSidebar.js
+++ b/ui/src/components/sidebar/MediaSidebar.js
@@ -134,6 +134,20 @@ const exifNameLookup = {
   flash: 'Flash',
 }
 
+// From https://exiftool.org/TagNames/EXIF.html
+const exposurePrograms = {
+  0: 'Not defined',
+  1: 'Manual',
+  2: 'Normal program',
+  3: 'Aperture priority',
+  4: 'Shutter priority',
+  5: 'Creative program',
+  6: 'Action program',
+  7: 'Portrait mode',
+  8: 'Landscape mode ',
+  9: 'Bulb',
+}
+
 const SidebarContent = ({ media, hidePreview }) => {
   let exifItems = []
 
@@ -152,7 +166,7 @@ const SidebarContent = ({ media, hidePreview }) => {
 
     exif.dateShot = new Date(exif.dateShot).toLocaleString()
     if (exif.exposureProgram) {
-      exif.exposureProgram = `${exif.exposureProgram}`
+      exif.exposureProgram = exposurePrograms[exif.exposureProgram]
     }
 
     if (exif.aperture) {
