GithubHelp home page GithubHelp logo

marcel's Introduction

Marcel

Marcel chooses the most appropriate content type for a file by inspecting its contents, the declared MIME type (perhaps passed as a Content-Type header), and the file extension.

Marcel checks, in order:

  1. The "magic bytes" sniffed from the file contents.
  2. The declared type, typically provided in a Content-Type header on an uploaded file, unless it's the application/octet-stream default.
  3. The filename extension.
  4. Safe fallback to the indeterminate application/octet-stream default.

At each step, the most specific MIME subtype is selected. This allows the declared type and file extension to refine the parent type sniffed from the file contents, but not conflict with it. For example, if "file.csv" has declared type text/plain, text/csv is returned since it's a more specific subtype of text/plain. Similarly, Adobe Illustrator files are PDFs internally, so magic byte sniffing indicates application/pdf which is refined to application/illustrator by the ai file extension. But a PDF named "image.png" will still be detected as application/pdf since image/png is not a subtype.

Usage

# Magic bytes sniffing alone
Marcel::MimeType.for Pathname.new("example.gif")
#  => "image/gif"

File.open "example.gif" do |file|
  Marcel::MimeType.for file
end
#  => "image/gif"

# Magic bytes with filename fallback
Marcel::MimeType.for Pathname.new("unrecognisable-data"), name: "example.pdf"
#  => "application/pdf"

# File extension alone
Marcel::MimeType.for extension: ".pdf"
#  => "application/pdf"

# Magic bytes, declared type, and filename together
Marcel::MimeType.for Pathname.new("unrecognisable-data"), name: "example", declared_type: "image/png"
#  => "image/png"

# Safe fallback to application/octet-stream
Marcel::MimeType.for StringIO.new(File.read "unrecognisable-data")
#  => "application/octet-stream"

Extending

Custom file types may be added with Marcel::MimeType.extend:

Marcel::MimeType.extend "text/custom", extensions: %w( customtxt )
Marcel::MimeType.for name: "file.customtxt"
#  => "text/custom"

Motivation

Marcel was extracted from Basecamp's file detection heuristics. The aim is provide sensible, safe, "do what I expect" results for typical file handling. Test fixtures have been added for many common file types, including those typically encountered by Basecamp.

Contributing

