4483 lines
170 KiB
C++
4483 lines
170 KiB
C++
//==================================================================//
|
|
/*
|
|
AtomicParsley - main.cpp
|
|
|
|
AtomicParsley is GPL software; you can freely distribute,
|
|
redistribute, modify & use under the terms of the GNU General
|
|
Public License; either version 2 or its successor.
|
|
|
|
AtomicParsley is distributed under the GPL "AS IS", without
|
|
any warranty; without the implied warranty of merchantability
|
|
or fitness for either an expressed or implied particular purpose.
|
|
|
|
Please see the included GNU General Public License (GPL) for
|
|
your rights and further details; see the file COPYING. If you
|
|
cannot, write to the Free Software Foundation, 59 Temple Place
|
|
Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
|
|
|
|
Copyright (c) 2005-2007 puck_lock
|
|
with contributions from others; see the CREDITS file
|
|
|
|
----------------------
|
|
Code Contributions by:
|
|
|
|
* Mike Brancato - Debian patches & build support
|
|
* Brian Story - porting getopt & native Win32 patches
|
|
*/
|
|
//==================================================================//
|
|
|
|
#include "AtomicParsley.h"
|
|
|
|
// define one-letter cli options for
|
|
#define OPT_HELP 'h'
|
|
#define OPT_TEST 'T'
|
|
#define OPT_ShowTextData 't'
|
|
#define OPT_ExtractPix 'E'
|
|
#define OPT_ExtractPixToPath 'e'
|
|
#define Meta_artist 'a'
|
|
#define Meta_artDirector 0xC6
|
|
#define Meta_arranger 0xC8
|
|
#define Meta_author 0xC9
|
|
#define Meta_conductor 0xD0
|
|
#define Meta_director 0xD1
|
|
#define Meta_originalArtist 0xD2
|
|
#define Meta_producer 0xD3
|
|
//#define Meta_performer 0xD4
|
|
#define Meta_soundEngineer 0xD5
|
|
#define Meta_soloist 0xD6
|
|
#define Meta_executiveProducer 0xD7
|
|
#define Meta_songtitle 's'
|
|
#define Meta_subtitle 0xC5
|
|
#define Meta_album 'b'
|
|
#define Meta_tracknum 'k'
|
|
#define Meta_disknum 'd'
|
|
#define Meta_genre 'g'
|
|
#define Meta_comment 'c'
|
|
#define Meta_year 'y'
|
|
#define Meta_lyrics 'l'
|
|
#define Meta_lyrics_file 0xC7
|
|
#define Meta_composer 'w'
|
|
#define Meta_copyright 'x'
|
|
#define Meta_grouping 'G'
|
|
#define Meta_album_artist 'A'
|
|
#define Meta_compilation 'C'
|
|
#define Meta_hdvideo 'O'
|
|
#define Meta_BPM 'B'
|
|
#define Meta_artwork 'r'
|
|
#define Meta_advisory 'V'
|
|
#define Meta_stik 'S'
|
|
#define Meta_description 'p'
|
|
#define Meta_Rating 0xCB
|
|
#define Meta_longdescription 'j'
|
|
#define Meta_TV_Network 'n'
|
|
#define Meta_TV_ShowName 'H'
|
|
#define Meta_TV_EpisodeNumber 'N'
|
|
#define Meta_TV_SeasonNumber 'U'
|
|
#define Meta_TV_Episode 'I'
|
|
#define Meta_podcastFlag 'f'
|
|
#define Meta_category 'q'
|
|
#define Meta_keyword 'K'
|
|
#define Meta_podcast_URL 'L'
|
|
#define Meta_podcast_GUID 'J'
|
|
#define Meta_PurchaseDate 'D'
|
|
#define Meta_apID 'Y'
|
|
#define Meta_cnID 0xC0
|
|
#define Meta_geID 0xC2
|
|
#define Meta_xID 0xC3
|
|
#define Meta_storedescription 0xC4
|
|
#define Meta_EncodingTool 0xB7
|
|
#define Meta_EncodedBy 0xC1
|
|
#define Meta_PlayGapless 0xBA
|
|
#define Meta_SortOrder 0xBF
|
|
|
|
#define Meta_ReverseDNS_Form 'M'
|
|
#define Meta_rDNS_rating 0xBB
|
|
|
|
#define Meta_StandardDate 'Z'
|
|
#define Meta_URL 'u'
|
|
#define Meta_Information 'i'
|
|
#define Meta_uuid 'z'
|
|
#define Opt_Extract_all_uuids 0xB8
|
|
#define Opt_Extract_a_uuid 0xB9
|
|
#define Opt_Ipod_AVC_uuid 0xBE
|
|
|
|
#define Metadata_Purge 'P'
|
|
#define UserData_Purge 'X'
|
|
#define foobar_purge '.'
|
|
#define Meta_dump 'Q'
|
|
#define Manual_atom_removal 'R'
|
|
#define Opt_FreeFree 'F'
|
|
#define OPT_OutputFile 'o'
|
|
#define OPT_NoOptimize 0xBD
|
|
|
|
#define OPT_OverWrite 'W'
|
|
|
|
#if defined(_WIN32)
|
|
#define OPT_PreserveTimeStamps 0xCA
|
|
#endif
|
|
|
|
#define ISO_Copyright 0xAA
|
|
|
|
#define _3GP_Title 0xAB
|
|
#define _3GP_Author 0xAC
|
|
#define _3GP_Performer 0xAD
|
|
#define _3GP_Genre 0xAE
|
|
#define _3GP_Description 0xAF
|
|
#define _3GP_Copyright 0xB0
|
|
#define _3GP_Album 0xB1
|
|
#define _3GP_Year 0xB2
|
|
#define _3GP_Rating 0xB3
|
|
#define _3GP_Classification 0xB4
|
|
#define _3GP_Keyword 0xB5
|
|
#define _3GP_Location 0xB6
|
|
|
|
#define Meta_ID3v2Tag 0xBC
|
|
|
|
#define Meta_movementCount 0xE0
|
|
#define Meta_movementName 0xE1
|
|
#define Meta_movementNumber 0xE2
|
|
#define Meta_showWorkMovement 0xE3
|
|
#define Meta_work 0xE4
|
|
|
|
char *output_file;
|
|
|
|
int total_args;
|
|
|
|
static void kill_signal(int sig);
|
|
|
|
static void kill_signal(int sig) { exit(0); }
|
|
|
|
// less than 80 (max 78) char wide, giving a general (concise) overview
|
|
static const char *shortHelp_text =
|
|
"\n"
|
|
"AtomicParlsey sets metadata into MPEG-4 files & derivatives supporting 3 "
|
|
"tag\n"
|
|
" schemes: iTunes-style, 3GPP assets & ISO defined copyright "
|
|
"notifications.\n"
|
|
"\n"
|
|
"AtomicParlsey quick help for setting iTunes-style metadata into MPEG-4 "
|
|
"files.\n"
|
|
"\n"
|
|
"General usage examples:\n"
|
|
" AtomicParsley /path/to.mp4 -T 1\n"
|
|
" AtomicParsley /path/to.mp4 -t +\n"
|
|
" AtomicParsley /path/to.mp4 --artist \"Me\" --artwork /path/to/art.jpg\n"
|
|
" Atomicparsley /path/to.mp4 --albumArtist \"You\" --podcastFlag true\n"
|
|
" Atomicparsley /path/to.mp4 --stik \"TV Show\" --advisory explicit\n"
|
|
"\n"
|
|
"Getting information about the file & tags:\n"
|
|
" -T --test Test file for mpeg4-ishness & print atom tree\n"
|
|
" -t --textdata Prints tags embedded within the file\n"
|
|
" -E --extractPix Extracts pix to the same folder as the mpeg-4 file\n"
|
|
"\n"
|
|
"Setting iTunes-style metadata tags\n"
|
|
" --artist (string) Set the artist tag\n"
|
|
" --title (string) Set the title tag\n"
|
|
" --album (string) Set the album tag\n"
|
|
" --genre (string) Genre tag (see --longhelp for more info)\n"
|
|
" --tracknum (num)[/tot] Track number (or track number/total "
|
|
"tracks)\n"
|
|
" --disk (num)[/tot] Disk number (or disk number/total disks)\n"
|
|
" --comment (string) Set the comment tag\n"
|
|
" --year (num|UTC) Year tag (see --longhelp for \"Release "
|
|
"Date\")\n"
|
|
" --lyrics (string) Set lyrics (not subject to 256 byte limit)\n"
|
|
" --lyricsFile (/path) Set lyrics to the content of a file\n"
|
|
" --composer (string) Set the composer tag\n"
|
|
" --copyright (string) Set the copyright tag\n"
|
|
" --grouping (string) Set the grouping tag\n"
|
|
" --artwork (/path) Set a piece of artwork (jpeg or png only)\n"
|
|
" --bpm (number) Set the tempo/bpm\n"
|
|
" --albumArtist (string) Set the album artist tag\n"
|
|
" --compilation (boolean) Set the compilation flag (true or false)\n"
|
|
" --hdvideo (number) Set the hdvideo flag to one of:\n"
|
|
" false or 0 for standard definition\n"
|
|
" true or 1 for 720p\n"
|
|
" 2 for 1080p\n"
|
|
" --advisory (string*) Content advisory (*values: 'clean', "
|
|
"'explicit')\n"
|
|
" --stik (string*) Sets the iTunes \"stik\" atom (see "
|
|
"--longhelp)\n"
|
|
" --description (string) Set the description tag\n"
|
|
" --longdesc (string) Set the long description tag\n"
|
|
" --storedesc (string) Set the store description tag\n"
|
|
" --TVNetwork (string) Set the TV Network name\n"
|
|
" --TVShowName (string) Set the TV Show name\n"
|
|
" --TVEpisode (string) Set the TV episode/production code\n"
|
|
" --TVSeasonNum (number) Set the TV Season number\n"
|
|
" --TVEpisodeNum (number) Set the TV Episode number\n"
|
|
" --podcastFlag (boolean) Set the podcast flag (true or false)\n"
|
|
" --category (string) Sets the podcast category\n"
|
|
" --keyword (string) Sets the podcast keyword\n"
|
|
" --podcastURL (URL) Set the podcast feed URL\n"
|
|
" --podcastGUID (URL) Set the episode's URL tag\n"
|
|
" --purchaseDate (UTC) Set time of purchase\n"
|
|
" --encodingTool (string) Set the name of the encoder\n"
|
|
" --encodedBy (string) Set the name of the Person/company who "
|
|
"encoded the file\n"
|
|
" --apID (string) Set the Account Name\n"
|
|
" --cnID (number) Set the iTunes Catalog ID (see --longhelp)\n"
|
|
" --geID (number) Set the iTunes Genre ID (see --longhelp)\n"
|
|
" --xID (string) Set the vendor-supplied iTunes xID (see "
|
|
"--longhelp)\n"
|
|
" --gapless (boolean) Set the gapless playback flag\n"
|
|
" --contentRating (string*) Set tv/mpaa rating (see -rDNS-help)\n"
|
|
"\n"
|
|
"Deleting tags\n"
|
|
" Set the value to \"\": --artist \"\" --stik \"\" --bpm \"\"\n"
|
|
" To delete (all) artwork: --artwork REMOVE_ALL\n"
|
|
" manually removal: --manualAtomRemove "
|
|
"\"moov.udta.meta.ilst.ATOM\"\n"
|
|
"\n"
|
|
"More detailed iTunes help is available with AtomicParsley --longhelp\n"
|
|
"Setting reverse DNS forms for iTunes files: see --reverseDNS-help\n"
|
|
"Setting 3gp assets into 3GPP & derivative files: see --3gp-help\n"
|
|
"Setting copyright notices for all files: see --ISO-help\n"
|
|
"For file-level options & padding info: see --file-help\n"
|
|
"Setting custom private tag extensions: see --uuid-help\n"
|
|
"Setting ID3 tags onto mpeg-4 files: see --ID3-help\n"
|
|
"\n"
|
|
"----------------------------------------------------------------------";
|
|
|
|
// an expansive, verbose, unconstrained (about 112 char wide) detailing of
|
|
// options
|
|
static const char *longHelp_text =
|
|
"AtomicParsley help page for setting iTunes-style metadata into MPEG-4 "
|
|
"files. \n"
|
|
" (3gp help available with AtomicParsley --3gp-help)\n"
|
|
" (ISO copyright help available with AtomicParsley --ISO-help)\n"
|
|
" (reverse DNS form help available with AtomicParsley "
|
|
"--reverseDNS-help)\n"
|
|
"Usage: AtomicParsley [mp4FILE]... [OPTION]... [ARGUMENT]... [ "
|
|
"[OPTION2]...[ARGUMENT2]...] \n"
|
|
"\n"
|
|
"example: AtomicParsley /path/to.mp4 -e ~/Desktop/pix\n"
|
|
"example: AtomicParsley /path/to.mp4 --podcastURL \"http://www.url.net\" "
|
|
"--tracknum 45/356\n"
|
|
"example: AtomicParsley /path/to.mp4 --copyright \"\342\204\227 \302\251 "
|
|
"2006\"\n"
|
|
"example: AtomicParsley /path/to.mp4 --year \"2006-07-27T14:00:43Z\" "
|
|
"--purchaseDate timestamp\n"
|
|
"example: AtomicParsley /path/to.mp4 --sortOrder artist \"Mighty Dub Cats, "
|
|
"The\n"
|
|
"--------------------------------------------------------------------------"
|
|
"----------------------\n"
|
|
" Extract any pictures in user data \"covr\" atoms to separate files. \n"
|
|
" --extractPix , -E Extract to same folder "
|
|
"(basename derived from file).\n"
|
|
" --extractPixToPath , -e (/path/basename) Extract to specific path "
|
|
"(numbers added to basename).\n"
|
|
" example: --e "
|
|
"~/Desktop/SomeText\n"
|
|
" gives: "
|
|
"SomeText_artwork_1.jpg SomeText_artwork_2.png\n"
|
|
" Note: extension comes from "
|
|
"embedded image file format\n"
|
|
"--------------------------------------------------------------------------"
|
|
"----------------------\n"
|
|
" Tag setting options:\n"
|
|
"\n"
|
|
" --artist , -a (str) Set the artist tag: "
|
|
"\"moov.udta.meta.ilst.\302\251ART.data\"\n"
|
|
" --title , -s (str) Set the title tag: "
|
|
"\"moov.udta.meta.ilst.\302\251nam.data\"\n"
|
|
" --album , -b (str) Set the album tag: "
|
|
"\"moov.udta.meta.ilst.\302\251alb.data\"\n"
|
|
" --genre , -g (str) Set the genre tag: \"\302\251gen\" "
|
|
"(custom) or \"gnre\" (standard).\n"
|
|
" see the standard list with "
|
|
"\"AtomicParsley --genre-list\"\n"
|
|
" --tracknum , -k (num)[/tot] Set the track number (or track "
|
|
"number & total tracks).\n"
|
|
" --disk , -d (num)[/tot] Set the disk number (or disk "
|
|
"number & total disks).\n"
|
|
" --comment , -c (str) Set the comment tag: "
|
|
"\"moov.udta.meta.ilst.\302\251cmt.data\"\n"
|
|
" --year , -y (num|UTC) Set the year tag: "
|
|
"\"moov.udta.meta.ilst.\302\251day.data\"\n"
|
|
" set with UTC "
|
|
"\"2006-09-11T09:00:00Z\" for Release Date\n"
|
|
" --lyrics , -l (str) Set the lyrics tag: "
|
|
"\"moov.udta.meta.ilst.\302\251lyr.data\"\n"
|
|
" --lyricsFile , (/path) Set the lyrics tag to the content "
|
|
"of a file\n"
|
|
" --composer , -w (str) Set the composer tag: "
|
|
"\"moov.udta.meta.ilst.\302\251wrt.data\"\n"
|
|
" --copyright , -x (str) Set the copyright tag: "
|
|
"\"moov.udta.meta.ilst.cprt.data\"\n"
|
|
" --grouping , -G (str) Set the grouping tag: "
|
|
"\"moov.udta.meta.ilst.\302\251grp.data\"\n"
|
|
" --artwork , -A (/path) Set a piece of artwork (jpeg or "
|
|
"png) on \"covr.data\"\n"
|
|
" Note: multiple pieces are "
|
|
"allowed with more --artwork args\n"
|
|
" --bpm , -B (num) Set the tempo/bpm tag: "
|
|
"\"moov.udta.meta.ilst.tmpo.data\"\n"
|
|
" --albumArtist , -A (str) Set the album artist tag: "
|
|
"\"moov.udta.meta.ilst.aART.data\"\n"
|
|
" --compilation , -C (bool) Sets the \"cpil\" atom (true or "
|
|
"false to delete the atom)\n"
|
|
" --hdvideo , -V (bool) Sets the \"hdvd\" atom (true or "
|
|
"false to delete the atom)\n"
|
|
" --advisory , -y (1of3) Sets the iTunes lyrics advisory "
|
|
"('remove', 'clean', 'explicit') \n"
|
|
" --stik , -S (1of7) Sets the iTunes \"stik\" atom "
|
|
"(--stik \"remove\" to delete) \n"
|
|
" \"Movie\", \"Normal\", \"TV "
|
|
"Show\" .... others: \n"
|
|
" see the full list with "
|
|
"\"AtomicParsley --stik-list\"\n"
|
|
" or set in an integer value "
|
|
"with --stik value=(num)\n"
|
|
" Note: --stik Audiobook will change "
|
|
"file extension to '.m4b'\n"
|
|
" --description , -p (str) Sets the description on the "
|
|
"\"desc\" atom\n"
|
|
" --Rating , (str) Sets the Rating on the \"rate\" "
|
|
"atom\n"
|
|
" --longdesc , -j (str) Sets the long description on the "
|
|
"\"ldes\" atom\n"
|
|
" --storedesc , (str) Sets the iTunes store description "
|
|
"on the \"sdes\" atom\n"
|
|
" --TVNetwork , -n (str) Sets the TV Network name on the "
|
|
"\"tvnn\" atom\n"
|
|
" --TVShowName , -H (str) Sets the TV Show name on the "
|
|
"\"tvsh\" atom\n"
|
|
" --TVEpisode , -I (str) Sets the TV Episode on "
|
|
"\"tven\":\"209\", but it is a string: \"209 Part 1\"\n"
|
|
" --TVSeasonNum , -U (num) Sets the TV Season number on the "
|
|
"\"tvsn\" atom\n"
|
|
" --TVEpisodeNum , -N (num) Sets the TV Episode number on the "
|
|
"\"tves\" atom\n"
|
|
|
|
" --podcastFlag , -f (bool) Sets the podcast flag (values are "
|
|
"\"true\" or \"false\")\n"
|
|
" --category , -q (str) Sets the podcast category; "
|
|
"typically a duplicate of its genre\n"
|
|
" --keyword , -K (str) Sets the podcast keyword; invisible "
|
|
"to MacOSX Spotlight\n"
|
|
" --podcastURL , -L (URL) Set the podcast feed URL on the "
|
|
"\"purl\" atom\n"
|
|
" --podcastGUID , -J (URL) Set the episode's URL tag on the "
|
|
"\"egid\" atom\n"
|
|
" --purchaseDate , -D (UTC) Set Universal Coordinated Time of "
|
|
"purchase on a \"purd\" atom\n"
|
|
" (use \"timestamp\" to set UTC to "
|
|
"now; can be akin to id3v2 TDTG tag)\n"
|
|
" --encodingTool , (str) Set the name of the encoder on the "
|
|
"\"\302\251too\" atom\n"
|
|
" --encodedBy , (str) Set the name of the Person/company "
|
|
"who encoded the file on the \"\302\251enc\" atom\n"
|
|
" --apID , -Y (str) Set the name of the Account Name on "
|
|
"the \"apID\" atom\n"
|
|
" --cnID , (num) Set iTunes Catalog ID, used for "
|
|
"combining SD and HD encodes in iTunes on the \"cnID\" atom\n"
|
|
"\n"
|
|
" To combine you must set \"hdvd\" "
|
|
"atom on one file and must have same \"stik\" on both file\n"
|
|
" Must not use \"stik\" of value Home "
|
|
"Video(0), use Movie(9)\n"
|
|
"\n"
|
|
" iTunes Catalog numbers can be "
|
|
"obtained by finding the item in the iTunes Store. Once item\n"
|
|
" is found in the iTunes Store right "
|
|
"click on picture of item and select copy link. Paste this link\n"
|
|
" into a document or web browser to "
|
|
"display the catalog number ID.\n"
|
|
"\n"
|
|
" An example link for the video "
|
|
"Street Kings is:\n"
|
|
" "
|
|
"http://itunes.apple.com/WebObjects/MZStore.woa/wa/"
|
|
"viewMovie?id=278743714&s=143441\n"
|
|
" Here you can see the cnID is "
|
|
"278743714\n"
|
|
"\n"
|
|
" Alternatively you can use iMDB "
|
|
"numbers, however these will not match the iTunes catalog.\n"
|
|
"\n"
|
|
" --geID , (num) Set iTunes Genre ID. This does not "
|
|
"necessarily have to match genre.\n"
|
|
" See --genre-movie-id-list and "
|
|
"--genre-tv-id-list\n"
|
|
"\n"
|
|
" --xID , (str) Set iTunes vendor-supplied xID, "
|
|
"used to allow iTunes LPs and iTunes Extras to interact \n"
|
|
" with other content in your "
|
|
"iTunes Library\n"
|
|
" --gapless , (bool) Sets the gapless playback flag for "
|
|
"a track in a gapless album\n"
|
|
|
|
" --sortOrder (type) (str) Sets the sort order string for that "
|
|
"type of tag.\n"
|
|
" (available types are: \"name\", "
|
|
"\"artist\", \"albumartist\",\n"
|
|
" \"album\", \"composer\", "
|
|
"\"show\")\n"
|
|
"\n"
|
|
"NOTE: Except for artwork, only 1 of each tag is allowed; artwork allows "
|
|
"multiple pieces.\n"
|
|
"NOTE: Tags that carry text(str) have a limit of 255 utf8 characters;\n"
|
|
"however lyrics and long descriptions have no limit.\n"
|
|
"--------------------------------------------------------------------------"
|
|
"----------------------\n"
|
|
" To delete a single atom, set the tag to null (except artwork):\n"
|
|
" --artist \"\" --lyrics \"\"\n"
|
|
" --artwork REMOVE_ALL \n"
|
|
" --metaEnema , -P Douches away every atom under "
|
|
"\"moov.udta.meta.ilst\" \n"
|
|
" --foobar2000Enema , -2 Eliminates foobar2000's "
|
|
"non-compliant so-out-o-spec tagging scheme\n"
|
|
" --manualAtomRemove \"some.atom.path\" where some.atom.path can be:\n"
|
|
" keys to using manualAtomRemove:\n"
|
|
" ilst.ATOM.data or ilst.ATOM target an iTunes-style metadata tag\n"
|
|
" ATOM:lang=foo target an atom with this language "
|
|
"setting; like 3gp assets\n"
|
|
" ATOM.----.name:[foo] target a reverseDNS metadata tag; "
|
|
"like iTunNORM\n"
|
|
" Note: these atoms show up with 'AP "
|
|
"-t' as: Atom \"----\" [foo]\n"
|
|
" 'foo' is actually carried on the "
|
|
"'name' atom\n"
|
|
" ATOM[x] target an atom with an index other "
|
|
"than 1; like trak[2]\n"
|
|
" ATOM.uuid=hex-hex-hex-hex targt a uuid atom with the uuid of "
|
|
"hex string representation\n"
|
|
" examples:\n"
|
|
" moov.udta.meta.ilst.----.name:[iTunNORM] "
|
|
"moov.trak[3].cprt:lang=urd\n"
|
|
" moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740\n"
|
|
"--------------------------------------------------------------------------"
|
|
"----------------------\n"
|
|
|
|
#if defined(__APPLE__)
|
|
" Environmental Variables (affecting picture placement)\n"
|
|
"\n"
|
|
" set PIC_OPTIONS in your shell to set these flags; preferences are "
|
|
"separated by colons (:)\n"
|
|
"\n"
|
|
" MaxDimensions=num (default: 0; unlimited); sets maximum pixel "
|
|
"dimensions\n"
|
|
" DPI=num (default: 72); sets dpi\n"
|
|
" MaxKBytes=num (default: 0; unlimited); maximum kilobytes for file "
|
|
"(jpeg only)\n"
|
|
" AddBothPix=bool (default: false); add original & converted pic (for "
|
|
"archival purposes)\n"
|
|
" AllPixJPEG | AllPixPNG =bool (default: false); force conversion to a "
|
|
"specific picture format\n"
|
|
" SquareUp (include to square images to largest dimension, allows "
|
|
"an [ugly] 160x1200->1200x1200)\n"
|
|
" removeTempPix (include to delete temp pic files created when "
|
|
"resizing images after tagging)\n"
|
|
" ForceHeight=num (must also specify width, below) force image pixel "
|
|
"height\n"
|
|
" ForceWidth=num (must also specify height, above) force image pixel "
|
|
"width\n"
|
|
"\n"
|
|
" Examples: (bash-style)\n"
|
|
" export "
|
|
"PIC_OPTIONS=\"MaxDimensions=400:DPI=72:MaxKBytes=100:AddBothPix=true:"
|
|
"AllPixJPEG=true\"\n"
|
|
" export PIC_OPTIONS=\"SquareUp:removeTempPix\"\n"
|
|
" export PIC_OPTIONS=\"ForceHeight=999:ForceWidth=333:removeTempPix\"\n"
|
|
"--------------------------------------------------------------------------"
|
|
"----------------------\n"
|
|
#endif
|
|
;
|
|
|
|
static const char *fileLevelHelp_text =
|
|
"AtomicParsley help page for general & file level options.\n"
|
|
#if defined(_WIN32)
|
|
#ifndef __CYGWIN__
|
|
" Note: you can change the input/output behavior to raw 8-bit utf8 if the "
|
|
"program name\n"
|
|
" is appended with \"-utf8\". AtomicParsley-utf8.exe will have "
|
|
"problems with files/\n"
|
|
" folders with unicode characters in given paths.\n"
|
|
#else
|
|
" Note: you can change the input/output behavior for MCDI functions to "
|
|
"raw 8-bit utf8\n"
|
|
" if the program name is appended with \"-utf8\".\n"
|
|
#endif
|
|
"\n"
|
|
#endif
|
|
"--------------------------------------------------------------------------"
|
|
"----------------------\n"
|
|
" Atom reading services:\n"
|
|
"\n"
|
|
" --test , -T Tests file to see if it is a valid "
|
|
"MPEG-4 file.\n"
|
|
" Prints out the hierarchical atom "
|
|
"tree.\n"
|
|
" -T 1 Supplemental track level info with "
|
|
"\"-T 1\"\n"
|
|
" -T +dates Track level with creation/modified "
|
|
"dates\n"
|
|
"\n"
|
|
" --textdata , -t print user data text metadata relevant to "
|
|
"brand (inc. # of any pics).\n"
|
|
" -t + show supplemental info like free space, "
|
|
"available padding, user data\n"
|
|
" length & media data length\n"
|
|
" -t 1 show all textual metadata (disregards "
|
|
"brands, shows track copyright)\n"
|
|
"\n"
|
|
" --brands show the major & minor brands for the "
|
|
"file & available tagging schemes\n"
|
|
"\n"
|
|
"--------------------------------------------------------------------------"
|
|
"----------------------\n"
|
|
" File services:\n"
|
|
"\n"
|
|
" --freefree [num] , Remove \"free\" atoms which only "
|
|
"act as filler in the file\n"
|
|
" ?(num)? - optional integer argument "
|
|
"to delete 'free's to desired level\n"
|
|
"\n"
|
|
" NOTE 1: levels begin at level 1 aka "
|
|
"file level.\n"
|
|
" NOTE 2: Level 0 (which doesn't "
|
|
"exist) deletes level 1 atoms that pre-\n"
|
|
" cede 'moov' & don't serve "
|
|
"as padding. Typically, such atoms\n"
|
|
" are created by libmp4ff or "
|
|
"libmp4v2 as a byproduct of tagging.\n"
|
|
" NOTE 3: When padding falls below "
|
|
"MIN_PAD (typically zero), a default\n"
|
|
" amount of padding "
|
|
"(typically 2048 bytes) will be added. To\n"
|
|
" achieve absolutely 0 bytes "
|
|
"'free' space with --freefree, set\n"
|
|
" DEFAULT_PAD to 0 via the "
|
|
"AP_PADDING mechanism (see below).\n"
|
|
"\n"
|
|
" --preventOptimizing Prevents reorganizing the file to "
|
|
"have file metadata before media data.\n"
|
|
" iTunes/Quicktime have so far "
|
|
"*always* placed metadata first; many 3rd\n"
|
|
" party utilities do not (preventing "
|
|
"streaming to the web, AirTunes, iTV).\n"
|
|
" Used in conjunction with "
|
|
"--overWrite, files with metadata at the end\n"
|
|
" (most ffmpeg produced files) can "
|
|
"have their tags rapidly updated without\n"
|
|
" requiring a full rewrite. Note: "
|
|
"this does not un-optimize a file.\n"
|
|
" Note: this option will be canceled "
|
|
"out if used with the --freefree option\n"
|
|
"\n"
|
|
" --metaDump Dumps out 'moov.udta' metadata out "
|
|
"to a new file next to original\n"
|
|
" (for diagnostic purposes, "
|
|
"please remove artwork before sending)\n"
|
|
" --output , -o (/path) Specify the filename of tempfile "
|
|
"(voids overWrite)\n"
|
|
" --overWrite , -W Writes to temp file; deletes "
|
|
"original, renames temp to original\n"
|
|
" If possible, padding will be used "
|
|
"to update without a full rewrite.\n"
|
|
"\n"
|
|
#if defined(_WIN32)
|
|
" --preserveTime Will overwrite the original file in "
|
|
"place (--overWrite forced),\n"
|
|
" but will also keep the original "
|
|
"file's timestamps intact.\n"
|
|
"\n"
|
|
#endif
|
|
" --DeepScan Parse areas of the file that are "
|
|
"normally skipped (must be the 3rd arg)\n"
|
|
" --iPod-uuid (num) Place the ipod-required uuid for "
|
|
"higher resolution avc video files\n"
|
|
" Currently, the only number used is "
|
|
"1200 - the maximum number of macro-\n"
|
|
" blocks allowed by the higher "
|
|
"resolution iPod setting.\n"
|
|
" NOTE: this requires the "
|
|
"\"--DeepScan\" option as the 3rd cli argument\n"
|
|
" NOTE2: only works on the first avc "
|
|
"video track, not all avc tracks\n"
|
|
"\n"
|
|
"Examples: \n"
|
|
" --freefree 0 (deletes all top-level non-padding atoms preceding "
|
|
"'mooov') \n"
|
|
" --freefree 1 (deletes all non-padding atoms at the top most "
|
|
"level) \n"
|
|
" --output ~/Desktop/newfile.mp4\n"
|
|
" AP /path/to/file.m4v --DeepScan --iPod-uuid 1200\n"
|
|
|
|
"--------------------------------------------------------------------------"
|
|
"----------------------\n"
|
|
" Padding & 'free' atoms:\n"
|
|
"\n"
|
|
" A special type of atom called a 'free' atom is used for padding (all "
|
|
"'free' atoms contain NULL space).\n"
|
|
" When changes need to occur, these 'free' atom are used. They grows or "
|
|
"shink, but the relative locations\n"
|
|
" of certain other atoms (stco/mdat) remain the same. If there is no "
|
|
"'free' space, a full rewrite will occur.\n"
|
|
" The locations of 'free' atom(s) that AP can use as padding must be "
|
|
"follow 'moov.udta' & come before 'mdat'.\n"
|
|
" A 'free' preceding 'moov' or following 'mdat' won't be used as padding "
|
|
"for example. \n"
|
|
"\n"
|
|
" Set the shell variable AP_PADDING with these values, separated by "
|
|
"colons to alter padding behavior:\n"
|
|
"\n"
|
|
" DEFAULT_PADDING= - the amount of padding added if the minimum padding "
|
|
"is non-existant in the file\n"
|
|
" default = 2048\n"
|
|
" MIN_PAD= - the minimum padding present before more padding "
|
|
"will be added\n"
|
|
" default = 0\n"
|
|
" MAX_PAD= - the maximum allowable padding; excess padding will "
|
|
"be eliminated\n"
|
|
" default = 5000\n"
|
|
"\n"
|
|
" If you use --freefree to eliminate 'free' atoms from the file, the "
|
|
"DEFAULT_PADDING amount will still be\n"
|
|
" added to any newly written files. Set DEFAULT_PADDING=0 to prevent any "
|
|
"'free' padding added at rewrite.\n"
|
|
" You can set MIN_PAD to be assured that at least that amount of padding "
|
|
"will be present - similarly,\n"
|
|
" MAX_PAD limits any excessive amount of padding. All 3 options will in "
|
|
"all likelyhood produce a full\n"
|
|
" rewrite of the original file. Another case where a full rewrite will "
|
|
"occur is when the original file\n"
|
|
" is not optimized and has 'mdat' preceding 'moov'.\n"
|
|
"\n"
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
"Examples:\n"
|
|
" c:> SET AP_PADDING=\"DEFAULT_PAD=0\" or c:> SET "
|
|
"AP_PADDING=\"DEFAULT_PAD=3128\"\n"
|
|
" c:> SET AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n"
|
|
#else
|
|
"Examples (bash style):\n"
|
|
" $ export AP_PADDING=\"DEFAULT_PAD=0\" or $ export "
|
|
"AP_PADDING=\"DEFAULT_PAD=3128\"\n"
|
|
" $ export AP_PADDING=\"DEFAULT_PAD=5128:MIN_PAD=200:MAX_PAD=6049\"\n"
|
|
#endif
|
|
"\n"
|
|
"Note: while AtomicParsley is still in the beta stage, the original file "
|
|
"will always remain untouched - \n"
|
|
" unless given the --overWrite flag when if possible, utilizing "
|
|
"available padding to update tags\n"
|
|
" will be tried (falling back to a full rewrite if changes are "
|
|
"greater than the found padding).\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" iTunes 7 & Gapless playback:\n"
|
|
"\n"
|
|
" iTunes 7 adds NULL space at the ends of files (filled with zeroes). It "
|
|
"is possble this is how iTunes\n"
|
|
" implements gapless playback - perhaps not. In any event, with "
|
|
"AtomicParsley you can choose to preserve\n"
|
|
" that NULL space, or you can eliminate its presence (typically around "
|
|
"2,000 bytes). The default behavior\n"
|
|
" is to preserve it - if it is present at all. You can choose to eliminate "
|
|
"it by setting the environ-\n"
|
|
" mental preference for AP_PADDING to have DEFAULT_PAD=0\n"
|
|
"\n"
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
"Example:\n"
|
|
" c:> SET AP_PADDING=\"DEFAULT_PAD=0\"\n"
|
|
#else
|
|
"Example (bash style):\n"
|
|
" $ export AP_PADDING=\"DEFAULT_PAD=0\"\n"
|
|
#endif
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n";
|
|
|
|
// detailed options for 3gp branded files
|
|
static const char *_3gpHelp_text =
|
|
"AtomicParsley 3gp help page for setting 3GPP-style metadata.\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" 3GPP text tags can be encoded in either UTF-8 (default input encoding) "
|
|
"or UTF-16 (converted from UTF-8)\n"
|
|
" Many 3GPP text tags can be set for a desired language by a "
|
|
"3-letter-lowercase code (default is \"eng\").\n"
|
|
" For tags that support the language attribute (all except year), more "
|
|
"than one tag of the same name\n"
|
|
" (3 titles for example) differing in the language code is supported.\n"
|
|
"\n"
|
|
" iTunes-style metadata is not supported by the 3GPP TS 26.244 version "
|
|
"6.4.0 Release 6 specification.\n"
|
|
" 3GPP asset tags can be set at movie level or track level & are set in a "
|
|
"different hierarchy: moov.udta \n"
|
|
" if at movie level (versus iTunes moov.udta.meta.ilst). Other 3rd party "
|
|
"utilities may allow setting\n"
|
|
" iTunes-style metadata in 3gp files. When a 3gp file is detected (file "
|
|
"extension doesn't matter), only\n"
|
|
" 3gp spec-compliant metadata will be read & written.\n"
|
|
"\n"
|
|
" Note1: there are a number of different 'brands' that 3GPP files come "
|
|
"marked as. Some will not be \n"
|
|
" supported by AtomicParsley due simply to them being unknown and "
|
|
"untested. You can compile your\n"
|
|
" own AtomicParsley to evaluate it by adding the hex code into the "
|
|
"source of APar_IdentifyBrand.\n"
|
|
"\n"
|
|
" Note2: There are slight accuracy discrepancies in location's fixed "
|
|
"point decimals set and retrieved.\n"
|
|
"\n"
|
|
" Note3: QuickTime Player can see a limited subset of these tags, but "
|
|
"only in 1 language & there seems to\n"
|
|
" be an issue with not all unicode text displaying properly. This "
|
|
"is an issue withing QuickTime -\n"
|
|
" the exact same text (in utf8) displays properly in an MPEG-4 "
|
|
"file. Some languages can also display\n"
|
|
" more glyphs than others.\n"
|
|
"\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" Tag setting options (default user data area is movie level; default lang "
|
|
"is 'eng'; default encoding is UTF8):\n"
|
|
" required arguments are in (parentheses); optional arguments are in "
|
|
"[brackets]\n"
|
|
"\n"
|
|
" --3gp-title (str) [lang=3str] [UTF16] [area] ......... "
|
|
"Set a 3gp media title tag\n"
|
|
" --3gp-author (str) [lang=3str] [UTF16] [area] ......... "
|
|
"Set a 3gp author of the media tag\n"
|
|
" --3gp-performer (str) [lang=3str] [UTF16] [area] ......... "
|
|
"Set a 3gp performer or artist tag\n"
|
|
" --3gp-genre (str) [lang=3str] [UTF16] [area] ......... "
|
|
"Set a 3gp genre asset tag\n"
|
|
" --3gp-description (str) [lang=3str] [UTF16] [area] ......... "
|
|
"Set a 3gp description or caption tag\n"
|
|
" --3gp-copyright (str) [lang=3str] [UTF16] [area] ......... "
|
|
"Set a 3gp copyright notice tag*\n"
|
|
"\n"
|
|
" --3gp-album (str) [lang=3str] [UTF16] [trknum=int] [area] "
|
|
"Set a 3gp album tag (& opt. tracknum)\n"
|
|
" --3gp-year (int) [area] ........................... Set a "
|
|
"3gp recording year tag (4 digit only)\n"
|
|
"\n"
|
|
" --3gp-rating (str) [entity=4str] [criteria=4str] "
|
|
"[lang=3str] [UTF16] [area] Rating tag\n"
|
|
" --3gp-classification (str) [entity=4str] [index=int] "
|
|
"[lang=3str] [UTF16] [area] Classification\n"
|
|
"\n"
|
|
" --3gp-keyword (str) [lang=3str] [UTF16] [area] Format of "
|
|
"str: 'keywords=word1,word2,word3,word4'\n"
|
|
"\n"
|
|
" --3gp-location (str) [lang=3str] [UTF16] [area] Set a 3gp "
|
|
"location tag (default: Central Park)\n"
|
|
" [longitude=fxd.pt] [latitude=fxd.pt] "
|
|
"[altitude=fxd.pt]\n"
|
|
" [role=str] [body=str] [notes=str]\n"
|
|
" fxd.pt values are decimal coordinates "
|
|
"(55.01209, 179.25W, 63)\n"
|
|
" 'role=' values: 'shooting location', "
|
|
"'real location', 'fictional location'\n"
|
|
" a negative value in coordinates "
|
|
"will be seen as a cli flag\n"
|
|
" append 'S', 'W' or 'B': lat=55S, "
|
|
"long=90.23W, alt=90.25B\n"
|
|
"\n"
|
|
" [area] can be \"movie\", \"track\" or \"track=num\" where 'num' is the "
|
|
"number of the track. If not specificied,\n"
|
|
" assets will be placed at movie level. The \"track\" option sets the "
|
|
"asset across all available tracks\n"
|
|
"\n"
|
|
"Note1: '4str' = a 4 letter string like \"PG13\"; 3str is a 3 letter "
|
|
"string like \"eng\"; int is an integer\n"
|
|
"Note2: List all languages for '3str' with \"AtomicParsley --language-list "
|
|
"(unknown languages become \"und\")\n"
|
|
"*Note3: The 3gp copyright asset can potentially be altered by using the "
|
|
"--ISO-copyright setting.\n"
|
|
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
"Usage: AtomicParsley [3gpFILE] --option [argument] [optional_arguments] "
|
|
"[ --option2 [argument2]...] \n"
|
|
"\n"
|
|
"example: AtomicParsley /path/to.3gp -t \n"
|
|
"example: AtomicParsley /path/to.3gp -T 1 \n"
|
|
"example: Atomicparsley /path/to.3gp --3gp-performer \"Enjoy Yourself\" "
|
|
"lang=pol UTF16\n"
|
|
"example: Atomicparsley /path/to.3gp --3gp-year 2006 --3gp-album \"White "
|
|
"Label\" track=8 lang=fra\n"
|
|
"example: Atomicparsley /path/to.3gp --3gp-album \"Cow Cod Soup For "
|
|
"Everyone\" track=10 lang=car\n"
|
|
"\n"
|
|
"example: Atomicparsley /path/to.3gp --3gp-classification \"Poor Sport\" "
|
|
"entity=\"PTA \" index=12 UTF16\n"
|
|
"example: Atomicparsley /path/to.3gp --3gp-keyword "
|
|
"keywords=\"foo1,foo2,foo 3\" UTF16 --3gp-keyword \"\"\n"
|
|
"example: Atomicparsley /path/to.3gp --3gp-location 'Bethesda Terrace' "
|
|
"latitude=40.77 longitude=73.98W \n"
|
|
" altitude=4.3B "
|
|
"role='real' body=Earth notes='Underground'\n"
|
|
"\n"
|
|
"example: Atomicparsley /path/to.3gp --3gp-title \"I see London.\" "
|
|
"--3gp-title \"Veo Madrid.\" lang=spa \n"
|
|
" --3gp-title \"Widze Warsawa.\" "
|
|
"lang=pol\n"
|
|
"\n";
|
|
|
|
static const char *ISOHelp_text =
|
|
"AtomicParsley help page for setting ISO copyright notices at movie & "
|
|
"track level.\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" The ISO specification allows for setting copyright in a number of "
|
|
"places. This copyright atom is\n"
|
|
" independant of the iTunes-style --copyright tag that can be set. This "
|
|
"ISO tag is identical to the\n"
|
|
" 3GP-style copyright. In fact, using --ISO-copyright can potentially "
|
|
"overwrite the 3gp copyright\n"
|
|
" asset if set at movie level & given the same language to set the "
|
|
"copyright on. This copyright\n"
|
|
" notice is the only metadata tag defined by the reference ISO 14496-12 "
|
|
"specification.\n"
|
|
"\n"
|
|
" ISO copyright notices can be set at movie level, track level for a "
|
|
"single track, or for all tracks.\n"
|
|
" Multiple copyright notices are allowed, but they must differ in the "
|
|
"language setting. To see avail-\n"
|
|
" able languages use \"AtomicParsley --language-list\". Notices can be "
|
|
"set in utf8 or utf16.\n"
|
|
"\n"
|
|
" --ISO-copyright (str) [movie|track|track=#] [lang=3str] [UTF16] "
|
|
"Set a copyright notice\n"
|
|
" # in 'track=#' "
|
|
"denotes the target track\n"
|
|
" 3str is the 3 "
|
|
"letter ISO-639-2 language.\n"
|
|
" Brackets [] "
|
|
"show optional parameters.\n"
|
|
" Defaults are: "
|
|
"movie level, 'eng' in utf8.\n"
|
|
"\n"
|
|
"example: AtomicParsley /path/file.mp4 -t 1 Note: the only way to see "
|
|
"all contents is with -t 1 \n"
|
|
"example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\"\n"
|
|
"example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" movie\n"
|
|
"example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track=2 "
|
|
"lang=urd\n"
|
|
"example: AtomicParsley /path/file.mp4 --ISO-copyright \"Sample\" track "
|
|
"UTF16\n"
|
|
"example: AP --ISO-copyright \"Example\" track --ISO-copyright \"Por "
|
|
"Exemplo\" track=2 lang=spa UTF16\n"
|
|
"\n"
|
|
"Note: to remove the copyright, set the string to \"\" - the track and "
|
|
"language must match the target.\n"
|
|
"example: --ISO-copyright \"\" track --ISO-copyright \"\" track=2 "
|
|
"lang=spa\n"
|
|
"\n"
|
|
"Note: (foo) denotes required arguments; [foo] denotes optional parameters "
|
|
"& may have defaults.\n";
|
|
|
|
static const char *uuidHelp_text =
|
|
"AtomicParsley help page for setting uuid user extension metadata tags.\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" Setting a user-defined 'uuid' private extention tags will appear in "
|
|
"\"moov.udta.meta\"). These will\n"
|
|
" only be read by AtomicParsley & can be set irrespective of file "
|
|
"branding. The form of uuid that AP\n"
|
|
" is a v5 uuid generated from a sha1 hash of an atom name in an "
|
|
"'AtomicParsley.sf.net' namespace.\n"
|
|
"\n"
|
|
" The uuid form is in some Sony & Compressor files, but of version 4 "
|
|
"(random/pseudo-random). An example\n"
|
|
" uuid of 'cprt' in the 'AtomicParsley.sf.net' namespace is: "
|
|
"\"4bd39a57-e2c8-5655-a4fb-7a19620ef151\".\n"
|
|
" 'cprt' in the same namespace will always create that uuid; uuid atoms "
|
|
"will only print out if the\n"
|
|
" uuid generated is the same as discovered. Sony uuids don't for example "
|
|
"show up with AP -t.\n"
|
|
"\n"
|
|
" --information , -i (str) Set an information tag on uuid atom "
|
|
"name\"\251inf\"\n"
|
|
" --url , -u (URL) Set a URL tag on uuid atom name "
|
|
"\"\302\251url\"\n"
|
|
" --tagtime , timestamp Set the Coordinated Univeral Time "
|
|
"of tagging on \"tdtg\"\n"
|
|
"\n"
|
|
" Define & set an arbitrary atom with a text data or embed a file:\n"
|
|
" --meta-uuid There are two forms: 1 for text & 1 for file "
|
|
"operations\n"
|
|
" setting text form:\n"
|
|
" --meta-uuid (atom) \"text\" (str) \"atom\" = 4 "
|
|
"character atom name of your choice\n"
|
|
" str is whatever text "
|
|
"you want to set\n"
|
|
" file embedding form:\n"
|
|
" --meta-uuid (atom) \"file\" (/path) [description=\"foo\"] "
|
|
"[mime-type=\"foo/moof\"]\n"
|
|
" \"atom\" = 4 "
|
|
"character atom name of your choice\n"
|
|
" /path = path to the "
|
|
"file that will be embedded*\n"
|
|
" description = "
|
|
"optional description of the file\n"
|
|
" default is "
|
|
"\"[none]\"\n"
|
|
" mime-type = optional "
|
|
"mime type for the file\n"
|
|
" default is "
|
|
"\"none\"\n"
|
|
" Note: no "
|
|
"auto-disocevery of mime type\n"
|
|
" if "
|
|
"you know/want it: supply it.\n"
|
|
" *Note: a file extension "
|
|
"(/path/file.ext) is required\n"
|
|
"\n"
|
|
"Note: (foo) denotes required arguments; [foo] denotes optional arguments "
|
|
"& may have defaults.\n"
|
|
"\n"
|
|
"Examples: \n"
|
|
" --tagtime timestamp --information \"[psst]I see metadata\" --url "
|
|
"http://www.bumperdumper.com\n"
|
|
" --meta-uuid tagr text \"Johnny Appleseed\" --meta-uuid \302\251sft text "
|
|
"\"OpenShiiva encoded.\"\n"
|
|
" --meta-uuid scan file /usr/pix/scans.zip\n"
|
|
" --meta-uuid 1040 file ../../2006_taxes.pdf description=\"Fooled 'The "
|
|
"Man' yet again.\"\n"
|
|
"can be removed with:\n"
|
|
" --tagtime \"\" --information \"\" --url \" \" --meta-uuid scan file "
|
|
"\n"
|
|
" --manualAtomRemove "
|
|
"\"moov.udta.meta.uuid=672c98cd-f11f-51fd-adec-b0ee7b4d215f\" \\\n"
|
|
" --manualAtomRemove "
|
|
"\"moov.udta.meta.uuid=1fed6656-d911-5385-9cb2-cb2c100f06e7\"\n"
|
|
"Remove the Sony uuid atoms with:\n"
|
|
" --manualAtomRemove "
|
|
"moov.trak[1].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n"
|
|
" --manualAtomRemove "
|
|
"moov.trak[2].uuid=55534d54-21d2-4fce-bb88-695cfac9c740 \\\n"
|
|
" --manualAtomRemove uuid=50524f46-21d2-4fce-bb88-695cfac9c740\n"
|
|
"\n"
|
|
"Viewing the contents of uuid atoms:\n"
|
|
" -t or --textdata Shows the uuid atoms (both text & file) that "
|
|
"AP sets:\n"
|
|
" Example output:\n"
|
|
" Atom uuid=ec0f...d7 (AP uuid for \"scan\") contains: FILE.zip; "
|
|
"description=[none]\n"
|
|
" Atom uuid=672c...5f (AP uuid for \"tagr\") contains: Johnny "
|
|
"Appleseed\n"
|
|
"\n"
|
|
"Extracting an embedded file in a uuid atom:\n"
|
|
" --extract1uuid (atom) Extract file embedded within "
|
|
"uuid=atom into same folder\n"
|
|
" (file will be named with suffix "
|
|
"shown in --textdata)\n"
|
|
" --extract-uuids [/path] Extract all files in uuid atoms "
|
|
"under the moov.udta.meta\n"
|
|
" hierarchy. If no /path is given, "
|
|
"files will be extracted\n"
|
|
" to the same folder as the "
|
|
"originating file.\n"
|
|
"\n"
|
|
" Examples:\n"
|
|
" --extract1uuid scan\n"
|
|
" ... Extracted uuid=scan attachment to file: "
|
|
"/some/path/FILE_scan_uuid.zip\n"
|
|
" --extract-uuids ~/Desktop/plops\n"
|
|
" ... Extracted uuid=pass attachment to file: "
|
|
"/Users/me/Desktop/plops_pass_uuid.pdf\n"
|
|
" ... Extracted uuid=site attachment to file: "
|
|
"/Users/me/Desktop/plops_site_uuid.html\n"
|
|
"\n"
|
|
"--------------------------------------------------------------------------"
|
|
"----------------------\n";
|
|
|
|
static const char *rDNSHelp_text =
|
|
"AtomicParsley help page for setting reverse domain '----' metadata "
|
|
"atoms.\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" Please note that the reverse DNS format supported here is not "
|
|
"feature complete.\n"
|
|
"\n"
|
|
" Another style of metadata that iTunes uses is called the reverse DNS "
|
|
"format. For all known tags,\n"
|
|
" iTunes offers no user-accessible exposure to these tags or their "
|
|
"contents. This reverse DNS form has\n"
|
|
" a differnt form than other iTunes tags that have a atom name that "
|
|
"describes the content of 'data'\n"
|
|
" atom it contains. In the reverseDNS format, the parent to the structure "
|
|
"called the '----' atom, with\n"
|
|
" children atoms that describe & contain the metadata carried. The 'mean' "
|
|
"child contains the reverse\n"
|
|
" domain itself ('com.apple.iTunes') & the 'name' child contains the "
|
|
"descriptor ('iTunNORM'). A 'data'\n"
|
|
" atom follows that actually contains the contents of the tag.\n"
|
|
"\n"
|
|
" --contentRating (rating) Set the US TV/motion picture media "
|
|
"content rating\n"
|
|
" for available ratings use "
|
|
"\"AtomicParsley --ratings-list\n"
|
|
|
|
" --rDNSatom (str) name=(name_str) domain=(reverse_domain) "
|
|
"Manually set a reverseDNS atom.\n"
|
|
"\n"
|
|
" To set the form manually, 3 things are required: a domain, a name, and "
|
|
"the desired text.\n"
|
|
" Note: multiple 'data' atoms are supported, but not in the "
|
|
"com.apple.iTunes domain\n"
|
|
" Examples:\n"
|
|
" --contentRating \"NC-17\" --contentRating \"TV-Y7\"\n"
|
|
" --rDNSatom \"mpaa|PG-13|300|\" name=iTunEXTC domain=com.apple.iTunes\n"
|
|
" --contentRating \"\"\n"
|
|
" --rDNSatom \"\" name=iTunEXTC domain=com.apple.iTunes\n"
|
|
" --rDNSatom \"try1\" name=EVAL domain=org.fsf --rDNSatom \"try 2\" "
|
|
"name=EVAL domain=org.fsf\n"
|
|
" --rDNSatom \"\" name=EVAL domain=org.fsf\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n";
|
|
|
|
static const char *ID3Help_text =
|
|
"AtomicParsley help page for ID32 atoms with ID3 tags.\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" ** Please note: ID3 tag support is not feature complete & is in an "
|
|
"alpha state. **\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" ID3 tags are the tagging scheme used by mp3 files (where they are found "
|
|
"typically at the start of the\n"
|
|
" file). In mpeg-4 files, ID3 version 2 tags are located in specific "
|
|
"hierarchies at certain levels, at\n"
|
|
" file/movie/track level. The way that ID3 tags are carried on mpeg-4 "
|
|
"files (carried by 'ID32' atoms)\n"
|
|
" was added in early 2006, but the ID3 tagging 'informal standard' was "
|
|
"last updated (to v2.4) in 2000.\n"
|
|
" With few exceptions, ID3 tags in mpeg-4 files exist identically to their "
|
|
"mp3 counterparts.\n"
|
|
"\n"
|
|
" The ID3 parlance, a frame contains an piece of metadata. A frame (like "
|
|
"COMM for comment, or TIT1 for\n"
|
|
" title) contains the information, while the tag contains all the frames "
|
|
"collectively. The 'informal\n"
|
|
" standard' for ID3 allows multiple langauges for frames like COMM "
|
|
"(comment) & USLT (lyrics). In mpeg-4\n"
|
|
" this language setting is removed from the ID3 domain and exists in the "
|
|
"mpeg-4 domain. That means that\n"
|
|
" when an english and a spanish comment are set, 2 separate ID32 atoms are "
|
|
"created, each with a tag & 1\n"
|
|
" frame as in this example:\n"
|
|
" --ID3Tag COMM \"Primary\" --desc=AAA --ID3Tag COMM \"El Segundo\" "
|
|
"UTF16LE lang=spa --desc=AAA\n"
|
|
" See available frames with \"AtomicParsley --ID3frames-list\"\n"
|
|
" See avilable imagetypes with \"AtomicParsley --imagetype-list\"\n"
|
|
"\n"
|
|
" AtomicParsley writes ID3 version 2.4.0 tags *only*. There is no "
|
|
"up-converting from older versions.\n"
|
|
" Defaults are:\n"
|
|
" default to movie level (moov.meta.ID32); other options are [ \"root\", "
|
|
"\"track=(num)\" ] (see WARNING)\n"
|
|
" UTF-8 text encoding when optional; other options are [ \"LATIN1\", "
|
|
"\"UTF16BE\", \"UTF16LE\" ]\n"
|
|
" frames that require descriptions have a default of \"\"\n"
|
|
" for frames requiring a language setting, the ID32 language is used "
|
|
"(currently defaulting to 'eng')\n"
|
|
" frames that require descriptions have a default of \"\"\n"
|
|
" image type defaults to 0x00 or Other; for image type 0x01, 32x32 png "
|
|
"is enforced (switching to 0x02)\n"
|
|
" setting the image mimetype is generally not required as the file is "
|
|
"tested, but can be overridden\n"
|
|
" zlib compression off\n"
|
|
"\n"
|
|
" WARNING:\n"
|
|
" Quicktime Player (up to v7.1.3 at least) will freeze opeing a file "
|
|
"with ID32 tags at movie level.\n"
|
|
" Specifically, the parent atom, 'meta' is the source of the issue. "
|
|
"You can set the tags at file or\n"
|
|
" track level which avoids the problem, but the default is movie "
|
|
"level. iTunes is unaffected.\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" Current limitations:\n"
|
|
" - syncsafe integers are used as indicated by the id3 \"informal "
|
|
"standard\". usage/reading of\n"
|
|
" nonstandard ordinary unsigned integers (uint32_t) is not/will not be "
|
|
"implemented.\n"
|
|
" - externally referenced images (using mimetype '-->') are prohibited "
|
|
"by the ID32 specification.\n"
|
|
" - the ID32 atom is only supported in a non-referenced context\n"
|
|
" - probably a raft of other limitations that my brain lost along the "
|
|
"way...\n"
|
|
"--------------------------------------------------------------------------"
|
|
"--------------------------\n"
|
|
" Usage:\n"
|
|
" --ID3Tag (frameID or alias) (str) [desc=(str)] [mimetype=(str)] "
|
|
"[imagetype=(str or hex)] [...]\n"
|
|
"\n"
|
|
" ... represents other arguments:\n"
|
|
" [compressed] zlib compress the frame\n"
|
|
" [UTF16BE, UTF16LE, LATIN1] alternative text encodings for frames that "
|
|
"support different encodings\n"
|
|
"\n"
|
|
"Note: (foo) denotes required arguments; [foo] denotes optional "
|
|
"parameters\n"
|
|
"\n"
|
|
" Examples:\n"
|
|
" --ID3Tag APIC /path/to/img.ext\n"
|
|
" --ID3Tag APIC /path/to/img.ext desc=\"something to say\" imagetype=0x08 "
|
|
"UTF16LE compressed\n"
|
|
" --ID3Tag composer \"I, Claudius\" --ID3Tag TPUB \"Seneca the Roman\" "
|
|
"--ID3Tag TMOO Imperial\n"
|
|
" --ID3Tag UFID look@me.org uniqueID=randomUUIDstamp\n"
|
|
"\n"
|
|
" Extracting embedded images in APIC frames:\n"
|
|
" --ID3Tag APIC extract\n"
|
|
" images are extracted into the same directory as the source mpeg-4 "
|
|
"file\n"
|
|
"\n"
|
|
#if defined(__APPLE__)
|
|
" Setting MCDI (Music CD Identifier):\n"
|
|
" --ID3Tag MCDI disk4\n"
|
|
" Information to create this frame is taken directly off an Audio "
|
|
"CD's TOC. If the target\n"
|
|
" disk is not found or is not an audio CD, a scan of all devices "
|
|
"will occur. If an Audio CD\n"
|
|
" is present, the scan should yield what should be entered after "
|
|
"'MCDI':\n"
|
|
" % AP file --ID3Tag MCDI disk3\n"
|
|
" % No cd present in drive at disk3\n"
|
|
" % Device 'disk4' contains cd media\n"
|
|
" % Good news, device 'disk4' is an Audio CD and can be used for "
|
|
"'MCDI' setting\n"
|
|
#elif defined(__linux__)
|
|
" Setting MCDI (Music CD Identifier):\n"
|
|
" --ID3Tag MCDI /dev/hdc\n"
|
|
" Information to create this frame is taken directly off an Audio "
|
|
"CD's TOC. An Audio CD\n"
|
|
" must be mounted & present.\n"
|
|
#elif defined(_WIN32)
|
|
" Setting MCDI (Music CD Identifier):\n"
|
|
" --ID3Tag MCDI D\n"
|
|
" Information to create this frame is taken directly off an Audio "
|
|
"CD's TOC. The letter after\n"
|
|
" \"MCDI\" is the letter of the drive where the CD is present.\n"
|
|
#endif
|
|
;
|
|
|
|
void ExtractPaddingPrefs(char *env_padding_prefs) {
|
|
pad_prefs.default_padding_size = DEFAULT_PADDING_LENGTH;
|
|
pad_prefs.minimum_required_padding_size = MINIMUM_REQUIRED_PADDING_LENGTH;
|
|
pad_prefs.maximum_present_padding_size = MAXIMUM_REQUIRED_PADDING_LENGTH;
|
|
|
|
if (env_padding_prefs != NULL) {
|
|
if (env_padding_prefs[0] == 0x22 || env_padding_prefs[0] == 0x27)
|
|
env_padding_prefs++;
|
|
}
|
|
char *env_pad_prefs_ptr = env_padding_prefs;
|
|
|
|
while (env_pad_prefs_ptr != NULL) {
|
|
env_pad_prefs_ptr = strsep(&env_padding_prefs, ":");
|
|
|
|
if (env_pad_prefs_ptr == NULL)
|
|
break;
|
|
|
|
if (strncmp(env_pad_prefs_ptr, "DEFAULT_PAD=", 12) == 0) {
|
|
strsep(&env_pad_prefs_ptr, "=");
|
|
sscanf(env_pad_prefs_ptr, "%" SCNu32, &pad_prefs.default_padding_size);
|
|
}
|
|
if (strncmp(env_pad_prefs_ptr, "MIN_PAD=", 8) == 0) {
|
|
strsep(&env_pad_prefs_ptr, "=");
|
|
sscanf(env_pad_prefs_ptr,
|
|
"%" SCNu32,
|
|
&pad_prefs.minimum_required_padding_size);
|
|
}
|
|
if (strncmp(env_pad_prefs_ptr, "MAX_PAD=", 8) == 0) {
|
|
strsep(&env_pad_prefs_ptr, "=");
|
|
sscanf(env_pad_prefs_ptr,
|
|
"%" SCNu32,
|
|
&pad_prefs.maximum_present_padding_size);
|
|
}
|
|
}
|
|
// fprintf(stdout, "Def %" PRIu32 "; Min %" PRIu32 "; Max %" PRIu32 "\n",
|
|
// pad_prefs.default_padding_size, pad_prefs.minimum_required_padding_size,
|
|
// pad_prefs.maximum_present_padding_size);
|
|
return;
|
|
}
|
|
|
|
void GetBasePath(const char *filepath, char *&basepath) {
|
|
// with a myriad of m4a, m4p, mp4, whatever else comes up... it might just be
|
|
// easiest to strip off the end.
|
|
int split_here = 0;
|
|
for (int i = strlen(filepath); i >= 0; i--) {
|
|
const char *this_char = &filepath[i];
|
|
if (*this_char == '.') {
|
|
split_here = i;
|
|
break;
|
|
}
|
|
}
|
|
memcpy(basepath, filepath, (size_t)split_here);
|
|
|
|
return;
|
|
}
|
|
|
|
void find_optional_args(char *argv[],
|
|
int start_optindargs,
|
|
uint16_t &packed_lang,
|
|
bool &asUTF16,
|
|
uint8_t &udta_container,
|
|
uint8_t &trk_idx,
|
|
int max_optargs) {
|
|
asUTF16 = false;
|
|
packed_lang = 5575; // und = 0x55C4 = 21956, but QTPlayer doesn't like und
|
|
// //eng = 0x15C7 = 5575
|
|
|
|
for (int i = 0; i <= max_optargs - 1; i++) {
|
|
if (argv[start_optindargs + i] && start_optindargs + i <= total_args) {
|
|
if (strncmp(argv[start_optindargs + i], "lang=", 5) == 0) {
|
|
if (!MatchLanguageCode(argv[start_optindargs + i] + 5)) {
|
|
packed_lang = PackLanguage("und", 0);
|
|
} else {
|
|
packed_lang = PackLanguage(argv[start_optindargs + i], 5);
|
|
}
|
|
|
|
} else if (strcmp(argv[start_optindargs + i], "UTF16") == 0) {
|
|
asUTF16 = true;
|
|
} else if (strcmp(argv[optind + i], "movie") == 0) {
|
|
udta_container = MOVIE_LEVEL_ATOM;
|
|
} else if (strncmp(argv[optind + i], "track=", 6) == 0) {
|
|
char *track_index_str = argv[optind + i];
|
|
strsep(&track_index_str, "=");
|
|
trk_idx = strtoul(track_index_str, NULL, 10);
|
|
udta_container = SINGLE_TRACK_ATOM;
|
|
} else if (strcmp(argv[optind + i], "track") == 0) {
|
|
udta_container = ALL_TRACKS_ATOM;
|
|
}
|
|
if (*argv[start_optindargs + i] == '-') {
|
|
break; // we've hit another cli argument
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void scan_ID3_optargs(char *argv[],
|
|
int start_optargs,
|
|
const char *&target_lang,
|
|
uint16_t &packed_lang,
|
|
uint8_t &char_encoding,
|
|
char *meta_container,
|
|
bool &multistring) {
|
|
packed_lang = 5575; // default ID32 lang is 'eng'
|
|
uint16_t i = 0;
|
|
|
|
while (argv[start_optargs + i] != NULL) {
|
|
if (argv[start_optargs + i] && start_optargs + i <= total_args) {
|
|
|
|
if (strncmp(argv[start_optargs + i], "lang=", 5) == 0) {
|
|
if (!MatchLanguageCode(argv[start_optargs + i] + 5)) {
|
|
packed_lang = PackLanguage("und", 0);
|
|
target_lang = "und";
|
|
} else {
|
|
packed_lang = PackLanguage(argv[start_optargs + i], 5);
|
|
target_lang = argv[start_optargs + i] + 5;
|
|
}
|
|
|
|
} else if (strcmp(argv[start_optargs + i], "UTF16LE") == 0) {
|
|
char_encoding = TE_UTF16LE_WITH_BOM;
|
|
} else if (strcmp(argv[start_optargs + i], "UTF16BE") == 0) {
|
|
char_encoding = TE_UTF16BE_NO_BOM;
|
|
} else if (strcmp(argv[start_optargs + i], "LATIN1") == 0) {
|
|
char_encoding = TE_LATIN1;
|
|
|
|
} else if (strcmp(argv[optind + i], "root") == 0) {
|
|
*meta_container = 0 - FILE_LEVEL_ATOM;
|
|
} else if (strncmp(argv[optind + i], "track=", 6) == 0) {
|
|
char *track_index_str = argv[optind + i];
|
|
strsep(&track_index_str, "=");
|
|
*meta_container = strtoul(track_index_str, NULL, 10);
|
|
}
|
|
}
|
|
|
|
if (*argv[start_optargs + i] == '-') {
|
|
break; // we've hit another cli argument or deleting some frame
|
|
}
|
|
i++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
const char *
|
|
find_ID3_optarg(char *argv[], int start_optargs, const char *arg_string) {
|
|
const char *ret_val = "";
|
|
uint16_t i = 0;
|
|
uint8_t arg_prefix_len = strlen(arg_string);
|
|
|
|
while (argv[start_optargs + i] != NULL) {
|
|
if (argv[start_optargs + i] && start_optargs + i <= total_args) {
|
|
if (strcmp(arg_string, "compressed") == 0 &&
|
|
strcmp(argv[start_optargs + i], "compressed") == 0) {
|
|
return "1";
|
|
}
|
|
// if (strcmp(arg_string, "text++") == 0 && strcmp(argv[start_optargs +
|
|
// i], "text++") == 0) {
|
|
// return "1";
|
|
//}
|
|
if (strncmp(argv[start_optargs + i], arg_string, arg_prefix_len) == 0) {
|
|
ret_val = argv[start_optargs + i] + arg_prefix_len;
|
|
break;
|
|
}
|
|
}
|
|
if (*argv[start_optargs + i] == '-') {
|
|
break; // we've hit another cli argument or deleting some frame
|
|
}
|
|
i++;
|
|
}
|
|
return ret_val;
|
|
}
|
|
|
|
//***********************************************
|
|
|
|
static void show_short_help(void) {
|
|
printf("%s\n", shortHelp_text);
|
|
ShowVersionInfo();
|
|
printf("\nSubmit bug fixes to https://github.com/wez/atomicparsley\n");
|
|
}
|
|
|
|
int real_main(int argc, char *argv[]) {
|
|
if (argc == 1) {
|
|
show_short_help();
|
|
exit(0);
|
|
} else if (argc == 2 && ((strcmp(argv[1], "-v") == 0) ||
|
|
(strcmp(argv[1], "-version") == 0) ||
|
|
(strcmp(argv[1], "--version") == 0))) {
|
|
ShowVersionInfo();
|
|
exit(0);
|
|
} else if (argc == 2) {
|
|
if ((strcmp(argv[1], "-help") == 0) || (strcmp(argv[1], "--help") == 0) ||
|
|
(strcmp(argv[1], "-h") == 0)) {
|
|
show_short_help();
|
|
exit(0);
|
|
} else if ((strcmp(argv[1], "--longhelp") == 0) ||
|
|
(strcmp(argv[1], "-longhelp") == 0) ||
|
|
(strcmp(argv[1], "-Lh") == 0)) {
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
if (UnicodeOutputStatus == WIN32_UTF16) { // convert the helptext to utf16
|
|
// to preserve \251 characters
|
|
int help_len = strlen(longHelp_text) + 1;
|
|
wchar_t *Lhelp_text = (wchar_t *)malloc(sizeof(wchar_t) * help_len);
|
|
wmemset(Lhelp_text, 0, help_len);
|
|
UTF8ToUTF16LE((unsigned char *)Lhelp_text,
|
|
2 * help_len,
|
|
(unsigned char *)longHelp_text,
|
|
help_len);
|
|
APar_unicode_win32Printout(Lhelp_text, (char *)longHelp_text);
|
|
free(Lhelp_text);
|
|
} else
|
|
#endif
|
|
{
|
|
fprintf(stdout, "%s", longHelp_text);
|
|
}
|
|
exit(0);
|
|
|
|
} else if ((strcmp(argv[1], "--3gp-help") == 0) ||
|
|
(strcmp(argv[1], "-3gp-help") == 0) ||
|
|
(strcmp(argv[1], "--3gp-h") == 0)) {
|
|
fprintf(stdout, "%s\n", _3gpHelp_text);
|
|
exit(0);
|
|
|
|
} else if ((strcmp(argv[1], "--ISO-help") == 0) ||
|
|
(strcmp(argv[1], "--iso-help") == 0) ||
|
|
(strcmp(argv[1], "-Ih") == 0)) {
|
|
fprintf(stdout, "%s\n", ISOHelp_text);
|
|
exit(0);
|
|
|
|
} else if ((strcmp(argv[1], "--file-help") == 0) ||
|
|
(strcmp(argv[1], "-file-help") == 0) ||
|
|
(strcmp(argv[1], "-fh") == 0)) {
|
|
fprintf(stdout, "%s\n", fileLevelHelp_text);
|
|
exit(0);
|
|
|
|
} else if ((strcmp(argv[1], "--uuid-help") == 0) ||
|
|
(strcmp(argv[1], "-uuid-help") == 0) ||
|
|
(strcmp(argv[1], "-uh") == 0)) {
|
|
fprintf(stdout, "%s\n", uuidHelp_text);
|
|
exit(0);
|
|
|
|
} else if ((strcmp(argv[1], "--reverseDNS-help") == 0) ||
|
|
(strcmp(argv[1], "-rDNS-help") == 0) ||
|
|
(strcmp(argv[1], "-rh") == 0)) {
|
|
fprintf(stdout, "%s\n", rDNSHelp_text);
|
|
exit(0);
|
|
|
|
} else if ((strcmp(argv[1], "--ID3-help") == 0) ||
|
|
(strcmp(argv[1], "-ID3-help") == 0) ||
|
|
(strcmp(argv[1], "-ID3h") == 0)) {
|
|
fprintf(stdout, "%s\n", ID3Help_text);
|
|
exit(0);
|
|
|
|
} else if (strcmp(argv[1], "--genre-list") == 0) {
|
|
ListGenresValues();
|
|
exit(0);
|
|
|
|
} else if (strcmp(argv[1], "--stik-list") == 0) {
|
|
ListStikValues();
|
|
exit(0);
|
|
|
|
} else if (strcmp(argv[1], "--language-list") == 0 ||
|
|
strcmp(argv[1], "--languages-list") == 0 ||
|
|
strcmp(argv[1], "--list-language") == 0 ||
|
|
strcmp(argv[1], "--list-languages") == 0 ||
|
|
strcmp(argv[1], "-ll") == 0) {
|
|
ListLanguageCodes();
|
|
exit(0);
|
|
|
|
} else if (strcmp(argv[1], "--ratings-list") == 0) {
|
|
ListMediaRatings();
|
|
exit(0);
|
|
|
|
} else if (strcmp(argv[1], "--genre-movie-id-list") == 0) {
|
|
ListMovieGenreIDValues();
|
|
exit(0);
|
|
|
|
} else if (strcmp(argv[1], "--genre-tv-id-list") == 0) {
|
|
ListTVGenreIDValues();
|
|
exit(0);
|
|
|
|
} else if (strcmp(argv[1], "--ID3frames-list") == 0) {
|
|
ListID3FrameIDstrings();
|
|
exit(0);
|
|
|
|
} else if (strcmp(argv[1], "--imagetype-list") == 0) {
|
|
List_imagtype_strings();
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
if (argc == 3 &&
|
|
(strcmp(argv[2], "--brands") == 0 || strcmp(argv[2], "-brands") == 0)) {
|
|
APar_ExtractBrands(argv[1]);
|
|
exit(0);
|
|
}
|
|
|
|
int extr = 99;
|
|
total_args = argc;
|
|
char *ISObasemediafile = argv[1];
|
|
|
|
TestFileExistence(ISObasemediafile, true);
|
|
|
|
char *padding_options = getenv("AP_PADDING");
|
|
ExtractPaddingPrefs(padding_options);
|
|
|
|
// it would probably be better to test output_file if provided & if
|
|
// --overWrite was provided.... probably only of use on Windows - and I'm not
|
|
// on it.
|
|
if (strlen(ISObasemediafile) + 11 > MAXPATHLEN) {
|
|
fprintf(stderr,
|
|
"%c %s",
|
|
'\a',
|
|
"AtomicParsley error: filename/filepath was too long.\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (argc > 3 && strcmp(argv[2], "--DeepScan") == 0) {
|
|
deep_atom_scan = true;
|
|
APar_ScanAtoms(ISObasemediafile, true);
|
|
}
|
|
|
|
while (1) {
|
|
static struct option long_options[] = {
|
|
{"help", 0, NULL, OPT_HELP},
|
|
{"test", optional_argument, NULL, OPT_TEST},
|
|
{"textdata", optional_argument, NULL, OPT_ShowTextData},
|
|
{"extractPix", 0, NULL, OPT_ExtractPix},
|
|
{"extractPixToPath", required_argument, NULL, OPT_ExtractPixToPath},
|
|
{"artist", required_argument, NULL, Meta_artist},
|
|
{"artDirector", required_argument, NULL, Meta_artDirector},
|
|
{"arranger", required_argument, NULL, Meta_arranger},
|
|
{"author", required_argument, NULL, Meta_author},
|
|
{"conductor", required_argument, NULL, Meta_conductor},
|
|
{"director", required_argument, NULL, Meta_director},
|
|
{"originalArtist", required_argument, NULL, Meta_originalArtist},
|
|
{"producer", required_argument, NULL, Meta_producer},
|
|
// { "performer", required_argument, NULL, Meta_performer
|
|
// },
|
|
{"soundEngineer", required_argument, NULL, Meta_soundEngineer},
|
|
{"soloist", required_argument, NULL, Meta_soloist},
|
|
{"executiveProducer", required_argument, NULL, Meta_executiveProducer},
|
|
{"title", required_argument, NULL, Meta_songtitle},
|
|
{"subtitle", required_argument, NULL, Meta_subtitle},
|
|
{"album", required_argument, NULL, Meta_album},
|
|
{"genre", required_argument, NULL, Meta_genre},
|
|
{"tracknum", required_argument, NULL, Meta_tracknum},
|
|
{"disknum", required_argument, NULL, Meta_disknum},
|
|
{"comment", required_argument, NULL, Meta_comment},
|
|
{"year", required_argument, NULL, Meta_year},
|
|
{"lyrics", required_argument, NULL, Meta_lyrics},
|
|
{"lyricsFile", required_argument, NULL, Meta_lyrics_file},
|
|
{"composer", required_argument, NULL, Meta_composer},
|
|
{"copyright", required_argument, NULL, Meta_copyright},
|
|
{"grouping", required_argument, NULL, Meta_grouping},
|
|
{"albumArtist", required_argument, NULL, Meta_album_artist},
|
|
{"compilation", required_argument, NULL, Meta_compilation},
|
|
{"hdvideo", required_argument, NULL, Meta_hdvideo},
|
|
{"advisory", required_argument, NULL, Meta_advisory},
|
|
{"bpm", required_argument, NULL, Meta_BPM},
|
|
{"artwork", required_argument, NULL, Meta_artwork},
|
|
{"stik", required_argument, NULL, Meta_stik},
|
|
{"description", required_argument, NULL, Meta_description},
|
|
{"longdesc", required_argument, NULL, Meta_longdescription},
|
|
{"storedesc", required_argument, NULL, Meta_storedescription},
|
|
{"Rating", required_argument, NULL, Meta_Rating},
|
|
{"TVNetwork", required_argument, NULL, Meta_TV_Network},
|
|
{"TVShowName", required_argument, NULL, Meta_TV_ShowName},
|
|
{"TVEpisode", required_argument, NULL, Meta_TV_Episode},
|
|
{"TVEpisodeNum", required_argument, NULL, Meta_TV_EpisodeNumber},
|
|
{"TVSeasonNum", required_argument, NULL, Meta_TV_SeasonNumber},
|
|
{"podcastFlag", required_argument, NULL, Meta_podcastFlag},
|
|
{"keyword", required_argument, NULL, Meta_keyword},
|
|
{"category", required_argument, NULL, Meta_category},
|
|
{"podcastURL", required_argument, NULL, Meta_podcast_URL},
|
|
{"podcastGUID", required_argument, NULL, Meta_podcast_GUID},
|
|
{"purchaseDate", required_argument, NULL, Meta_PurchaseDate},
|
|
{"encodingTool", required_argument, NULL, Meta_EncodingTool},
|
|
{"encodedBy", required_argument, NULL, Meta_EncodedBy},
|
|
{"apID", required_argument, NULL, Meta_apID},
|
|
{"cnID", required_argument, NULL, Meta_cnID},
|
|
{"geID", required_argument, NULL, Meta_geID},
|
|
{"xID", required_argument, NULL, Meta_xID},
|
|
{"gapless", required_argument, NULL, Meta_PlayGapless},
|
|
{"sortOrder", required_argument, NULL, Meta_SortOrder},
|
|
|
|
{"rDNSatom", required_argument, NULL, Meta_ReverseDNS_Form},
|
|
{"contentRating", required_argument, NULL, Meta_rDNS_rating},
|
|
|
|
{"tagtime", optional_argument, NULL, Meta_StandardDate},
|
|
{"information", required_argument, NULL, Meta_Information},
|
|
{"url", required_argument, NULL, Meta_URL},
|
|
{"meta-uuid", required_argument, NULL, Meta_uuid},
|
|
{"extract-uuids", optional_argument, NULL, Opt_Extract_all_uuids},
|
|
{"extract1uuid", required_argument, NULL, Opt_Extract_a_uuid},
|
|
{"iPod-uuid", required_argument, NULL, Opt_Ipod_AVC_uuid},
|
|
|
|
{"freefree", optional_argument, NULL, Opt_FreeFree},
|
|
{"metaEnema", 0, NULL, Metadata_Purge},
|
|
{"manualAtomRemove", required_argument, NULL, Manual_atom_removal},
|
|
{"udtaEnema", 0, NULL, UserData_Purge},
|
|
{"foobar2000Enema", 0, NULL, foobar_purge},
|
|
{"metaDump", 0, NULL, Meta_dump},
|
|
{"output", required_argument, NULL, OPT_OutputFile},
|
|
{"preventOptimizing", 0, NULL, OPT_NoOptimize},
|
|
{"overWrite", 0, NULL, OPT_OverWrite},
|
|
#if defined(_WIN32)
|
|
{"preserveTime", 0, NULL, OPT_PreserveTimeStamps},
|
|
#endif
|
|
{"ISO-copyright", required_argument, NULL, ISO_Copyright},
|
|
|
|
{"3gp-title", required_argument, NULL, _3GP_Title},
|
|
{"3gp-author", required_argument, NULL, _3GP_Author},
|
|
{"3gp-performer", required_argument, NULL, _3GP_Performer},
|
|
{"3gp-genre", required_argument, NULL, _3GP_Genre},
|
|
{"3gp-description", required_argument, NULL, _3GP_Description},
|
|
{"3gp-copyright", required_argument, NULL, _3GP_Copyright},
|
|
{"3gp-album", required_argument, NULL, _3GP_Album},
|
|
{"3gp-year", required_argument, NULL, _3GP_Year},
|
|
|
|
{"3gp-rating", required_argument, NULL, _3GP_Rating},
|
|
{"3gp-classification", required_argument, NULL, _3GP_Classification},
|
|
{"3gp-keyword", required_argument, NULL, _3GP_Keyword},
|
|
{"3gp-location", required_argument, NULL, _3GP_Location},
|
|
|
|
{"ID3Tag", required_argument, NULL, Meta_ID3v2Tag},
|
|
|
|
{"DeepScan", 0, &extr, 1},
|
|
{"movementCount", required_argument, NULL, Meta_movementCount},
|
|
{"movementName", required_argument, NULL, Meta_movementName},
|
|
{"movementNumber", required_argument, NULL, Meta_movementNumber},
|
|
{"showWorkMovement", required_argument, NULL, Meta_showWorkMovement},
|
|
{"work", required_argument, NULL, Meta_work},
|
|
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
int c = -1;
|
|
int option_index = 0;
|
|
|
|
c = getopt_long(argc,
|
|
argv,
|
|
"hTtEe:a:b:c:d:f:g:i:k:l:n:o:p:q::u:w:x:y:z:A:B:C:D:F:G:H:"
|
|
"I:J:K:L:MN:QR:S:U:WXV:ZP",
|
|
long_options,
|
|
&option_index);
|
|
|
|
if (c == -1) {
|
|
if (argc < 3 && argc > 2) {
|
|
APar_ScanAtoms(ISObasemediafile, true);
|
|
APar_PrintAtomicTree();
|
|
}
|
|
break;
|
|
}
|
|
|
|
signal(SIGTERM, kill_signal);
|
|
signal(SIGINT, kill_signal);
|
|
|
|
switch (c) {
|
|
// "optind" represents the count of arguments up to and including its
|
|
// optional flag:
|
|
|
|
case '?':
|
|
return 1;
|
|
|
|
case OPT_HELP: {
|
|
fprintf(stdout, "%s", longHelp_text);
|
|
return 0;
|
|
}
|
|
|
|
case OPT_TEST: {
|
|
deep_atom_scan = true;
|
|
APar_ScanAtoms(ISObasemediafile, true);
|
|
APar_PrintAtomicTree();
|
|
if (argv[optind]) {
|
|
if (strcmp(argv[optind], "+dates") == 0) {
|
|
APar_ExtractDetails(APar_OpenISOBaseMediaFile(ISObasemediafile, true),
|
|
SHOW_TRACK_INFO + SHOW_DATE_INFO);
|
|
} else {
|
|
APar_ExtractDetails(APar_OpenISOBaseMediaFile(ISObasemediafile, true),
|
|
SHOW_TRACK_INFO);
|
|
}
|
|
}
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
break;
|
|
}
|
|
|
|
case OPT_ShowTextData: {
|
|
if (argv[optind]) { // for utilities that write iTunes-style metadata into
|
|
// 3gp branded files
|
|
APar_ExtractBrands(ISObasemediafile);
|
|
deep_atom_scan = true;
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, true);
|
|
|
|
if (strcmp(argv[optind], "+") == 0) {
|
|
APar_Print_iTunesData(ISObasemediafile,
|
|
NULL,
|
|
PRINT_FREE_SPACE + PRINT_PADDING_SPACE +
|
|
PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE,
|
|
PRINT_DATA);
|
|
} else {
|
|
fprintf(stdout, "---------------------------\n");
|
|
APar_Print_ISO_UserData_per_track();
|
|
|
|
AtomicInfo *iTuneslistAtom =
|
|
APar_FindAtom("moov.udta.meta.ilst", false, SIMPLE_ATOM, 0);
|
|
if (iTuneslistAtom != NULL) {
|
|
fprintf(
|
|
stdout,
|
|
"---------------------------\n iTunes-style metadata tags:\n");
|
|
APar_Print_iTunesData(ISObasemediafile,
|
|
NULL,
|
|
PRINT_FREE_SPACE + PRINT_PADDING_SPACE +
|
|
PRINT_USER_DATA_SPACE + PRINT_MEDIA_SPACE,
|
|
PRINT_DATA,
|
|
iTuneslistAtom);
|
|
}
|
|
fprintf(stdout, "---------------------------\n");
|
|
}
|
|
|
|
} else {
|
|
deep_atom_scan = true;
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, true);
|
|
|
|
if (metadata_style >= THIRD_GEN_PARTNER) {
|
|
APar_PrintUserDataAssests();
|
|
} else if (metadata_style == ITUNES_STYLE) {
|
|
APar_Print_iTunesData(ISObasemediafile,
|
|
NULL,
|
|
0,
|
|
PRINT_DATA); // false, don't try to extractPix
|
|
APar_Print_APuuid_atoms(ISObasemediafile, NULL, PRINT_DATA);
|
|
}
|
|
}
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
break;
|
|
}
|
|
|
|
case OPT_ExtractPix: {
|
|
char *base_path = (char *)malloc(sizeof(char) * MAXPATHLEN + 1);
|
|
memset(base_path, 0, MAXPATHLEN + 1);
|
|
|
|
GetBasePath(ISObasemediafile, base_path);
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, true);
|
|
APar_Print_iTunesData(
|
|
ISObasemediafile,
|
|
base_path,
|
|
0,
|
|
EXTRACT_ARTWORK); // exportPix to stripped ISObasemediafile path
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
|
|
free(base_path);
|
|
base_path = NULL;
|
|
break;
|
|
}
|
|
|
|
case OPT_ExtractPixToPath: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, true);
|
|
APar_Print_iTunesData(ISObasemediafile,
|
|
optarg,
|
|
0,
|
|
EXTRACT_ARTWORK); // exportPix to a different path
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
break;
|
|
}
|
|
|
|
case Meta_artist: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "artist")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251ART.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_artDirector: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "artDirector")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251ard.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_arranger: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "arranger")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251arg.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_author: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "author")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251aut.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_conductor: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "conductor")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251con.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_director: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "director")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251dir.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_originalArtist: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "originalArtist")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251ope.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_producer: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "producer")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251prd.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
case Meta_performer : {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if ( !APar_assert(metadata_style == ITUNES_STYLE, 1,
|
|
"performer") ) { char major_brand[4]; UInt32_TO_String4(brand,
|
|
&*major_brand); APar_assert(false, 4, &*major_brand); break;
|
|
}
|
|
AtomicInfo* artistData_atom =
|
|
APar_MetaData_atom_Init("moov.udta.meta.ilst.\251prf.data", optarg,
|
|
AtomFlags_Data_Text); APar_Unified_atom_Put(artistData_atom, optarg,
|
|
UTF8_iTunesStyle_256glyphLimited, 0, 0); break;
|
|
}
|
|
*/
|
|
|
|
case Meta_soundEngineer: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "soundEngineer")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251sne.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_soloist: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "soloist")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251sol.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_executiveProducer: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(
|
|
metadata_style == ITUNES_STYLE, 1, "executiveProducer")) {
|
|
char major_brand[4];
|
|
UInt32_TO_String4(brand, &*major_brand);
|
|
APar_assert(false, 4, &*major_brand);
|
|
break;
|
|
}
|
|
AtomicInfo *artistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251xpd.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
artistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_songtitle: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "title")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *titleData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251nam.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_subtitle: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "subtitle")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *titleData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251st3.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
titleData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_album: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "album")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *albumData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251alb.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
albumData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_genre: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "genre")) {
|
|
break;
|
|
}
|
|
|
|
APar_MetaData_atomGenre_Set(optarg);
|
|
break;
|
|
}
|
|
|
|
case Meta_tracknum: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "track number")) {
|
|
break;
|
|
}
|
|
|
|
uint16_t pos_in_total = 0;
|
|
uint16_t the_total = 0;
|
|
if (strrchr(optarg, '/') != NULL) {
|
|
|
|
char *duplicate_info = optarg;
|
|
char *item_stat = strsep(&duplicate_info, "/");
|
|
sscanf(item_stat, "%" SCNu16, &pos_in_total);
|
|
item_stat = strsep(&duplicate_info, "/");
|
|
sscanf(item_stat, "%" SCNu16, &the_total);
|
|
} else {
|
|
sscanf(optarg, "%" SCNu16, &pos_in_total);
|
|
}
|
|
|
|
AtomicInfo *tracknumData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.trkn.data", optarg, AtomFlags_Data_Binary);
|
|
// tracknum: [0, 0, 0, 0, 0, 0, 0, pos_in_total, 0, the_total, 0, 0];
|
|
// BUT that first uint32_t is already accounted for in
|
|
// APar_MetaData_atom_Init
|
|
APar_Unified_atom_Put(
|
|
tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
|
|
APar_Unified_atom_Put(tracknumData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
pos_in_total,
|
|
16);
|
|
APar_Unified_atom_Put(tracknumData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
the_total,
|
|
16);
|
|
APar_Unified_atom_Put(
|
|
tracknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
|
|
break;
|
|
}
|
|
|
|
case Meta_disknum: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "disc number")) {
|
|
break;
|
|
}
|
|
|
|
uint16_t pos_in_total = 0;
|
|
uint16_t the_total = 0;
|
|
if (strrchr(optarg, '/') != NULL) {
|
|
|
|
char *duplicate_info = optarg;
|
|
char *item_stat = strsep(&duplicate_info, "/");
|
|
sscanf(item_stat, "%" SCNu16, &pos_in_total);
|
|
item_stat = strsep(&duplicate_info, "/");
|
|
sscanf(item_stat, "%" SCNu16, &the_total);
|
|
|
|
} else {
|
|
sscanf(optarg, "%" SCNu16, &pos_in_total);
|
|
}
|
|
|
|
AtomicInfo *disknumData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.disk.data", optarg, AtomFlags_Data_Binary);
|
|
// disknum: [0, 0, 0, 0, 0, 0, 0, pos_in_total, 0, the_total]; BUT that
|
|
// first uint32_t is already accounted for in APar_MetaData_atom_Init
|
|
APar_Unified_atom_Put(
|
|
disknumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
|
|
APar_Unified_atom_Put(disknumData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
pos_in_total,
|
|
16);
|
|
APar_Unified_atom_Put(disknumData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
the_total,
|
|
16);
|
|
break;
|
|
}
|
|
|
|
case Meta_comment: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "comment")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *commentData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251cmt.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
commentData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_year: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "year")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *yearData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251day.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
yearData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_lyrics: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *lyricsData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251lyr.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
lyricsData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_lyrics_file: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "lyrics")) {
|
|
break;
|
|
}
|
|
|
|
APar_MetaData_atomLyrics_Set(optarg);
|
|
break;
|
|
}
|
|
|
|
case Meta_composer: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "composer")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *composerData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251wrt.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
composerData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_copyright: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "copyright")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *copyrightData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.cprt.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
copyrightData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_grouping: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "grouping")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *groupingData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251grp.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
groupingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_compilation: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "compilation")) {
|
|
break;
|
|
}
|
|
|
|
if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) {
|
|
APar_RemoveAtom("moov.udta.meta.ilst.cpil.data", VERSIONED_ATOM, 0);
|
|
} else {
|
|
// compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t
|
|
// is already accounted for in APar_MetaData_atom_Init
|
|
AtomicInfo *compilationData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.cpil.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(compilationData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
1,
|
|
8); // a hard coded uint8_t of: 1 is compilation
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Meta_hdvideo: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "hdvideo")) {
|
|
break;
|
|
}
|
|
|
|
if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0 ||
|
|
strcmp(optarg, "0") == 0) {
|
|
APar_RemoveAtom("moov.udta.meta.ilst.hdvd.data", VERSIONED_ATOM, 0);
|
|
} else {
|
|
// compilation: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t
|
|
// is already accounted for in APar_MetaData_atom_Init
|
|
AtomicInfo *hdvideoData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.hdvd.data", optarg, AtomFlags_Data_UInt);
|
|
|
|
uint8_t hdvideo_value = 0;
|
|
if (strcmp(optarg, "true") == 0) {
|
|
hdvideo_value = 1;
|
|
} else {
|
|
sscanf(optarg, "%" SCNu8, &hdvideo_value);
|
|
}
|
|
|
|
APar_Unified_atom_Put(hdvideoData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
hdvideo_value,
|
|
8);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Meta_BPM: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "BPM")) {
|
|
break;
|
|
}
|
|
|
|
if (strcmp(optarg, "0") == 0 || strlen(optarg) == 0) {
|
|
APar_RemoveAtom("moov.udta.meta.ilst.tmpo.data", VERSIONED_ATOM, 0);
|
|
} else {
|
|
uint16_t bpm_value = 0;
|
|
sscanf(optarg, "%" SCNu16, &bpm_value);
|
|
// bpm is [0, 0, 0, 0, 0, bpm_value]; BUT that first uint32_t is
|
|
// already accounted for in APar_MetaData_atom_Init
|
|
AtomicInfo *bpmData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.tmpo.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(bpmData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
bpm_value,
|
|
16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Meta_advisory: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "content advisory")) {
|
|
break;
|
|
}
|
|
|
|
if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) {
|
|
APar_RemoveAtom("moov.udta.meta.ilst.rtng.data", VERSIONED_ATOM, 0);
|
|
} else {
|
|
uint8_t rating_value = 0;
|
|
if (strcmp(optarg, "clean") == 0) {
|
|
rating_value = 2; // only \02 is clean
|
|
} else if (strcmp(optarg, "explicit") == 0) {
|
|
rating_value = 4; // most non \00, \02 numbers are allowed
|
|
}
|
|
// rating is [0, 0, 0, 0, rating_value]; BUT that first uint32_t is
|
|
// already accounted for in APar_MetaData_atom_Init
|
|
AtomicInfo *advisoryData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.rtng.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(advisoryData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
rating_value,
|
|
8);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Meta_artwork: { // handled differently: there can be multiple
|
|
// "moov.udta.meta.ilst.covr.data" atoms
|
|
char *env_PicOptions = getenv("PIC_OPTIONS");
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "coverart")) {
|
|
break;
|
|
}
|
|
|
|
APar_MetaData_atomArtwork_Set(optarg, env_PicOptions);
|
|
break;
|
|
}
|
|
|
|
case Meta_stik: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "'stik'")) {
|
|
break;
|
|
}
|
|
|
|
if (strcmp(optarg, "remove") == 0 || strlen(optarg) == 0) {
|
|
APar_RemoveAtom("moov.udta.meta.ilst.stik.data", VERSIONED_ATOM, 0);
|
|
} else {
|
|
uint8_t stik_value = 0;
|
|
|
|
if (strncmp(optarg, "value=", 6) == 0) {
|
|
char *stik_val_str_ptr = optarg;
|
|
strsep(&stik_val_str_ptr, "=");
|
|
stik_value = strtoul(stik_val_str_ptr, NULL, 10);
|
|
} else {
|
|
stiks *return_stik = MatchStikString(optarg);
|
|
if (return_stik != NULL) {
|
|
stik_value = return_stik->stik_number;
|
|
if (strcmp(optarg, "Audiobook") == 0) {
|
|
forced_suffix_type = FORCE_M4B_TYPE;
|
|
}
|
|
}
|
|
}
|
|
// stik is [0, 0, 0, 0, stik_value]; BUT that first uint32_t is
|
|
// already accounted for in APar_MetaData_atom_Init
|
|
AtomicInfo *stikData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.stik.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(stikData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
stik_value,
|
|
8);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Meta_EncodingTool: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "encoding tool")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *encodingtoolData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251too.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(encodingtoolData_atom,
|
|
optarg,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
0,
|
|
0);
|
|
break;
|
|
}
|
|
|
|
case Meta_EncodedBy: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "encoded by")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *encodedbyData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251enc.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
encodedbyData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_apID: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "Account Name")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *apIDData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.apID.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
apIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_description: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "description")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *descriptionData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.desc.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
descriptionData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_longdescription: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "longdesc")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *descriptionData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.ldes.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_storedescription: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "storedesc")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *descriptionData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.sdes.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
descriptionData_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_Rating: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "Rating")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *ratingData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.rate.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
ratingData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_TV_Network: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Network")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *tvnetworkData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.tvnn.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
tvnetworkData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_TV_ShowName: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Show name")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *tvshownameData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.tvsh.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
tvshownameData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_TV_Episode: { // if the show "ABC Lost 209", its "209"
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(
|
|
metadata_style == ITUNES_STYLE, 1, "TV Episode string")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *tvepisodeData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.tven.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
tvepisodeData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_TV_SeasonNumber: { // if the show "ABC Lost 209", its 2; integer 2
|
|
// not char "2"
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "TV Season")) {
|
|
break;
|
|
}
|
|
|
|
uint16_t data_value = 0;
|
|
sscanf(optarg, "%" SCNu16, &data_value);
|
|
|
|
AtomicInfo *tvseasonData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.tvsn.data", optarg, AtomFlags_Data_UInt);
|
|
// season is [0, 0, 0, 0, 0, 0, 0, data_value]; BUT that first uint32_t
|
|
// is already accounted for in APar_MetaData_atom_Init
|
|
APar_Unified_atom_Put(
|
|
tvseasonData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
|
|
APar_Unified_atom_Put(tvseasonData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
data_value,
|
|
16);
|
|
break;
|
|
}
|
|
|
|
case Meta_TV_EpisodeNumber: { // if the show "ABC Lost 209", its 9; integer
|
|
// 9 (0x09) not char "9"(0x39)
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(
|
|
metadata_style == ITUNES_STYLE, 1, "TV Episode number")) {
|
|
break;
|
|
}
|
|
|
|
uint16_t data_value = 0;
|
|
sscanf(optarg, "%" SCNu16, &data_value);
|
|
|
|
AtomicInfo *tvepisodenumData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.tves.data", optarg, AtomFlags_Data_UInt);
|
|
// episodenumber is [0, 0, 0, 0, 0, 0, 0, data_value]; BUT that first
|
|
// uint32_t is already accounted for in APar_MetaData_atom_Init
|
|
APar_Unified_atom_Put(
|
|
tvepisodenumData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 0, 16);
|
|
APar_Unified_atom_Put(tvepisodenumData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
data_value,
|
|
16);
|
|
break;
|
|
}
|
|
|
|
case Meta_cnID: { // the iTunes Catalog ID
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(
|
|
metadata_style == ITUNES_STYLE, 1, "iTunes Catalog ID")) {
|
|
break;
|
|
}
|
|
|
|
uint32_t data_value = 0;
|
|
sscanf(optarg, "%" SCNu32, &data_value);
|
|
|
|
AtomicInfo *cnIDData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.cnID.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(cnIDData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
data_value,
|
|
32);
|
|
break;
|
|
}
|
|
|
|
case Meta_geID: { // the iTunes Genre ID
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes Genre ID")) {
|
|
break;
|
|
}
|
|
|
|
uint32_t data_value = 0;
|
|
sscanf(optarg, "%" SCNu32, &data_value);
|
|
|
|
AtomicInfo *geIDData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.geID.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(geIDData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
data_value,
|
|
32);
|
|
break;
|
|
}
|
|
|
|
case Meta_xID: { // the vendor-supplied iTunes xID
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "iTunes xID")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *xIDData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.xid .data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
xIDData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_album_artist: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "album artist")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *albumartistData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.aART.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
albumartistData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_podcastFlag: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast flag")) {
|
|
break;
|
|
}
|
|
|
|
if (strcmp(optarg, "false") == 0) {
|
|
APar_RemoveAtom("moov.udta.meta.ilst.pcst.data", VERSIONED_ATOM, 0);
|
|
} else {
|
|
// podcastflag: [0, 0, 0, 0, boolean_value]; BUT that first uint32_t
|
|
// is already accounted for in APar_MetaData_atom_Init
|
|
AtomicInfo *podcastFlagData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.pcst.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(
|
|
podcastFlagData_atom,
|
|
NULL,
|
|
UTF8_iTunesStyle_256glyphLimited,
|
|
1,
|
|
8); // a hard coded uint8_t of: 1 denotes podcast flag
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Meta_keyword: { // TODO to the end of iTunes-style metadata & uuid
|
|
// atoms
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "keyword")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *keywordData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.keyw.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
keywordData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_category: { // see
|
|
// http://www.apple.com/itunes/podcasts/techspecs.html
|
|
// for available categories
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "category")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *categoryData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.catg.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
categoryData_atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_podcast_URL: { // usually a read-only value, but useful for
|
|
// getting videos into the 'podcast' menu
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast URL")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *podcasturlData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.purl.data", optarg, AtomFlags_Data_Binary);
|
|
APar_Unified_atom_Put(
|
|
podcasturlData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_podcast_GUID: { // Global Unique IDentifier; it is *highly*
|
|
// doubtful that this would be useful...
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "podcast GUID")) {
|
|
break;
|
|
}
|
|
|
|
AtomicInfo *globalidData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.egid.data", optarg, AtomFlags_Data_Binary);
|
|
APar_Unified_atom_Put(
|
|
globalidData_atom, optarg, UTF8_iTunesStyle_Binary, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_PurchaseDate: { // might be useful to *remove* this, but adding
|
|
// it... although it could function like id3v2
|
|
// tdtg...
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "purchase date")) {
|
|
break;
|
|
}
|
|
char *purd_time;
|
|
bool free_memory = false;
|
|
if (optarg != NULL) {
|
|
if (strcmp(optarg, "timestamp") == 0) {
|
|
purd_time = (char *)malloc(sizeof(char) * 255);
|
|
free_memory = true;
|
|
APar_StandardTime(purd_time);
|
|
} else {
|
|
purd_time = optarg;
|
|
}
|
|
} else {
|
|
purd_time = optarg;
|
|
}
|
|
|
|
AtomicInfo *globalIDData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.purd.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
globalIDData_atom, purd_time, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
if (free_memory) {
|
|
free(purd_time);
|
|
purd_time = NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Meta_PlayGapless: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "gapless playback")) {
|
|
break;
|
|
}
|
|
|
|
if (strcmp(optarg, "false") == 0 || strlen(optarg) == 0) {
|
|
APar_RemoveAtom("moov.udta.meta.ilst.pgap.data", VERSIONED_ATOM, 0);
|
|
} else {
|
|
// gapless playback: [0, 0, 0, 0, boolean_value]; BUT that first
|
|
// uint32_t is already accounted for in APar_MetaData_atom_Init
|
|
AtomicInfo *gaplessData_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.pgap.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(
|
|
gaplessData_atom, NULL, UTF8_iTunesStyle_256glyphLimited, 1, 8);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Meta_SortOrder: {
|
|
AtomicInfo *sortOrder_atom = NULL;
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "sort order tags")) {
|
|
break;
|
|
}
|
|
|
|
if (argv[optind] == NULL) {
|
|
fprintf(stdout,
|
|
"AP warning, skipping setting the sort order %s tag\n",
|
|
optarg);
|
|
break;
|
|
}
|
|
|
|
if (strcmp(optarg, "name") == 0) {
|
|
sortOrder_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.sonm.data", argv[optind], AtomFlags_Data_Text);
|
|
} else if (strcmp(optarg, "artist") == 0) {
|
|
sortOrder_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.soar.data", argv[optind], AtomFlags_Data_Text);
|
|
} else if (strcmp(optarg, "albumartist") == 0) {
|
|
sortOrder_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.soaa.data", argv[optind], AtomFlags_Data_Text);
|
|
} else if (strcmp(optarg, "album") == 0) {
|
|
sortOrder_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.soal.data", argv[optind], AtomFlags_Data_Text);
|
|
} else if (strcmp(optarg, "composer") == 0) {
|
|
sortOrder_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.soco.data", argv[optind], AtomFlags_Data_Text);
|
|
} else if (strcmp(optarg, "show") == 0) {
|
|
sortOrder_atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.sosn.data", argv[optind], AtomFlags_Data_Text);
|
|
}
|
|
APar_Unified_atom_Put(
|
|
sortOrder_atom, argv[optind], UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
|
|
break;
|
|
}
|
|
|
|
// uuid atoms
|
|
|
|
case Meta_StandardDate: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
char *formed_time = (char *)malloc(sizeof(char) * 110);
|
|
if (argv[optind]) {
|
|
if (strlen(argv[optind]) > 0) {
|
|
APar_StandardTime(formed_time);
|
|
}
|
|
}
|
|
|
|
AtomicInfo *tdtgUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s",
|
|
"tdtg",
|
|
AtomFlags_Data_Text,
|
|
formed_time,
|
|
false);
|
|
APar_Unified_atom_Put(
|
|
tdtgUUID, formed_time, UTF8_iTunesStyle_Unlimited, 0, 0);
|
|
free(formed_time);
|
|
break;
|
|
}
|
|
|
|
case Meta_URL: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
AtomicInfo *urlUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s",
|
|
"\251url",
|
|
AtomFlags_Data_Text,
|
|
optarg,
|
|
false);
|
|
APar_Unified_atom_Put(urlUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_Information: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
AtomicInfo *infoUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s",
|
|
"\251inf",
|
|
AtomFlags_Data_Text,
|
|
optarg,
|
|
false);
|
|
APar_Unified_atom_Put(infoUUID, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_uuid: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
uint32_t uuid_dataType = 0;
|
|
uint32_t desc_len = 0;
|
|
uint8_t mime_len = 0;
|
|
char *uuid_file_path = NULL;
|
|
char *uuid_file_description = NULL;
|
|
char *uuid_file_extn = NULL;
|
|
char *uuid_file_mimetype = NULL;
|
|
// char* uuid_file_filename = NULL;
|
|
|
|
// a uuid in AP is a version 5 uuid created by getting a sha1 hash
|
|
// of a string (the atom name) in a namespace ('AP.sf.net'). This
|
|
// is guaranteed to be reproducible, so later it can be verified
|
|
// that this uuid (which could come from anywhere), is in fact made
|
|
// by AtomicParsley. This is achieved by storing the atom name
|
|
// string right after the uuid, and is read back later and a new
|
|
// uuid is created to see if it matches the discovered uuid. If
|
|
// they match, it will print out or extract to a file; if not, only
|
|
// its name will be displayed in the tree.
|
|
//
|
|
// --meta-uuid "\251foo" 1 'http://www.url.org' --meta-uuid "pdf1"
|
|
// file /some/path/pic.pdf description="My Booty, Your Booty,
|
|
// Djbouti"
|
|
|
|
if (strcmp(argv[optind], "text") == 0 || strcmp(argv[optind], "1") == 0)
|
|
uuid_dataType = AtomFlags_Data_Text;
|
|
if (strcmp(argv[optind], "file") == 0) {
|
|
uuid_dataType = AtomFlags_Data_uuid_binary;
|
|
if (optind + 1 < argc) {
|
|
if (true) { // TODO: test the file to see if it exists
|
|
uuid_file_path = argv[optind + 1];
|
|
// get the file extension/suffix of the file to embed
|
|
uuid_file_extn =
|
|
strrchr(uuid_file_path,
|
|
'.'); //'.' inclusive; say goodbye to AP-0.8.8.tar.bz2
|
|
|
|
//#ifdef _WIN32
|
|
//#define path_delim '\\'
|
|
//#else
|
|
//#define path_delim '/'
|
|
//#endif
|
|
// uuid_file_filename = strrchr(uuid_file_path,
|
|
// path_delim)+1; //includes whatever
|
|
// extensions
|
|
}
|
|
if (uuid_file_extn == NULL) {
|
|
fprintf(stdout,
|
|
"AP warning: embedding a file onto a uuid atom "
|
|
"requires a file extension. Skipping.\n");
|
|
continue;
|
|
}
|
|
// copy a pointer to description
|
|
int more_optional_args = 2;
|
|
while (optind + more_optional_args < argc) {
|
|
if (strncmp(
|
|
argv[optind + more_optional_args], "description=", 12) ==
|
|
0 &&
|
|
argv[optind + more_optional_args][12]) {
|
|
uuid_file_description = argv[optind + more_optional_args] + 12;
|
|
desc_len = strlen(uuid_file_description) +
|
|
1; //+1 for the trailing 1 byte NULL terminator
|
|
}
|
|
if (strncmp(argv[optind + more_optional_args], "mime-type=", 10) ==
|
|
0 &&
|
|
argv[optind + more_optional_args][10]) {
|
|
uuid_file_mimetype = argv[optind + more_optional_args] + 10;
|
|
mime_len = strlen(uuid_file_mimetype) +
|
|
1; //+1 for the trailing 1 byte NULL terminator
|
|
}
|
|
if (strncmp(argv[optind + more_optional_args], "--", 2) == 0) {
|
|
break; // we've hit another cli argument
|
|
}
|
|
more_optional_args++;
|
|
}
|
|
}
|
|
}
|
|
|
|
AtomicInfo *genericUUID = APar_uuid_atom_Init("moov.udta.meta.uuid=%s",
|
|
optarg,
|
|
uuid_dataType,
|
|
argv[optind + 1],
|
|
true);
|
|
|
|
if (uuid_dataType == AtomFlags_Data_uuid_binary && genericUUID != NULL) {
|
|
TestFileExistence(uuid_file_path, true);
|
|
|
|
// format for a uuid atom set by AP:
|
|
// 4 bytes - atom length as uin32_t
|
|
// 4 bytes - atom name as iso 8859-1 atom name as a 4byte string set
|
|
// to 'uuid' 16 bytes - the uuid; here a version 5 sha1-based hash
|
|
// derived from a name in a namespace of 'AtomicParsley.sf.net' 4 bytes
|
|
// - the name of the desired atom to create a uuid for (this "name" of
|
|
// the uuid is the only cli accessible means of crafting the uuid) 4
|
|
// bytes - atom version & flags (currently 1 for 'text' or '88' for
|
|
// binary attachment) 4 bytes - NULL space
|
|
/////////////text or 1 for version/flags
|
|
// X bytes - utf8 string, no null termination
|
|
/////////////binary attachment or 88 for version/flags
|
|
// 4 bytes - length of utf8 string describing the attachment
|
|
// X bytes - utf8 string describing the attachment, null terminated
|
|
// 1 byte - length of the file suffix (including the period) of the
|
|
// originating file X bytes - utf8 string of the file suffix, null
|
|
// terminated 1 byte - length of the MIME-type X bytes - utf8 string
|
|
// holding the MIME-type, null terminated 4 bytes - length of the
|
|
// attached binary data/file length X bytes - binary data
|
|
|
|
uint32_t extn_len = strlen(uuid_file_extn) +
|
|
1; //+1 for the trailing 1 byte NULL terminator
|
|
uint64_t file_len = findFileSize(uuid_file_path);
|
|
|
|
APar_MetaData_atom_QuickInit(genericUUID->AtomicNumber,
|
|
uuid_dataType,
|
|
20,
|
|
extn_len + desc_len + file_len + 100);
|
|
genericUUID->AtomicClassification =
|
|
EXTENDED_ATOM; // it gets reset in QuickInit Above; force its proper
|
|
// setting
|
|
|
|
if (uuid_file_description == NULL || desc_len == 0) {
|
|
APar_Unified_atom_Put(
|
|
genericUUID,
|
|
"[none]",
|
|
UTF8_3GP_Style,
|
|
7,
|
|
32); // sets 4bytes desc_len, then 7bytes description (forced to
|
|
// "[none]"=6 + 1 byte NULL =7)
|
|
} else {
|
|
APar_Unified_atom_Put(genericUUID,
|
|
uuid_file_description,
|
|
UTF8_3GP_Style,
|
|
desc_len,
|
|
32); // sets 4bytes desc_len, then Xbytes
|
|
// description (in that order)
|
|
}
|
|
|
|
APar_Unified_atom_Put(genericUUID,
|
|
uuid_file_extn,
|
|
UTF8_3GP_Style,
|
|
extn_len,
|
|
8); // sets 1bytes desc_len, then Xbytes file
|
|
// extension string (in that order)
|
|
|
|
if (uuid_file_mimetype == NULL || mime_len == 0) {
|
|
APar_Unified_atom_Put(
|
|
genericUUID,
|
|
"none",
|
|
UTF8_3GP_Style,
|
|
5,
|
|
8); // sets 4bytes desc_len, then 5bytes description (forced to
|
|
// "none" + 1byte null)
|
|
} else {
|
|
APar_Unified_atom_Put(
|
|
genericUUID,
|
|
uuid_file_mimetype,
|
|
UTF8_3GP_Style,
|
|
mime_len,
|
|
8); // sets 1 byte mime len, then Xbytes mime type
|
|
}
|
|
|
|
FILE *uuid_binfile = APar_OpenFile(uuid_file_path, "rb");
|
|
APar_Unified_atom_Put(genericUUID, NULL, UTF8_3GP_Style, file_len, 32);
|
|
// store the data directly on the atom in AtomicData
|
|
uint32_t bin_bytes_read = APar_ReadFile(
|
|
genericUUID->AtomicData + (genericUUID->AtomicLength - 32),
|
|
uuid_binfile,
|
|
file_len);
|
|
genericUUID->AtomicLength += bin_bytes_read;
|
|
fclose(uuid_binfile);
|
|
|
|
} else { // text type
|
|
APar_Unified_atom_Put(
|
|
genericUUID, argv[optind + 1], UTF8_iTunesStyle_Unlimited, 0, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Opt_Extract_all_uuids: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
char *output_path = NULL;
|
|
if (optind + 1 == argc) {
|
|
output_path = argv[optind];
|
|
}
|
|
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, true);
|
|
APar_Print_APuuid_atoms(
|
|
ISObasemediafile, output_path, EXTRACT_ALL_UUID_BINARYS);
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
|
|
exit(0); // never gets here
|
|
break;
|
|
}
|
|
|
|
case Opt_Extract_a_uuid: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
|
|
char *uuid_path = (char *)calloc(1, sizeof(char) * 256 + 1);
|
|
char *uuid_binary_str = (char *)calloc(1, sizeof(char) * 20 + 1);
|
|
char uuid_4char_name[16];
|
|
memset(uuid_4char_name, 0, 16);
|
|
AtomicInfo *extractionAtom = NULL;
|
|
|
|
UTF8Toisolat1((unsigned char *)&uuid_4char_name,
|
|
4,
|
|
(unsigned char *)optarg,
|
|
strlen(optarg));
|
|
APar_generate_uuid_from_atomname(uuid_4char_name, uuid_binary_str);
|
|
|
|
// this will only append (and knock off) %s (anything) at the end of a
|
|
// string
|
|
uint16_t path_len = strlen("moov.udta.meta.uuid=%s");
|
|
memcpy(uuid_path, "moov.udta.meta.uuid=%s", path_len - 2);
|
|
memcpy(uuid_path + (path_len - 2), uuid_binary_str, 16);
|
|
|
|
extractionAtom = APar_FindAtom(uuid_path, false, EXTENDED_ATOM, 0, true);
|
|
if (extractionAtom != NULL) {
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, true);
|
|
APar_Extract_uuid_binary_file(extractionAtom, ISObasemediafile, NULL);
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
}
|
|
|
|
free(uuid_path);
|
|
uuid_path = NULL;
|
|
free(uuid_binary_str);
|
|
uuid_binary_str = NULL;
|
|
exit(0);
|
|
break; // never gets here
|
|
}
|
|
|
|
case Opt_Ipod_AVC_uuid: {
|
|
if (deep_atom_scan == true) {
|
|
if (strcmp(optarg, "1200") != 0) {
|
|
fprintf(stdout,
|
|
"the ipod-uuid has a single preset of '1200' which "
|
|
"is required\n"); // 1200 might not be the only max
|
|
// macroblock setting down the pike
|
|
break;
|
|
}
|
|
uint8_t total_tracks = 0;
|
|
uint8_t a_track = 0; // unused
|
|
char atom_path[100];
|
|
AtomicInfo *video_desc_atom = NULL;
|
|
|
|
memset(atom_path, 0, 100);
|
|
|
|
APar_FindAtomInTrack(
|
|
total_tracks,
|
|
a_track,
|
|
NULL); // With track_num set to 0, it will return the total trak
|
|
// atom into total_tracks here.
|
|
|
|
while (a_track < total_tracks) {
|
|
a_track++;
|
|
sprintf(atom_path, "moov.trak[%u].mdia.minf.stbl.stsd.avc1", a_track);
|
|
video_desc_atom =
|
|
APar_FindAtom(atom_path, false, VERSIONED_ATOM, 0, false);
|
|
|
|
if (video_desc_atom != NULL) {
|
|
uint16_t mb_t = APar_TestVideoDescription(
|
|
video_desc_atom, APar_OpenFile(ISObasemediafile, "rb"));
|
|
if (mb_t > 0 && mb_t <= 1200) {
|
|
sprintf(atom_path,
|
|
"moov.trak[%u].mdia.minf.stbl.stsd.avc1.uuid=",
|
|
a_track);
|
|
uint8_t uuid_baselen = (uint8_t)strlen(atom_path);
|
|
APar_uuid_scanf(atom_path + uuid_baselen,
|
|
"6b6840f2-5f24-4fc5-ba39-a51bcf0323f3");
|
|
APar_endian_uuid_bin_str_conversion(atom_path + uuid_baselen);
|
|
APar_Generate_iPod_uuid(atom_path);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
fprintf(
|
|
stdout,
|
|
"the --DeepScan option is required for this operation. Skipping\n");
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Manual_atom_removal: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
|
|
char *compliant_name = (char *)malloc(sizeof(char) * strlen(optarg) + 1);
|
|
memset(compliant_name, 0, strlen(optarg) + 1);
|
|
UTF8Toisolat1((unsigned char *)compliant_name,
|
|
strlen(optarg),
|
|
(unsigned char *)optarg,
|
|
strlen(optarg));
|
|
|
|
if (strstr(optarg, "uuid=") != NULL) {
|
|
uint16_t uuid_path_pos = 0;
|
|
uint16_t uuid_path_len = strlen(optarg);
|
|
while (compliant_name + uuid_path_pos <
|
|
compliant_name + uuid_path_len) {
|
|
if (strncmp(compliant_name + uuid_path_pos, "uuid=", 5) == 0) {
|
|
uuid_path_pos += 5;
|
|
break;
|
|
}
|
|
uuid_path_pos++;
|
|
}
|
|
if (strlen(compliant_name + uuid_path_pos) >
|
|
4) { // if =4 then it would be the deprecated form (or it should be,
|
|
// if not it just won't find anything; no harm done)
|
|
uint8_t uuid_len = APar_uuid_scanf(compliant_name + uuid_path_pos,
|
|
optarg + uuid_path_pos);
|
|
compliant_name[uuid_path_pos + uuid_len] = 0;
|
|
}
|
|
APar_RemoveAtom(compliant_name, EXTENDED_ATOM, 0);
|
|
|
|
} else if (strcmp(compliant_name + (strlen(compliant_name) - 4),
|
|
"data") == 0) {
|
|
APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0);
|
|
|
|
} else {
|
|
size_t string_len = strlen(compliant_name);
|
|
// reverseDNS atom path
|
|
if (strstr(optarg, ":[") != NULL &&
|
|
compliant_name[string_len - 1] == ']') {
|
|
APar_RemoveAtom(compliant_name, VERSIONED_ATOM, 0);
|
|
|
|
// packed language asset
|
|
} else if (strncmp(compliant_name + string_len - 9, ":lang=", 6) == 0) {
|
|
uint16_t packed_lang =
|
|
PackLanguage(compliant_name + string_len - 3, 0);
|
|
memset(compliant_name + string_len - 9, 0, 1);
|
|
APar_RemoveAtom(compliant_name, PACKED_LANG_ATOM, packed_lang);
|
|
|
|
} else {
|
|
APar_RemoveAtom(compliant_name, UNKNOWN_ATOM, 0);
|
|
}
|
|
}
|
|
free(compliant_name);
|
|
compliant_name = NULL;
|
|
break;
|
|
}
|
|
|
|
// 3gp tags
|
|
|
|
/*
|
|
First, scan the file to get at atom tree (only happens once). Then take the
|
|
cli args and look for optional arguments. All arguments begin with -- or -;
|
|
other args are optional and are determined by directly testing arguments.
|
|
Optional arguments common to the 3gp asset group (language, unicode,
|
|
track/movie userdata) are extracted in find_optional_args. Setting assets in
|
|
all tracks requires getting the number of tracks. Loop through either once
|
|
(for movie & single track) or as many tracks there are for all tracks. Each
|
|
pass through the loop, set the individual pieces of metadata.
|
|
*/
|
|
case _3GP_Title: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"title")) {
|
|
break;
|
|
}
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
3);
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1; // otherwise, APar_UserData_atom_Init will shift
|
|
// to non-existing track 0
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *title_asset = APar_UserData_atom_Init(
|
|
"titl",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
APar_Unified_atom_Put(
|
|
title_asset,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Author: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"author")) {
|
|
break;
|
|
}
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
3);
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *author_asset = APar_UserData_atom_Init(
|
|
"auth",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
APar_Unified_atom_Put(
|
|
author_asset,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Performer: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"performer")) {
|
|
break;
|
|
}
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
3);
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *performer_asset = APar_UserData_atom_Init(
|
|
"perf",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
APar_Unified_atom_Put(
|
|
performer_asset,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Genre: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"genre")) {
|
|
break;
|
|
}
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
3);
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *genre_asset = APar_UserData_atom_Init(
|
|
"gnre",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
APar_Unified_atom_Put(
|
|
genre_asset,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Description: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"description")) {
|
|
break;
|
|
}
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
3);
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *description_asset = APar_UserData_atom_Init(
|
|
"dscp",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
APar_Unified_atom_Put(
|
|
description_asset,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ISO_Copyright: // ISO copyright atom common to all files that are
|
|
// derivatives of the base media file format, identical
|
|
// to....
|
|
case _3GP_Copyright: { // the 3gp copyright asset; this gets a test for
|
|
// major branding (but only with the cli arg
|
|
// --3gp-copyright).
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
|
|
if (c == _3GP_Copyright) {
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"copyright")) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
3);
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *copyright_notice = APar_UserData_atom_Init(
|
|
"cprt",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
APar_Unified_atom_Put(
|
|
copyright_notice,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Album: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"album")) {
|
|
break;
|
|
}
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
uint8_t tracknum = 0;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
4);
|
|
|
|
// cygle through the remaining independant arguments (before the next
|
|
// --cli_flag) and figure out if any are useful to us; already have lang &
|
|
// utf16
|
|
for (int i = 0; i <= 4;
|
|
i++) { // 3 possible arguments for this tag (the first - which
|
|
// doesn't count - is the data for the tag itself)
|
|
if (argv[optind + i] && optind + i <= total_args) {
|
|
if (strncmp(argv[optind + i], "trknum=", 7) == 0) {
|
|
char *track_num = argv[optind + i];
|
|
strsep(&track_num, "=");
|
|
tracknum = strtoul(track_num, NULL, 10);
|
|
}
|
|
if (*argv[optind + i] == '-')
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *album_asset = APar_UserData_atom_Init(
|
|
"albm",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
APar_Unified_atom_Put(
|
|
album_asset,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
if (tracknum != 0) {
|
|
APar_Unified_atom_Put(album_asset, NULL, UTF8_3GP_Style, tracknum, 8);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Year: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"year")) {
|
|
break;
|
|
}
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
uint16_t year_tag = 0;
|
|
|
|
if (argv[optind] && optind <= total_args) {
|
|
if (strcmp(argv[optind], "movie") == 0) {
|
|
userdata_area = MOVIE_LEVEL_ATOM;
|
|
}
|
|
if (strncmp(argv[optind], "track=", 6) == 0) {
|
|
char *trak_idx = argv[optind];
|
|
strsep(&trak_idx, "=");
|
|
selected_track = strtoul(trak_idx, NULL, 10);
|
|
userdata_area = SINGLE_TRACK_ATOM;
|
|
} else if (strcmp(argv[optind], "track") == 0) {
|
|
userdata_area = ALL_TRACKS_ATOM;
|
|
}
|
|
}
|
|
|
|
sscanf(optarg, "%" SCNu16, &year_tag);
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *recordingyear_asset = APar_UserData_atom_Init(
|
|
"yrrc",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
0);
|
|
APar_Unified_atom_Put(
|
|
recordingyear_asset, NULL, UTF8_3GP_Style, year_tag, 16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Rating: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"rating")) {
|
|
break;
|
|
}
|
|
char rating_entity[5] = {
|
|
'N',
|
|
'O',
|
|
'N',
|
|
'E',
|
|
0}; //'NONE' - thats what it will be if not provided
|
|
char rating_criteria[5] = {'N', 'O', 'N', 'E', 0};
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
5);
|
|
|
|
for (int i = 0; i < 5;
|
|
i++) { // 3 possible arguments for this tag (the first - which
|
|
// doesn't count - is the data for the tag itself)
|
|
if (argv[optind + i] && optind + i <= total_args) {
|
|
if (strncmp(argv[optind + i], "entity=", 7) == 0) {
|
|
char *entity = argv[optind + i];
|
|
strsep(&entity, "=");
|
|
memcpy(&rating_entity, entity, 4);
|
|
}
|
|
if (strncmp(argv[optind + i], "criteria=", 9) == 0) {
|
|
char *criteria = argv[optind + i];
|
|
strsep(&criteria, "=");
|
|
memcpy(&rating_criteria, criteria, 4);
|
|
}
|
|
if (*argv[optind + i] == '-')
|
|
break; // we've hit another cli argument
|
|
}
|
|
}
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *rating_asset = APar_UserData_atom_Init(
|
|
"rtng",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
|
|
APar_Unified_atom_Put(rating_asset,
|
|
NULL,
|
|
UTF8_3GP_Style,
|
|
UInt32FromBigEndian(rating_entity),
|
|
32);
|
|
APar_Unified_atom_Put(rating_asset,
|
|
NULL,
|
|
UTF8_3GP_Style,
|
|
UInt32FromBigEndian(rating_criteria),
|
|
32);
|
|
APar_Unified_atom_Put(
|
|
rating_asset,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Classification: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"classification")) {
|
|
break;
|
|
}
|
|
char classification_entity[5] = {
|
|
'N',
|
|
'O',
|
|
'N',
|
|
'E',
|
|
0}; //'NONE' - thats what it will be if not provided
|
|
uint16_t classification_index = 0;
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
5);
|
|
|
|
for (int i = 0; i < 4;
|
|
i++) { // 3 possible arguments for this tag (the first - which
|
|
// doesn't count - is the data for the tag itself)
|
|
if (argv[optind + i] && optind + i <= total_args) {
|
|
if (strncmp(argv[optind + i], "entity=", 7) == 0) {
|
|
char *cls_entity = argv[optind + i];
|
|
strsep(&cls_entity, "=");
|
|
memcpy(&classification_entity, cls_entity, 4);
|
|
}
|
|
if (strncmp(argv[optind + i], "index=", 6) == 0) {
|
|
char *cls_idx = argv[optind + i];
|
|
strsep(&cls_idx, "=");
|
|
sscanf(cls_idx, "%" SCNu16, &classification_index);
|
|
}
|
|
if (*argv[optind + i] == '-')
|
|
break; // we've hit another cli argument
|
|
}
|
|
}
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *classification_asset = APar_UserData_atom_Init(
|
|
"clsf",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
|
|
APar_Unified_atom_Put(classification_asset,
|
|
NULL,
|
|
UTF8_3GP_Style,
|
|
UInt32FromBigEndian(classification_entity),
|
|
32);
|
|
APar_Unified_atom_Put(classification_asset,
|
|
NULL,
|
|
UTF8_3GP_Style,
|
|
classification_index,
|
|
16);
|
|
APar_Unified_atom_Put(
|
|
classification_asset,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Keyword: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"keyword")) {
|
|
break;
|
|
}
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
char *formed_keyword_struct = NULL;
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
4);
|
|
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
if (strrchr(optarg, '=') !=
|
|
NULL) { // must be in the format of: keywords=foo1,foo2,foo3,foo4
|
|
char *arg_keywords = optarg;
|
|
char *keywords_globbed =
|
|
strsep(&arg_keywords, "="); // separate out 'keyword='
|
|
keywords_globbed =
|
|
strsep(&arg_keywords,
|
|
"="); // this is what we want to work on: just the keywords
|
|
char *keyword_ptr = keywords_globbed;
|
|
uint32_t keyword_strlen = strlen(keywords_globbed);
|
|
uint8_t keyword_count = 0;
|
|
uint32_t key_index = 0;
|
|
|
|
if (keyword_strlen >
|
|
0) { // if there is anything past the = then it counts as a keyword
|
|
keyword_count++;
|
|
}
|
|
|
|
while (true) { // count occurrences of comma here
|
|
if (*keyword_ptr == ',') {
|
|
keyword_count++;
|
|
}
|
|
keyword_ptr++;
|
|
key_index++;
|
|
if (keyword_strlen == key_index) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
formed_keyword_struct = (char *)calloc(
|
|
1,
|
|
sizeof(char) * set_UTF16_text
|
|
? (keyword_strlen * 4)
|
|
: (keyword_strlen * 2)); // *4 should carry utf16's BOM & TERM
|
|
uint32_t keyword_struct_bytes =
|
|
APar_3GP_Keyword_atom_Format(keywords_globbed,
|
|
keyword_count,
|
|
set_UTF16_text,
|
|
formed_keyword_struct);
|
|
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
AtomicInfo *keyword_asset = APar_UserData_atom_Init(
|
|
"kywd",
|
|
keyword_strlen ? "temporary" : "",
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang); // just a "temporary" valid string to satisfy a test
|
|
// there
|
|
if (keyword_strlen > 0) {
|
|
APar_Unified_atom_Put(
|
|
keyword_asset, NULL, UTF8_3GP_Style, packed_lang, 16);
|
|
APar_Unified_atom_Put(
|
|
keyword_asset, NULL, UTF8_3GP_Style, keyword_count, 8);
|
|
APar_atom_Binary_Put(
|
|
keyword_asset, formed_keyword_struct, keyword_struct_bytes, 3);
|
|
}
|
|
}
|
|
if (formed_keyword_struct != NULL) {
|
|
free(formed_keyword_struct);
|
|
formed_keyword_struct = NULL;
|
|
}
|
|
} else {
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
APar_UserData_atom_Init("kywd",
|
|
"",
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track
|
|
: i_asset,
|
|
packed_lang);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case _3GP_Location: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style >= THIRD_GEN_PARTNER &&
|
|
metadata_style < MOTIONJPEG2000,
|
|
2,
|
|
"location")) {
|
|
break;
|
|
}
|
|
bool set_UTF16_text = false;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t userdata_area = MOVIE_LEVEL_ATOM;
|
|
uint8_t selected_track = 0;
|
|
uint8_t a_track = 0; // unused
|
|
uint8_t asset_iterations = 0;
|
|
double longitude =
|
|
-73.98; // if you don't provide a place, you WILL be put right into
|
|
// Central Park. Welcome to New York City... now go away.
|
|
double latitude = 40.77;
|
|
double altitude = 4.3;
|
|
uint8_t role = 0;
|
|
const char *astronomical_body = "Earth";
|
|
const char *additional_notes = "no notes";
|
|
|
|
find_optional_args(argv,
|
|
optind,
|
|
packed_lang,
|
|
set_UTF16_text,
|
|
userdata_area,
|
|
selected_track,
|
|
10);
|
|
|
|
for (int i = 0; i <= 10;
|
|
i++) { // 9 possible arguments for this tag (the first - which
|
|
// doesn't count - is the data for the tag itself)
|
|
if (argv[optind + i] && optind + i <= total_args) {
|
|
if (strncmp(argv[optind + i], "longitude=", 10) == 0) {
|
|
char *_long = argv[optind + i];
|
|
strsep(&_long, "=");
|
|
sscanf(_long, "%lf", &longitude);
|
|
if (_long[strlen(_long) - 1] == 'W') {
|
|
longitude *= -1;
|
|
}
|
|
}
|
|
if (strncmp(argv[optind + i], "latitude=", 9) == 0) {
|
|
char *_latt = argv[optind + i];
|
|
strsep(&_latt, "=");
|
|
sscanf(_latt, "%lf", &latitude);
|
|
if (_latt[strlen(_latt) - 1] == 'S') {
|
|
latitude *= -1;
|
|
}
|
|
}
|
|
if (strncmp(argv[optind + i], "altitude=", 9) == 0) {
|
|
char *_alti = argv[optind + i];
|
|
strsep(&_alti, "=");
|
|
sscanf(_alti, "%lf", &altitude);
|
|
if (_alti[strlen(_alti) - 1] == 'B') {
|
|
altitude *= -1;
|
|
}
|
|
}
|
|
if (strncmp(argv[optind + i], "role=", 5) == 0) {
|
|
char *_role = argv[optind + i];
|
|
strsep(&_role, "=");
|
|
if (strcmp(_role, "shooting location") == 0 ||
|
|
strcmp(_role, "shooting") == 0) {
|
|
role = 0;
|
|
} else if (strcmp(_role, "real location") == 0 ||
|
|
strcmp(_role, "real") == 0) {
|
|
role = 1;
|
|
} else if (strcmp(_role, "fictional location") == 0 ||
|
|
strcmp(_role, "fictional") == 0) {
|
|
role = 2;
|
|
}
|
|
}
|
|
if (strncmp(argv[optind + i], "body=", 5) == 0) {
|
|
char *_astrobody = argv[optind + i];
|
|
strsep(&_astrobody, "=");
|
|
astronomical_body = _astrobody;
|
|
}
|
|
if (strncmp(argv[optind + i], "notes=", 6) == 0) {
|
|
char *_add_notes = argv[optind + i];
|
|
strsep(&_add_notes, "=");
|
|
additional_notes = _add_notes;
|
|
}
|
|
if (*argv[optind + i] == '-')
|
|
break; // we've hit another cli argument
|
|
}
|
|
}
|
|
|
|
// fprintf(stdout, "long, lat, alt = %lf %lf %lf\n", longitude, latitude,
|
|
// altitude);
|
|
if (userdata_area == MOVIE_LEVEL_ATOM ||
|
|
userdata_area == SINGLE_TRACK_ATOM) {
|
|
asset_iterations = 1;
|
|
} else if (userdata_area == ALL_TRACKS_ATOM) {
|
|
APar_FindAtomInTrack(
|
|
asset_iterations,
|
|
a_track,
|
|
NULL); // With asset_iterations set to 0, it will return the total
|
|
// trak atom into total_tracks here.
|
|
if (asset_iterations == 1)
|
|
selected_track = 1;
|
|
}
|
|
|
|
if (longitude < -180.0 || longitude > 180.0 || latitude < -90.0 ||
|
|
latitude > 90.0) {
|
|
fprintf(stdout,
|
|
"AtomicParsley warning: longitude or latitude was "
|
|
"invalid; skipping setting location\n");
|
|
} else {
|
|
for (uint8_t i_asset = 1; i_asset <= asset_iterations; i_asset++) {
|
|
// short location_3GP_atom = APar_UserData_atom_Init("moov.udta.loci",
|
|
// optarg, packed_lang);
|
|
AtomicInfo *location_asset = APar_UserData_atom_Init(
|
|
"loci",
|
|
optarg,
|
|
userdata_area,
|
|
asset_iterations == 1 ? selected_track : i_asset,
|
|
packed_lang);
|
|
APar_Unified_atom_Put(
|
|
location_asset,
|
|
optarg,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
packed_lang,
|
|
16);
|
|
APar_Unified_atom_Put(location_asset, NULL, false, role, 8);
|
|
|
|
APar_Unified_atom_Put(location_asset,
|
|
NULL,
|
|
false,
|
|
float_to_16x16bit_fixed_point(longitude),
|
|
32);
|
|
APar_Unified_atom_Put(location_asset,
|
|
NULL,
|
|
false,
|
|
float_to_16x16bit_fixed_point(latitude),
|
|
32);
|
|
APar_Unified_atom_Put(location_asset,
|
|
NULL,
|
|
false,
|
|
float_to_16x16bit_fixed_point(altitude),
|
|
32);
|
|
APar_Unified_atom_Put(
|
|
location_asset,
|
|
astronomical_body,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
0,
|
|
0);
|
|
APar_Unified_atom_Put(
|
|
location_asset,
|
|
additional_notes,
|
|
(set_UTF16_text ? UTF16_3GP_Style : UTF8_3GP_Style),
|
|
0,
|
|
0);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Meta_ReverseDNS_Form: { //--rDNSatom "mv-ma" name=iTuneEXTC
|
|
// domain=com.apple.iTunes
|
|
char *reverseDNS_atomname = NULL;
|
|
char *reverseDNS_atomdomain = NULL;
|
|
uint32_t rdns_atom_flags = AtomFlags_Data_Text;
|
|
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "reverse DNS form")) {
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i <= 5 - 1; i++) {
|
|
if (argv[optind + i] && optind + i <= argc) {
|
|
if (strncmp(argv[optind + i], "name=", 5) == 0) {
|
|
reverseDNS_atomname = argv[optind + i] + 5;
|
|
} else if (strncmp(argv[optind + i], "domain=", 7) == 0) {
|
|
reverseDNS_atomdomain = argv[optind + i] + 7;
|
|
} else if (strncmp(argv[optind + i], "datatype=", 9) == 0) {
|
|
sscanf(argv[optind + i] + 9, "%" SCNu32, &rdns_atom_flags);
|
|
}
|
|
if (*argv[optind + i] == '-') {
|
|
break; // we've hit another cli argument
|
|
}
|
|
}
|
|
}
|
|
|
|
if (reverseDNS_atomname == NULL) {
|
|
fprintf(stdout,
|
|
"AtomicParsley warning: no name for the reverseDNS "
|
|
"form was found. Skipping.\n");
|
|
|
|
} else if ((int)strlen(reverseDNS_atomname) !=
|
|
test_conforming_alpha_string(reverseDNS_atomname)) {
|
|
fprintf(stdout,
|
|
"AtomicParsley warning: Some part of the reverseDNS "
|
|
"atom name was non-conforming. Skipping.\n");
|
|
|
|
} else if (reverseDNS_atomdomain == NULL) {
|
|
fprintf(stdout,
|
|
"AtomicParsley warning: no domain for the reverseDNS "
|
|
"form was found. Skipping.\n");
|
|
|
|
} else if (rdns_atom_flags != AtomFlags_Data_Text) {
|
|
fprintf(stdout,
|
|
"AtomicParsley warning: currently, only the strings "
|
|
"are supported in reverseDNS atoms. Skipping.\n");
|
|
|
|
} else {
|
|
AtomicInfo *rDNS_data_atom =
|
|
APar_reverseDNS_atom_Init(reverseDNS_atomname,
|
|
optarg,
|
|
&rdns_atom_flags,
|
|
reverseDNS_atomdomain);
|
|
APar_Unified_atom_Put(
|
|
rDNS_data_atom, optarg, UTF8_iTunesStyle_Unlimited, 0, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Meta_rDNS_rating: {
|
|
const char *media_rating = Expand_cli_mediastring(optarg);
|
|
uint32_t rDNS_data_flags = AtomFlags_Data_Text;
|
|
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
|
|
if (media_rating != NULL || strlen(optarg) == 0) {
|
|
AtomicInfo *rDNS_rating_data_atom = APar_reverseDNS_atom_Init(
|
|
"iTunEXTC", media_rating, &rDNS_data_flags, "com.apple.iTunes");
|
|
APar_Unified_atom_Put(rDNS_rating_data_atom,
|
|
media_rating,
|
|
UTF8_iTunesStyle_Unlimited,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Meta_ID3v2Tag: {
|
|
const char *target_frame_ID = NULL;
|
|
uint16_t packed_lang = 0;
|
|
uint8_t char_encoding = TE_UTF8; // utf8 is the default encoding
|
|
char meta_container = 0 - MOVIE_LEVEL_ATOM;
|
|
bool multistring = false;
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
|
|
// limit the files that can be tagged with meta.ID32 atoms. The file has
|
|
// to conform to the ISO BMFFv2 in order for a 'meta' atom. This should
|
|
// exclude files branded as 3gp5 for example, except it doesn't always.
|
|
// The test is for a compatible brand (of a v2 ISO MBFF). Quicktime writes
|
|
// some 3GPP files as 3gp5 with a compatible brand of mp42, so tagging
|
|
// works on these files. Not when you use timed text though.
|
|
if (!APar_assert(parsedAtoms[0].ancillary_data != 0 ||
|
|
(metadata_style >= THIRD_GEN_PARTNER_VER1_REL7 &&
|
|
metadata_style < MOTIONJPEG2000),
|
|
3,
|
|
NULL)) {
|
|
break;
|
|
}
|
|
AdjunctArgs *id3args = (AdjunctArgs *)malloc(sizeof(AdjunctArgs));
|
|
|
|
id3args->targetLang = NULL; // it will default later to "eng"
|
|
id3args->descripArg = NULL;
|
|
id3args->mimeArg = NULL;
|
|
id3args->pictypeArg = NULL;
|
|
id3args->ratingArg = NULL;
|
|
id3args->dataArg = NULL;
|
|
id3args->pictype_uint8 = 0;
|
|
id3args->groupSymbol = 0;
|
|
id3args->zlibCompressed = false;
|
|
id3args->multistringtext = false;
|
|
|
|
target_frame_ID = ConvertCLIFrameStr_TO_frameID(optarg);
|
|
if (target_frame_ID == NULL) {
|
|
target_frame_ID = optarg;
|
|
}
|
|
|
|
int frameType = FrameStr_TO_FrameType(target_frame_ID);
|
|
if (frameType >= 0) {
|
|
if (TestCLI_for_FrameParams(frameType, 0)) {
|
|
id3args->descripArg = find_ID3_optarg(argv, optind, "desc=");
|
|
}
|
|
if (TestCLI_for_FrameParams(frameType, 1)) {
|
|
id3args->mimeArg = find_ID3_optarg(argv, optind, "mimetype=");
|
|
}
|
|
if (TestCLI_for_FrameParams(frameType, 2)) {
|
|
id3args->pictypeArg = find_ID3_optarg(argv, optind, "imagetype=");
|
|
}
|
|
if (TestCLI_for_FrameParams(frameType, 3)) {
|
|
id3args->dataArg = find_ID3_optarg(argv, optind, "uniqueID=");
|
|
}
|
|
if (TestCLI_for_FrameParams(frameType, 4)) {
|
|
id3args->filenameArg = find_ID3_optarg(argv, optind, "filename=");
|
|
}
|
|
if (TestCLI_for_FrameParams(frameType, 5)) {
|
|
id3args->ratingArg = find_ID3_optarg(argv, optind, "rating=");
|
|
}
|
|
if (TestCLI_for_FrameParams(frameType, 6)) {
|
|
id3args->dataArg = find_ID3_optarg(argv, optind, "counter=");
|
|
}
|
|
if (TestCLI_for_FrameParams(frameType, 7)) {
|
|
id3args->dataArg = find_ID3_optarg(argv, optind, "data=");
|
|
}
|
|
if (TestCLI_for_FrameParams(frameType, 8)) {
|
|
id3args->dataArg = find_ID3_optarg(argv, optind, "data=");
|
|
}
|
|
if (*find_ID3_optarg(argv, optind, "compressed") == '1') {
|
|
id3args->zlibCompressed = true;
|
|
}
|
|
|
|
const char *groupsymbol = find_ID3_optarg(argv, optind, "groupsymbol=");
|
|
if (groupsymbol[0] == '0' && groupsymbol[1] == 'x') {
|
|
id3args->groupSymbol = strtoul(groupsymbol, NULL, 16);
|
|
if (id3args->groupSymbol < 0x80 || id3args->groupSymbol > 0xF0)
|
|
id3args->groupSymbol = 0;
|
|
}
|
|
}
|
|
|
|
scan_ID3_optargs(argv,
|
|
optind,
|
|
id3args->targetLang,
|
|
packed_lang,
|
|
char_encoding,
|
|
&meta_container,
|
|
multistring);
|
|
if (id3args->targetLang == NULL)
|
|
id3args->targetLang = "eng";
|
|
|
|
APar_OpenISOBaseMediaFile(
|
|
ISObasemediafile,
|
|
true); // if not already scanned, the whole tag for
|
|
// *this* ID32 atom needs to be read from file
|
|
AtomicInfo *id32_atom = APar_ID32_atom_Init(
|
|
target_frame_ID, meta_container, id3args->targetLang, packed_lang);
|
|
|
|
if (strcmp(argv[optind + 0], "extract") == 0 &&
|
|
(strcmp(target_frame_ID, "APIC") == 0 ||
|
|
strcmp(target_frame_ID, "GEOB") == 0)) {
|
|
if (id32_atom != NULL) {
|
|
APar_Extract_ID3v2_file(
|
|
id32_atom, target_frame_ID, ISObasemediafile, NULL, id3args);
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
APar_ID3FrameAmmend(
|
|
id32_atom, target_frame_ID, argv[optind + 0], id3args, char_encoding);
|
|
|
|
free(id3args);
|
|
id3args = NULL;
|
|
|
|
break;
|
|
}
|
|
|
|
// utility functions
|
|
|
|
case Metadata_Purge: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
APar_RemoveAtom("moov.udta.meta.ilst", SIMPLE_ATOM, 0);
|
|
|
|
break;
|
|
}
|
|
|
|
case UserData_Purge: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
APar_RemoveAtom("moov.udta", SIMPLE_ATOM, 0);
|
|
|
|
break;
|
|
}
|
|
|
|
case foobar_purge: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
APar_RemoveAtom("moov.udta.tags", UNKNOWN_ATOM, 0);
|
|
|
|
break;
|
|
}
|
|
|
|
case Opt_FreeFree: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
int free_level = -1;
|
|
if (argv[optind]) {
|
|
sscanf(argv[optind], "%i", &free_level);
|
|
}
|
|
APar_freefree(free_level);
|
|
|
|
break;
|
|
}
|
|
|
|
case OPT_OverWrite: {
|
|
alter_original = true;
|
|
break;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
case OPT_PreserveTimeStamps: {
|
|
alter_original = true;
|
|
preserve_timestamps = true;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case Meta_dump: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, true);
|
|
APar_MetadataFileDump(ISObasemediafile);
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
|
|
APar_FreeMemory();
|
|
exit(0);
|
|
}
|
|
|
|
case OPT_NoOptimize: {
|
|
force_existing_hierarchy = true;
|
|
break;
|
|
}
|
|
|
|
case OPT_OutputFile: {
|
|
output_file = optarg;
|
|
break;
|
|
}
|
|
|
|
case Meta_movementCount: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "movementCount")) {
|
|
break;
|
|
}
|
|
uint8_t data_value = 0;
|
|
sscanf(optarg, "%" SCNu8, &data_value);
|
|
AtomicInfo *atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251mvc.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(
|
|
atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 8);
|
|
break;
|
|
}
|
|
|
|
case Meta_movementName: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "movementName")) {
|
|
break;
|
|
}
|
|
AtomicInfo *atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251mvn.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_movementNumber: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "movementNumber")) {
|
|
break;
|
|
}
|
|
uint8_t data_value = 0;
|
|
sscanf(optarg, "%" SCNu8, &data_value);
|
|
AtomicInfo *atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251mvi.data", optarg, AtomFlags_Data_UInt);
|
|
APar_Unified_atom_Put(
|
|
atom, NULL, UTF8_iTunesStyle_256glyphLimited, data_value, 8);
|
|
break;
|
|
}
|
|
|
|
case Meta_showWorkMovement: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "showWorkMovement")) {
|
|
break;
|
|
}
|
|
AtomicInfo *atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.shwm.data", optarg, AtomFlags_Data_Binary);
|
|
APar_Unified_atom_Put(
|
|
atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
|
|
case Meta_work: {
|
|
APar_ScanAtoms(ISObasemediafile);
|
|
if (!APar_assert(metadata_style == ITUNES_STYLE, 1, "work")) {
|
|
break;
|
|
}
|
|
AtomicInfo *atom = APar_MetaData_atom_Init(
|
|
"moov.udta.meta.ilst.\251wrk.data", optarg, AtomFlags_Data_Text);
|
|
APar_Unified_atom_Put(
|
|
atom, optarg, UTF8_iTunesStyle_256glyphLimited, 0, 0);
|
|
break;
|
|
}
|
|
} /* end switch */
|
|
} /* end while */
|
|
|
|
// after all the modifications are enacted on the tree in memory, THEN
|
|
// write out the changes
|
|
|
|
if (modified_atoms) {
|
|
|
|
#if defined(_WIN32)
|
|
HANDLE hFile, hFileOut;
|
|
FILETIME createTime, accessTime, writeTime;
|
|
if (preserve_timestamps == true) {
|
|
hFile = APar_OpenFileWin32(ISObasemediafile,
|
|
GENERIC_WRITE | GENERIC_READ,
|
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
0);
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
GetFileTime(hFile, &createTime, &accessTime, &writeTime);
|
|
CloseHandle(hFile);
|
|
} else {
|
|
fprintf(stdout, "\n Invalid HANDLE!");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
APar_DetermineAtomLengths();
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, true);
|
|
APar_WriteFile(ISObasemediafile, output_file, alter_original);
|
|
|
|
#if defined(_WIN32)
|
|
if (preserve_timestamps == true) {
|
|
|
|
hFileOut = APar_OpenFileWin32(ISObasemediafile,
|
|
GENERIC_WRITE | GENERIC_READ,
|
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
0);
|
|
|
|
if (hFileOut != INVALID_HANDLE_VALUE) {
|
|
SetFileTime(hFileOut, &createTime, &accessTime, &writeTime);
|
|
CloseHandle(hFileOut);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!alter_original) {
|
|
// The file was opened orignally as read-only; when it came time to
|
|
// writeback into the original file, that FILE was closed, and a
|
|
// new one opened with write abilities, so to close a FILE that no
|
|
// longer exists would.... be retarded.
|
|
APar_OpenISOBaseMediaFile(ISObasemediafile, false);
|
|
}
|
|
} else {
|
|
if (ISObasemediafile != NULL && argc > 3 && !deep_atom_scan) {
|
|
fprintf(stdout, "No changes.\n");
|
|
}
|
|
}
|
|
APar_FreeMemory();
|
|
return 0;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
|
|
#if !defined(__CYGWIN__)
|
|
|
|
int wmain(int argc, wchar_t *arguments[]) {
|
|
int return_val = 0;
|
|
uint16_t name_len = wcslen(arguments[0]);
|
|
if (name_len >= 9 &&
|
|
_wcsicmp(arguments[0] + (name_len - 9), L"-utf8.exe") == 0) {
|
|
UnicodeOutputStatus = UNIVERSAL_UTF8;
|
|
} else {
|
|
UnicodeOutputStatus = WIN32_UTF16;
|
|
}
|
|
|
|
char **argv = (char **)calloc(argc + 1, sizeof(char *));
|
|
|
|
// for native Win32 & full unicode support (well, cli arguments anyway),
|
|
// take whatever 16bit unicode windows gives (utf16le), and convert
|
|
// EVERYTHING that is sends to utf8; use those utf8 strings (mercifully
|
|
// subject to familiar standby's like strlen) to pass around the program
|
|
// like getopt_long to get cli options; convert from utf8 filenames as
|
|
// required for unicode filename support on Windows using wide file
|
|
// functions.
|
|
for (int z = 0; z < argc; z++) {
|
|
uint32_t wchar_length = wcslen(arguments[z]) + 1;
|
|
argv[z] = (char *)malloc(sizeof(char) * 8 * wchar_length);
|
|
memset(argv[z], 0, 8 * wchar_length);
|
|
if (UnicodeOutputStatus == WIN32_UTF16) {
|
|
UTF16LEToUTF8((unsigned char *)argv[z],
|
|
8 * wchar_length,
|
|
(unsigned char *)arguments[z],
|
|
wchar_length * 2);
|
|
} else {
|
|
strip_bogusUTF16toRawUTF8((unsigned char *)argv[z],
|
|
8 * wchar_length,
|
|
arguments[z],
|
|
wchar_length);
|
|
}
|
|
}
|
|
|
|
argv[argc] = NULL;
|
|
|
|
return_val = real_main(argc, argv);
|
|
|
|
for (int free_cnt = 0; free_cnt < argc; free_cnt++) {
|
|
free(argv[free_cnt]);
|
|
argv[free_cnt] = NULL;
|
|
}
|
|
|
|
free(argv);
|
|
argv = NULL;
|
|
|
|
return return_val;
|
|
}
|
|
|
|
#ifdef __MINGW32__
|
|
|
|
int main() {
|
|
int argc;
|
|
wchar_t **argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
|
return wmain(argc, argv);
|
|
}
|
|
|
|
#endif
|
|
|
|
#else // defined __CYGWIN__
|
|
|
|
int main(int argc, char *argv[]) {
|
|
size_t name_len = strlen(argv[0]);
|
|
if (name_len >= 5 && (strcmp(argv[0] + (name_len - 5), "-utf8") == 0 ||
|
|
strcmp(argv[0] + (name_len - 5), "-UTF8") == 0)) {
|
|
UnicodeOutputStatus = UNIVERSAL_UTF8;
|
|
} else {
|
|
UnicodeOutputStatus = WIN32_UTF16;
|
|
}
|
|
return real_main(argc, argv);
|
|
}
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
int main(int argc, char *argv[]) { return real_main(argc, argv); }
|
|
|
|
#endif
|
|
|
|
/* vim:ts=4:sw=4:et:
|
|
*/
|