Marcel generates MIME lookup tables with bundle exec rake update. MIME types are seeded from data found in data/*.xml. Custom MIMEs may be added to data/custom.xml, while overrides to the standard MIME database may be added to lib/marcel/mime_type/definitions.rb.

Marcel follows the same contributing guidelines as rails/rails.

Testing

The main test fixture files are split into two folders, those that can be recognised by magic bytes, and those that can only be recognised by name. Even though strictly unnecessary, the fixtures in both folders should all be valid files of the type they represent.

License

Marcel itself is released under the terms of the MIT License. See the MIT-LICENSE file for details.

Portions of Marcel are adapted from the mimemagic gem, released under the terms of the MIT License.

Marcel's magic signature data is adapted from Apache Tika, released under the terms of the Apache License. See the APACHE-LICENSE file for details.

marcel's People

Contributors

afcapel avatar andy-h-nguyen avatar benkoshy avatar brian-kephart avatar byroot avatar dependabot[bot] avatar eileencodes avatar elebow avatar excid3 avatar fursich avatar georgeclaghorn avatar gmcgibbon avatar hahmed avatar huacnlee avatar janko avatar jeremy avatar junaruga avatar kaspth avatar kiskoza avatar olleolleolle avatar pixeltrix avatar qcam avatar rafaelfranca avatar sbocinec avatar tomafro avatar ursm avatar valeronm avatar vipulnsward avatar wonda-tea-coffee avatar y-yagi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

marcel's Issues

ZIP archive misidentified as video/x-ms-wmv

I have a valid ZIP archive that happens to include the bytes wmv2 in the first four kilobytes. Active Storage misidentifies the file as Windows Media Video. When scanning over such a broad range of bytes, WMV magic needs a lower priority than other matches.

Marcel::MimeType.for Pathname.new('A-453.zip'), name: 'A-453.zip', declared_type: 'application/zip'
# => "video/x-ms-wmv"

File.read('A-453.zip')[0...4]
# => "PK\u0003\u0004"

File.read('A-453.zip').index('wmv2')
# => 585

`unzip -t A-453.zip`.chomp.split("\n").last
# => "No errors detected in compressed data of A-453.zip."

single test failure when using rack 3

Hello,

In case it's useful, I'm building with rack 3, and there's a single test failure:

starting phase `check'
/tmp/guix-build-ruby-marcel-1.0.2.drv-0/source/test/test_helper.rb:36: warning: assigned but unused variable - extension
/tmp/guix-build-ruby-marcel-1.0.2.drv-0/source/test/mime_type_test.rb:22: warning: assigned but unused variable - content_type
Run options: --seed 37592

# Running:

....................................................................................................................................................................................................................................................................................................................................................................E....

Finished in 0.094140s, 3834.7065 runs/s, 3866.5738 assertions/s.

  1) Error:
Marcel::MimeTypeTest#test_gets_content_type_from_sources_that_conform_to_Rack::Lint::InputWrapper:
NameError: uninitialized constant Rack::Lint::InputWrapper
    /tmp/guix-build-ruby-marcel-1.0.2.drv-0/source/test/mime_type_test.rb:43:in `block in <class:MimeTypeTest>'

361 runs, 364 assertions, 0 failures, 1 errors, 0 skips
rake aborted!
Command failed with status (1)

Tasks: TOP => default => test
(See full trace by running task with --trace)
error: in phase 'check': uncaught exception

So it seems there's not much missing!

Thank you!

Potential minor security risk (upload/spread arbitrary data) with ActiveStorage

Situation
ActiveStorage uses Marcel#for to determine the mime type of an uploaded file.
Marcel#for tries to determine the mime type from the given io object or path (in the case of ActiveStorage an io object is always supplied). If that fails (because Marcel cannot determine the type using its magic headers), it falls back to using the file's extension or content type - in case of ActiveStorage both of these are user supplied.
So as long as an uploaded file doesn't match any of the magic headers, the user is free to supply any mime type he chooses, posing a potential way to upload malicious files and disguising them as different mime types (also concerns mime type validations)

This is the method in question:

def for(pathname_or_io = nil, name: nil, extension: nil, declared_type: nil)
type_from_data = for_data(pathname_or_io)
fallback_type = for_declared_type(declared_type) || for_name(name) || for_extension(extension) || BINARY
if type_from_data
most_specific_type type_from_data, fallback_type
else
fallback_type
end
end

The else-block always falls back to the mime times derived from user supplied information about the file, even if an io object or path was supplied.

What do you think about this? Am I missing something?

Fix
From my current point of view, I'd suggest returning the BINARY (constant) type if no mime type could be extracted from a supplied io object or path.

        ...
        else
          # when an io object or pathname were supplied but type could not be determined from it,
          # don't use any fallback type extracted from other supplied information.
          pathname_or_io ? BINARY : fallback_type
        end

Errno::EACCES: Permission denied @ rb_sysopen

Been trying to install this gem as part of a rails app and I keep getting the following error...

Errno::EACCES: Permission denied @ rb_sysopen -
C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/marcel-0.3.2/test/fixtures/magic/video/quicktime/quicktime.mov
An error occurred while installing marcel (0.3.2), and Bundler cannot continue.
Make sure that `gem install marcel -v '0.3.2' --source 'https://rubygems.org/'`

I have full access to the folder so I'm mighty confused.

Apache tika mime types used in marcel 1.0.0 missing common font types (font/woff, font/woff2,..

The new Apache Tika MIME types introduced in v1.0.0 2e58d19 are missing common font types, e.g. font/woff, font/woff2, etc. Apache Tika seems to support only a limited set of TrueType fonts https://tika.apache.org/1.20/formats.html#Font_formats.

The previous used MIME types DB from freedesktop project used in mimemagic gem used to have these types: https://gitlab.freedesktop.org/xdg/shared-mime-info/-/blob/master/data/freedesktop.org.xml.in#L2085

The lack of these types is causing issues with validations (e.g. we are using shrine with marcel to validate metadata but were forced to upgrade to v1.0.0 due to the recent issues with mimemagic) - we allow users of our app to use/upload these modern font formats that are heavily used on the web.

I'm not yet sure about the best solution for this issue- ideal would be to add changes in the upstream (Apache Tika) so marcel can pull those later, though, at a quick look the project seems to be understandably limitting the supported mime types. So at least wanted to report and potentially discuss the issue as I think there will be others coming affected by missing the same or other types

Unable to Preview audio/vnd.wave in Chrome

I am using active storage(5.2.5)

On .wav file uploading it shows content type as 'audio/wav' in direct upload. After active_storage_attachments create, mimemagic gem updates the content type as audio/x-wav. And the same file previewing in chrome.

But after changing to marcel it saves the content type as 'audio/vnd.wave'. While debugging, active_storage / blob / identifiable updates the content types. And 'audio/vnd.wave' not previewed in chrome, but safari allows previewing.

So, tried to comment the lines to update on active_storage/blob/identifiable. It updates the content type as audio/x-wav and allows it to preview in Chrome.

Tried to add configuration in application.rb

config.active_storage.content_types_allowed_inline += %w[audio/wave audio/vnd.wave audio/wav audio/x-wav audio/x-pn-wav]

But unable to preview the audio/vnd.wave extension files in chrome.

Do I need to update any configurations to preview the .wav extension file in Chrome?

Hope understand my issue. Please help me to identify the issue.

compatibility is broken with certain types of office documents

Hi - first of all A HUGE THANKS for your recent hard work!
I can't thank you enough for your dedication that saved entire rails community (and of course, myself included) from license problems.

By the way, we have just upgraded our Rails app with marcel 1.0.0, and found that some tests started failing due to mis-detection of xlsx formats.

summary

  • certain xlsx files are mis-detected as 'application/zip':
> Marcel::MimeType.for File.open('spec/fixtures/document/excel/sample.xlsx')
=> "application/zip"
  • after some investigation, it turned out that the files are all generated by google sheet (by exporting original google sheets as xlsx)

  • the files seem to have valid xlsx format, because it can be viewed correctly by ms excel.

reproduction case

To reproduce above case, let me submit a PR separately with mis-detected files attached as fixture.

quick analysis and a few thoughts..

It looks like the parts that consists of office open document doesn't have a fixed order.
So in some cases, [Contents_Type].xml doesn't come immediately after the first 30 bytes.

Currently marcel is very specific about where to expect[Contents_Type].xml, whereas mimemagic used to look up wider ranges to find the part.

Would that make sense to relax the area for magic match, so that it can detect much varied xlsx's?

Support older XLIFF mime type (application/x-xliff+xml)

The previous Marcel version that used Mimemagic had a different behavior then version 1.0 that uses Tika.

It seems that the mime type for XLIFF changed after spec version 2.0, from application/x-xliff+xml (private), to application/xliff+xml (standard tree).

From the spec:

XLIFF 2.0 was provisionally registered as application/xliff+xml in the IANA Provisional Standard Media Type Registry.

http://docs.oasis-open.org/xliff/xliff-core/v2.1/os/xliff-core-v2.1-os.html

Related:
#48

Thanks

Documentation is misleading

The README reads:

If this doesn't work, it uses the type gleaned from the filename, extension, and finally the declared type.

But the code is

fallback_type = for_declared_type(declared_type) || for_name(name) || for_extension(extension) || BINARY

so clearly the declared type is not checked last.

Which is unfortunate for my use case, so actually maybe the documentation is right wrt the intentions, but the code is wrong?

ogg vorbis files recognized as audio/vorbis

Trying to detect the MIME type of an ogg vorbis file returns audio/vorbis, which according to https://wiki.xiph.org/MIME_Types_and_File_Extensions is for vorbis streams without containers, instead of audio/ogg.

irb(main):003:0> Marcel::Magic.by_magic(File.open('spec/fixtures/files/boop.ogg'))
=> #<Marcel::Magic:0x0000560870431ec0 @mediatype="audio", @subtype="vorbis", @type="audio/vorbis">
irb(main):002:0> Marcel::MimeType.for(Pathname.new('spec/fixtures/files/boop.ogg'))
=> "audio/vorbis"
irb(main):004:0> Marcel::MimeType.for(File.open('spec/fixtures/files/boop.ogg'), name: 'boop.ogg')
=> "audio/vorbis"

This is a bit surprising and might throw some tools off, as audio/vorbis is generally not expected nor associated with any file format of extension (since it's for streams themselves and not files/containers).

Passing declared_type works but it might not be provider, or provided by an untrusted source:

irb(main):006:0> Marcel::MimeType.for(File.open('spec/fixtures/files/boop.ogg'), name: 'boop.ogg', declared_type: 'audio/ogg')
=> "audio/ogg"

EDIT: this seems to come from https://github.com/rails/marcel/blob/main/data/tika.xml#L5135-L5146 and introduced in Apache Tika by apache/tika@41c6749 but I do think it's wrong, as audio/vorbis seem to be defined by RFC5215 and specific to RTP streams.

[Help]: Trying to generate tables with the strings as is

Hi there, I'm trying to understand how the generate_tables.rb works, after giving it some debugging I did not understand how can generate the file without modifying the strings with escaped characters.

For example: I need that this record from tika: <match value="MM\x00\x2a" type="string" offset="0"/> generates the same string on the @magic table:

like ...b["MM\x00\x2a"]]... but what I get is something like: b["MM\000*"]]

Of course this is not a bug on the marcel side, but I need to port this to another language and I need to keep the strings as is.

It seems that there are some gsub that modifies that, but even commenting those the strings are not properly generated,

could anyone give me some hints on what to do here?

thanks

Audio/flac?

Previous marcel analyzed a FLAC file and returned mime-type: audio/flac.

New 1.0 with it's own data decides audio/x-flac instead. Perhaps because of this line:

'flac' => 'audio/x-flac',

It is not clear to me which is "right", the situation with "official" mime/content-types has gotten a bit out of hand.

But a differnet part of marcel uses audio/flac:

"audio/flac", # .flac

Not sure if these data tables are generated from some upstream source, so this is an issue (if it is one?) for some such source? If the marcel/tables.rb is generated from an upstream source, a comment at top of file saying so might be helpful.

Is there any way for me to configure marcel to tell it I'd rather it return audio/flac?

Various office files wrong mimetype

According to this the mimetype of .oft (outlook email emplate) should be application/vnd.ms-outlook, but marcel currently reports application/x-tika-msoffice.

Some other issues include:

  • .accdb, .mdb and .mde are reported as font/ttf
  • .mht is reported as multipart/related

All of these files worked on marcel 0.3.3

image/svg+xml returned for an html document with svg in it

Summary

A properly declared HTML document that contains some svg is being detected as image/svg+xml. No hints given to Marcel seem to affect this result.

Reproduction:

Marcel::MimeType.for(
  StringIO.new('<!doctype html><html><body><svg></svg></body></html>'), 
  name: 'index.html', 
  declared_type: 'text/html',
)
=> 'image/svg+xml

What I expect to happen:

Returns text/html

What actually happens:

Returns image/svg+xml.

From what I could gather, the image/svg+xml matcher is position 22 in the Magic types and text/html is position 361. SVG's magic range is ... large, so it pretty much always wins and believes the magic bytes to prove the type.

What's the ideal solution here?

Incorrect HTML magic identification when preceeded by a comment

If the HTML has a comment before the opening tag, it is incorrectly identified as XML.

Steps to reproduce

io = StringIO.new(<<~HTML)
  <!--/* Throwaway comment but it has to be over 64 characters to fail AND have a uppercase HTML tag */-->
  <HTML>
    <head>
    </head>
    <body>
      <h1>Magic!</h1>
    </body
  </HTML>
HTML
Marcel::MimeType.for(io)
# => "application/xml"
io = StringIO.new(<<~HTML)
  <!--/* Throwaway comment but it has to be over 128 characters to fail AND have a lowercase HTML tag, we can pad this one out a bit to get it longer */-->
  <html>
    <head>
    </head>
    <body>
      <h1>Magic!</h1>
    </body
  </html>
HTML
Marcel::MimeType.for(io)
# => "application/xml"

Updating the magic definitions is a temporary workaround but obviously the comment could be any length, the broader lookup here https://github.com/rails/marcel/blob/main/lib/marcel/tables.rb#L2761 falls below the comment xml matching magic in https://github.com/rails/marcel/blob/main/lib/marcel/tables.rb#L2747.

Temporary workaround

Marcel::MimeType.extend "text/html", magic: [[0..256, "<HTML"]]
Marcel::MimeType.extend "text/html", magic: [[0..256, "<html"]]

APACHE-LICENSE file not included in gem

Although the Github project was updated with the Apache license, the distributed gem does not include the license file. I'm not sure this was intended given it's listed in the gemspec?

gem name

Hello,

Just a quick question :

How did you pick the gem name ?
Does it have something to do with Marcel Marceau the famous mime ?

I'm just being curious :-).

Regards

Marcel::MimeType.for inconsistent results

An issue was raised in Rails reporting ActiveStorage was not producing accurate MIME types. This is traced back to calling Marcel::MimeType.for:

https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/blob.rb

  def extract_content_type(io)
      Marcel::MimeType.for io, name: filename.to_s, declared_type: content_type
    end

rails/rails#32632

If the io starts with "Article" then it results in "message/news", otherwise 'text/csv'. Do you know what is happening here and how to fix it?

Please see examples below:

2.5.0 Marcel::MimeType.for(StringIO.new("1895,1"), name: 'text.csv', declared_type: 'text/csv')
=> "text/csv"
2.5.0 Marcel::MimeType.for(StringIO.new("1895 1"), name: 'text.csv', declared_type: 'text/csv')
=> "text/csv"
2.5.0 Marcel::MimeType.for(StringIO.new("Article dates analysis"), name: 'text.csv', declared_type: 'text/csv')
=> "message/news"
2.5.0 Marcel::MimeType.for(StringIO.new("dates analysis"), name: 'text.csv', declared_type: 'text/csv')
=> "text/csv"
2.5.0 Marcel::MimeType.for(StringIO.new("Dates analysis"), name: 'text.csv', declared_type: 'text/csv')
=> "text/csv"
2.5.0 Marcel::MimeType.for(StringIO.new("article"), name: 'text.csv', declared_type: 'text/csv')
=> "text/csv"
2.5.0 Marcel::MimeType.for(StringIO.new("Article"), name: 'text.csv', declared_type: 'text/csv')
=> "message/news"

Yanked mimemagic 0.3.5 leaves us with no buildable version of rails 6.0

mimemagic 0.3.5 has been yanked from rubygems, marcel needs to be upgraded to use 0.4.0 and released ASAP.

Rails applications depending on activestorage -> marcel are unable to be built currently..

Bundler could not find compatible versions for gem "mimemagic":
  In Gemfile:
    mimemagic (~> 0.4.0)

    rails (~> 6.0.3, >= 6.0.3.4) was resolved to 6.0.3.4, which depends on
      activestorage (= 6.0.3.4) was resolved to 6.0.3.4, which depends on
        marcel (~> 0.3.1) was resolved to 0.3.3, which depends on
          mimemagic (~> 0.3.2)

Installation of Marcel v1.0.0 breaks with "cannot load such file -- mimemagic/overlay (LoadError)"

After upgrade Rails from 6.1.3 to 6.1.3.1, the application not load, throwing a error like this one below:

cannot load such file -- mimemagic/overlay (LoadError)
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:89:in `register'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:44:in `require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/activesupport-6.1.3.1/lib/active_support/dependencies.rb:332:in `block in require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/activesupport-6.1.3.1/lib/active_support/dependencies.rb:299:in `load_dependency'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/activesupport-6.1.3.1/lib/active_support/dependencies.rb:332:in `require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/carrierwave-2.2.0/lib/carrierwave/sanitized_file.rb:5:in `<main>'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/activesupport-6.1.3.1/lib/active_support/dependencies.rb:332:in `block in require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/activesupport-6.1.3.1/lib/active_support/dependencies.rb:299:in `load_dependency'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/activesupport-6.1.3.1/lib/active_support/dependencies.rb:332:in `require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/carrierwave-2.2.0/lib/carrierwave.rb:99:in `<main>'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bootsnap-1.7.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bundler-2.2.15/lib/bundler/runtime.rb:66:in `block (2 levels) in require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bundler-2.2.15/lib/bundler/runtime.rb:61:in `each'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bundler-2.2.15/lib/bundler/runtime.rb:61:in `block in require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bundler-2.2.15/lib/bundler/runtime.rb:50:in `each'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bundler-2.2.15/lib/bundler/runtime.rb:50:in `require'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/bundler-2.2.15/lib/bundler.rb:173:in `require'
  /vagrant/config/application.rb:11:in `<top (required)>'
  /vagrant/config/environment.rb:3:in `require_relative'
  /vagrant/config/environment.rb:3:in `<top (required)>'
  config.ru:3:in `require_relative'
  config.ru:3:in `block in <main>'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/rack-2.2.3/lib/rack/builder.rb:125:in `instance_eval'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/rack-2.2.3/lib/rack/builder.rb:125:in `initialize'
  config.ru:1:in `new'
  config.ru:1:in `<main>'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/passenger-6.0.7/src/helper-scripts/rack-preloader.rb:101:in `eval'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/passenger-6.0.7/src/helper-scripts/rack-preloader.rb:101:in `preload_app'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/passenger-6.0.7/src/helper-scripts/rack-preloader.rb:189:in `block in <module:App>'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/passenger-6.0.7/src/ruby_supportlib/phusion_passenger/loader_shared_helpers.rb:382:in `run_block_and_record_step_progress'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/passenger-6.0.7/src/helper-scripts/rack-preloader.rb:188:in `<module:App>'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/passenger-6.0.7/src/helper-scripts/rack-preloader.rb:30:in `<module:PhusionPassenger>'
  /usr/local/rvm/gems/ruby-3.0.0@social/gems/passenger-6.0.7/src/helper-scripts/rack-preloader.rb:29:in `<main>'

I've posted the entire stack strace, for better understanding. Maybe this can be related to recent license changes in mimemagic, right?

Related links: https://stackoverflow.com/q/66837647

Thanks a lot! ๐Ÿค ๐Ÿป

tests failing with mimemagic 0.3.5

When trying to run the tests with mimemagic 0.3.5, I get 5 tests failing.
They are all related to the reported mime type of truetype fonts:

Instead application/x-font-ttf.ttf, they are font/ttf

   1) correctly returns application/x-font-ttf for application/x-font-ttf.ttf given both file and declared type
      --- expected
      +++ actual
      @@ -1 +1,3 @@
      -"application/x-font-ttf"
      +# encoding: ASCII-8BIT
      +#    valid: true
      +"font/ttf"

      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:183
      # /usr/lib/ruby/vendor_ruby/minitest/utils/extension.rb:6
      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:218
      # test/magic_and_declared_type_test.rb:7
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:98
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:195
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:95
      # /usr/lib/ruby/vendor_ruby/minitest.rb:270
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:94
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:211
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:93
      # /usr/lib/ruby/vendor_ruby/minitest.rb:1029
      # /usr/lib/ruby/vendor_ruby/minitest.rb:339
      # /usr/lib/ruby/vendor_ruby/minitest.rb:326
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest.rb:352
      # /usr/lib/ruby/vendor_ruby/minitest.rb:324
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:141
      # /usr/lib/ruby/vendor_ruby/minitest.rb:68

   2) gets type for application/x-font-ttf by using only magic bytes application/x-font-ttf.ttf
      --- expected
      +++ actual
      @@ -1 +1,3 @@
      -"application/x-font-ttf"
      +# encoding: ASCII-8BIT
      +#    valid: true
      +"font/ttf"

      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:183
      # /usr/lib/ruby/vendor_ruby/minitest/utils/extension.rb:6
      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:218
      # test/magic_test.rb:10
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:98
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:195
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:95
      # /usr/lib/ruby/vendor_ruby/minitest.rb:270
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:94
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:211
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:93
      # /usr/lib/ruby/vendor_ruby/minitest.rb:1029
      # /usr/lib/ruby/vendor_ruby/minitest.rb:339
      # /usr/lib/ruby/vendor_ruby/minitest.rb:326
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest.rb:352
      # /usr/lib/ruby/vendor_ruby/minitest.rb:324
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:141
      # /usr/lib/ruby/vendor_ruby/minitest.rb:68

   3) correctly returns application/x-font-ttf for application/x-font-ttf.ttf given both file and name
      --- expected
      +++ actual
      @@ -1 +1,3 @@
      -"application/x-font-ttf"
      +# encoding: ASCII-8BIT
      +#    valid: true
      +"font/ttf"

      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:183
      # /usr/lib/ruby/vendor_ruby/minitest/utils/extension.rb:6
      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:218
      # test/magic_and_name_test.rb:10
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:98
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:195
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:95
      # /usr/lib/ruby/vendor_ruby/minitest.rb:270
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:94
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:211
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:93
      # /usr/lib/ruby/vendor_ruby/minitest.rb:1029
      # /usr/lib/ruby/vendor_ruby/minitest.rb:339
      # /usr/lib/ruby/vendor_ruby/minitest.rb:326
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest.rb:352
      # /usr/lib/ruby/vendor_ruby/minitest.rb:324
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:141
      # /usr/lib/ruby/vendor_ruby/minitest.rb:68

   4) gets type for application/x-font-ttf by filename from application/x-font-ttf.ttf
      --- expected
      +++ actual
      @@ -1 +1,3 @@
      -"application/x-font-ttf"
      +# encoding: ASCII-8BIT
      +#    valid: true
      +"font/ttf"

      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:183
      # /usr/lib/ruby/vendor_ruby/minitest/utils/extension.rb:6
      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:218
      # test/name_test.rb:7
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:98
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:195
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:95
      # /usr/lib/ruby/vendor_ruby/minitest.rb:270
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:94
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:211
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:93
      # /usr/lib/ruby/vendor_ruby/minitest.rb:1029
      # /usr/lib/ruby/vendor_ruby/minitest.rb:339
      # /usr/lib/ruby/vendor_ruby/minitest.rb:326
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest.rb:352
      # /usr/lib/ruby/vendor_ruby/minitest.rb:324
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:141
      # /usr/lib/ruby/vendor_ruby/minitest.rb:68

   5) gets type for application/x-font-ttf given file extension .ttf
      --- expected
      +++ actual
      @@ -1 +1,3 @@
      -"application/x-font-ttf"
      +# encoding: ASCII-8BIT
      +#    valid: true
      +"font/ttf"

      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:183
      # /usr/lib/ruby/vendor_ruby/minitest/utils/extension.rb:6
      # /usr/lib/ruby/vendor_ruby/minitest/assertions.rb:218
      # test/extension_test.rb:19
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:98
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:195
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:95
      # /usr/lib/ruby/vendor_ruby/minitest.rb:270
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:94
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:211
      # /usr/lib/ruby/vendor_ruby/minitest/test.rb:93
      # /usr/lib/ruby/vendor_ruby/minitest.rb:1029
      # /usr/lib/ruby/vendor_ruby/minitest.rb:339
      # /usr/lib/ruby/vendor_ruby/minitest.rb:326
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:325
      # /usr/lib/ruby/vendor_ruby/minitest.rb:365
      # /usr/lib/ruby/vendor_ruby/minitest.rb:352
      # /usr/lib/ruby/vendor_ruby/minitest.rb:324
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:164
      # /usr/lib/ruby/vendor_ruby/minitest.rb:141
      # /usr/lib/ruby/vendor_ruby/minitest.rb:68

Finished in 0.330862s, 810.0066 runs/s, 822.0962 assertions/s.
268 runs, 272 assertions, 5 failures, no errors, no skips

Failed Tests:

rake TEST=/home/boutil/debian/ruby-team/ruby-marcel/test/magic_and_declared_type_test.rb TESTOPTS="--name=test_correctly_returns_application/x-font-ttf_for_application/x-font-ttf.ttf_given_both_file_and_declared_type"
rake TEST=/home/boutil/debian/ruby-team/ruby-marcel/test/magic_test.rb TESTOPTS="--name=test_gets_type_for_application/x-font-ttf_by_using_only_magic_bytes_application/x-font-ttf.ttf"
rake TEST=/home/boutil/debian/ruby-team/ruby-marcel/test/magic_and_name_test.rb TESTOPTS="--name=test_correctly_returns_application/x-font-ttf_for_application/x-font-ttf.ttf_given_both_file_and_name"
rake TEST=/home/boutil/debian/ruby-team/ruby-marcel/test/name_test.rb TESTOPTS="--name=test_gets_type_for_application/x-font-ttf_by_filename_from_application/x-font-ttf.ttf"
rake TEST=/home/boutil/debian/ruby-team/ruby-marcel/test/extension_test.rb TESTOPTS="--name=test_gets_type_for_application/x-font-ttf_given_file_extension_.ttf"

AVIF image variants are assigned the wrong :content_type

Same as rails/rails#43150

Steps to reproduce

Repro

Expected behavior

AVIF image variant blobs should have a :content_type of 'image/avif'

Actual behavior

AVIF image variants have a :content_type of 'video/quicktime' on the blob. At least they do when running on MacOS. The images display correctly in Chrome, but the "Content-type" header is wrong as a result of the incorrect attribute.

System configuration

Rails version: Edge
Marcel version: Edge

Ruby version: 3.0

JSON file with html content detected as text/html since v1.0.0

Hello, I've noticed that json files with html content are reported as text/html, which isn't correct, is it?. Accidentally, I tested also against v0.3.3 and it reported it correctly as application/json, so one could say it's a regression?

I'm raising this issue, but not sure if it's fixable or if it's even a bug? ๐Ÿ™ˆ

require 'bundler/inline'
require "stringio"

gemfile do
  source 'https://rubygems.org'
  gem 'marcel', '1.0.2'
  # gem 'marcel', '0.3.3'
end

puts Marcel::VERSION
string = StringIO.new('{"a": "b"}')
puts string.read + ": " + Marcel::MimeType.for(string, name: "test.json")
string = StringIO.new('{"a": "<html"}')
puts string.read + ": " + Marcel::MimeType.for(string, name: "test.json")
string = StringIO.new('{"a": "<htm"}')
puts string.read + ": " + Marcel::MimeType.for(string, name: "test.json")

output for 1.0.2:

1.0.2
{"a": "b"}: application/json
{"a": "<html"}: text/html
{"a": "<htm"}: application/json

output for 0.3.3

0.3.3
{"a": "b"}: application/json
{"a": "<html"}: application/json
{"a": "<htm"}: application/json

Let me know if you need help with this ๐Ÿ˜…

JSON being incorrectly mapped

Any JSON file I run through Marcel, i.e.:

{"data": "test"}

comes back as application/octet-stream instead of application/json.

csv with BOM, detected as text/plain

Hello,

I use version 1.0.4 with carriewave, the file is csv with BOM, but it detected content_type text/plain

see below screenshot, the first file is csv, it detected as text/csv
the second file is csv with BOM, it detected as text/plain

Monosnap Hyper 2024-04-29 15-14-28

Summary of a number of differences in mime type reporting before and after Tika

Hello ๐Ÿ‘‹

In light of the differences that are showing up in mime type reporting pre and post Tika I thought it might be nice to try and get ahead of the bug reports by trying to get a big set of example files and run the mime type detection on them before and after the change to Tika.

I found a source of about 500 files here https://gitlab.freedesktop.org/xdg/shared-mime-info/-/tree/master/tests/mime-detection. Unfortunately Tika doesn't seem to have a similar set of test files in the source afaict.

I then ran the following test script against this set of files:

require "marcel"

ARGV.each do |filename|
  basename = File.basename(filename)

  File.open(filename) do |file|
    puts "%s %s" % [basename, Marcel::MimeType.for(file, name: basename)]
  end
end

I ran this script using 2 versions of Marcel - v0.3.3 and the current at time of writing HEAD - a525d5b

The attached CSV shows all the instances where a different MIME type was reported between the two versions. There are a total of 286. Most of the MIME types I would say are fairly niche and could no doubt be ignored without ever causing anyone a problem. But there are some common ones in there. And conversely the set of files is not a complete list of all MIME types known to humanity, so there will no doubt still be others that show up.

Anyway, I figured this list may be useful. Feel free to close this issue if it's not. ๐Ÿฅฐ

mimetype_for_diff-v0.3.3-a525d5b3.csv

Google Docs/Sheets/PPT detection is being limited to 64kb (offset 30:65536)

Hi team,

Thanks for migrating this gem to use Tika and replaced mimemagic gem, we're using the latest gem version on production and so far so good, great work, thank you for your hard work!

We just figured out that some certain xlsx and docx files which are uploaded from our users are being miss-detected as application/zip, same as this issue #35

But it only happen with some files that have a size larger than 64kb

Summary:

There were 3 xlsx files:

  1. test.xlsx => 5kb => mimetype: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
  2. test2.xlsx => 30kb => mimetype: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
  3. test3.xlsx => 368kb => mimetype: application/zip

The root cause of 3rd case is it's failed when executing a matching comparison for [Content_Types].xml with offset is 30:65536 while Google Docs/sheets have the fingerprint items at the end of the file.

Can we implement a negative offset to read from the end of the file for these cases?

Marcel incorrectly reports all text files as `application/octet-stream`

On plain ASCII text, even with a .txt extension, Marcel thinks the mime type is application/octet-stream instead of text/plain. Here is code to reproduce the issue:

File.write("/tmp/foo.txt", "hello world")
Marcel::MimeType.for(Pathname.new("/tmp/foo.txt"))
# returns "application/octet-stream" 

Note that file correctly identifies the content:

$ file /tmp/foo.txt                                                                                                                       
/tmp/foo.txt: ASCII text, with no line terminators                                                                                                           

This is in Marcel 1.0.2.

JPEG XL image variants are assigned the wrong :content_type

Steps to reproduce

Download a JXL ( EG from here )

Run Marcel::MimeType.for Pathname.new('logo.jxl') or Marcel::MimeType.for(File.read('logo.jxl'))

Expected result

The correct mime type is 'image/jxl'

Actual behaviour

The mime type 'application/octet-stream' is returned.

irb(main):022> Marcel::MimeType.for Pathname.new('logo.jxl')
=> "application/octet-stream"
Marcel::MimeType.for(File.read('logo.jxl'))
=> "application/octet-stream"

Incorrect identification for JS content starting with comments

I am opening this issue for discussion due to some inconsistent behaviour, coming from ActiveStorage #identify method...

There is an inconsistent behaviour in method Marcel::MimeType.for concerning (at least) javascript files starting with comments and given specific declared types (text/javascript or application/x-javascript). For example:

Marcel::MimeType.for("//Comment\nconsole.log('test');", name: 'sample.js', declared_type: 'application/javascript')
# => application/javascript --> Correct

Marcel::MimeType.for("console.log('test');", name: 'sample.js', declared_type: 'text/javascript')
# => text/javascript --> Correct

Marcel::MimeType.for("//Comment\nconsole.log('test');", name: 'sample.js', declared_type: 'text/javascript')
# => text/plain --> Inconsistent (Incorrect?)

Marcel::MimeType.for("//Comment\nconsole.log('test');", name: 'sample.js', declared_type: 'application/x-javascript')
# => text/plain --> Inconsistent (Incorrect?)

Of course, the explanation is that on (generated) file lib/marcel/tables.rb there are definitions only for type application/javascript so the above behaviour is somehow expected.

The thing is that on file data/tika.xml, we have reference for types text/javascript and application/x-javascript, as they are described as "alias" of application/javascript.

marcel/data/tika.xml

Lines 335 to 340 in 8e28563

<mime-type type="application/javascript">
<alias type="application/x-javascript"/>
<alias type="text/javascript"/>
<sub-class-of type="text/plain"/>
<_comment>JavaScript Source Code</_comment>
<glob pattern="*.js"/>

So my question is, should we change the script/generate_tables.rb script in order to also take into consideration the aliases, when adding values on constants TYPE_EXTS and TYPE_PARENTS?? Something like:

TYPE_EXTS = {
  ...
  'application/javascript' => %w(js),
  'application/x-javascript' => %w(js),
  'text/javascript' => %w(js),
  ...
}

TYPE_PARENTS = {
    ...
    'application/javascript' => %w(text/plain),
    'application/x-javascript' => %w(text/plain),
    'text/javascript' => %w(text/plain),
    ...
}

Thanks!

Can I haz release?

Looks like codebase is relatively stable. Can we get a 1.0.2 release please?

Does not detect html if the document contains svg.

Issue

If html file contains svg tag Marcel will detect "image/svg+xml" type.
This causes some problems with the browser, it says: "This page contains the following errors:"

Investigation

I use Marcel::MimeType.for and if I give pathname or IO it will call this,
then this (magic_match(io, method)) with :find.

In the end, we iterate by MAGIC and find first matched type.

Conclusion

When I wrote this issue, I found a fix, but the latest version does not have it. When are you planning to release the next release?

Bitmap type conflicts with Rails defaults

Marcel 1.0.2 returns image/bmp for bitmap files, which results in a deprecation warning when passed to Active Storage in Rails 7.0:

DEPRECATION WARNING: image/bmp is not a valid content type, it should not be used when creating a blob, and support for it will be removed in Rails 7.1. If you want to keep supporting this content type past Rails 7.1, add it to `config.active_storage.variable_content_types`. Dismiss this warning by setting `config.active_storage.silence_invalid_content_types_warning = true`.

The preferred form is image/x-bmp.

Breaking change with 1.0.3 - removal of Marcel::TYPES

CarrierWave (at least version 3.0.5) relied on Marcel::TYPES (see here)

It looks like Marcel::Types changed ages ago but was just released with 1.0.3. Was this constant intended to be public (breaking change in a patch release) or potentially is CarrierWave doing something it shouldn't?

Getting stack traces that look like:

     NameError:
       uninitialized constant Marcel::TYPES
     # ./app/models/attachment.rb:44:in `block in set_remote_url'
     # ./app/models/attachment.rb:40:in `set_remote_url'

Add test cases for most common BC3 upload types

  • jpg, 32454
  • png, 19749
  • pdf, 19130
  • docx, 9030
  • xlsx, 3926
  • jpeg, 2357
  • pptx, 1699
  • zip, 1451
  • gif, 1092
  • doc, 962
  • mp4, 770
  • psd, 756
  • xls, 743
  • mp3, 602
  • ai, 549
  • mov, 411
  • eps, 386
  • csv, 334
  • txt, 285
  • html, 283
  • tif, 250
  • wav, 230
  • key, 149
  • pages, 105

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.