nerab / dropcaster

Simple Podcast Publishing
http://nerab.github.io/dropcaster/
MIT License
154 stars 33 forks source link

Sidecar files #9

Closed nerab closed 2 years ago

nerab commented 9 years ago

You may override the meta data for any episode by providing a YAML file with the same name as the mp3 file, but with an extension of yml or yaml (ususally refered to as sidecar file). Any attributes specified in this file override the ID tags in the mp3 file.

Dropcaster will only write the sidecar file if the appropriate command line option was passed, and it will use the information in it only for generating new files like the index.rss. It will not write back to mp3 files.

Sidecar files are also useful for referencing a (remote) enclosure without re-hosting it (which, besides wasted bandwidth, could also create copyright issues). If a sidecar file is present and it points to a remote media URL, dropcaster will generate an item in the feed, even if the media is not available locally.

anatomatic commented 9 years ago

Is this feature implemented? I've been trying to change the pub-date on some files so they order correctly in my pod-catcher. Easy enough to override the ID3 tags if not.

PS Love Dropcaster, big help in publishing podcasts meant for a small group.

nerab commented 9 years ago

Side car files are in the vision but not implemented yet, sorry.

Would the feature be useful for you as described, or should dropcaster work differently?

And - thanks for the nice words! Where can I find your podcast?

anatomatic commented 9 years ago

Got it — it was worded so well here I thought it might exist.

I was able to solve my issue by changing the date modified times of the mp3s via touch -t. If my assumptions are correct, dropcaster takes date modified and converts that to pubDate in the XML.

I'm afraid the podcast I'm dropcasting isn't public — basically I use it to broadcast recording meeting notes to a few collaborators, but if I do make a public one I will certainly share.

I'd say the only other functionality (besides sidecar files) I'd find useful is the ability to use .m4a or other audio-file types besides mp3. But it's easy enough to keep everything in mp3.

nerab commented 9 years ago

The power of having a vsion ;-)

Thanks for the explanation!

nerab commented 9 years ago

mp4 requires a meta data parser I could not find yet.

ankushnarula commented 8 years ago

Is this project/issue dead?

nerab commented 8 years ago

What makes you think so?

ankushnarula commented 8 years ago

The contribution graph? :-) I need this tool but unfortunately my files are also generated in m4a format. I saw a fellow created a gem called ruby-audioinfo but I haven't had a chance to examine it or your code yet. Thank you for your quick reply.

nerab commented 8 years ago

Maybe ffmpeg can read mp4 meta data? It would be awesome if you could give it a try and report back here.

ankushnarula commented 8 years ago

ffprobe included with ffmpeg is able to extract metadata. I'm using the command-line against m4a and m4b files. I receive full metadata including chapter information. Please see the sample command and JSON output below. Also, I found this little ruby wrapper but have not had a chance to examine it yet: https://github.com/beanieboi/ffprober

> ffprobe -v quiet -print_format json -show_format -show_streams -show_chapters /tmp/Audiobooks/OnAnarchismUnabridged.m4b

{
    "streams": [
        {
            "index": 0,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_time_base": "1/44100",
            "codec_tag_string": "mp4a",
            "codec_tag": "0x6134706d",
            "sample_fmt": "fltp",
            "sample_rate": "44100",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/44100",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 851329024,
            "duration": "19304.513016",
            "bit_rate": "86896",
            "max_bit_rate": "126344",
            "nb_frames": "831376",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0
            },
            "tags": {
                "creation_time": "2016-09-02 23:36:26",
                "language": "eng"
            }
        },
        {
            "index": 1,
            "codec_name": "mov_text",
            "codec_long_name": "3GPP Timed Text subtitle",
            "codec_type": "subtitle",
            "codec_time_base": "1/22050",
            "codec_tag_string": "text",
            "codec_tag": "0x74786574",
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/22050",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 425664512,
            "duration": "19304.513016",
            "nb_frames": "6",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0
            },
            "tags": {
                "creation_time": "2016-09-02 23:36:26",
                "language": "eng"
            }
        },
        {
            "index": 2,
            "codec_name": "mjpeg",
            "codec_long_name": "MJPEG (Motion JPEG)",
            "codec_type": "video",
            "codec_time_base": "1/90000",
            "codec_tag_string": "[0][0][0][0]",
            "codec_tag": "0x0000",
            "width": 500,
            "height": 500,
            "coded_width": 500,
            "coded_height": 500,
            "has_b_frames": 0,
            "sample_aspect_ratio": "1:1",
            "display_aspect_ratio": "1:1",
            "pix_fmt": "yuvj420p",
            "level": -99,
            "color_range": "pc",
            "color_space": "bt470bg",
            "chroma_location": "center",
            "refs": 1,
            "r_frame_rate": "90000/1",
            "avg_frame_rate": "0/0",
            "time_base": "1/90000",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 1737406170,
            "duration": "19304.513000",
            "bits_per_raw_sample": "8",
            "disposition": {
                "default": 0,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 1
            }
        }
    ],
    "chapters": [
        {
            "id": 0,
            "time_base": "1/22050",
            "start": 0,
            "start_time": "0.000000",
            "end": 25486336,
            "end_time": "1155.842902",
            "tags": {
                "title": "Chapter 1"
            }
        },
        {
            "id": 1,
            "time_base": "1/22050",
            "start": 25486336,
            "start_time": "1155.842902",
            "end": 79076352,
            "end_time": "3586.229116",
            "tags": {
                "title": "Chapter 2"
            }
        },
        {
            "id": 2,
            "time_base": "1/22050",
            "start": 79076352,
            "start_time": "3586.229116",
            "end": 134173696,
            "end_time": "6084.974875",
            "tags": {
                "title": "Chapter 3"
            }
        },
        {
            "id": 3,
            "time_base": "1/22050",
            "start": 134173696,
            "start_time": "6084.974875",
            "end": 286458880,
            "end_time": "12991.332426",
            "tags": {
                "title": "Chapter 4"
            }
        },
        {
            "id": 4,
            "time_base": "1/22050",
            "start": 286458880,
            "start_time": "12991.332426",
            "end": 317988864,
            "end_time": "14421.263673",
            "tags": {
                "title": "Chapter 5"
            }
        },
        {
            "id": 5,
            "time_base": "1/22050",
            "start": 317988864,
            "start_time": "14421.263673",
            "end": 391087104,
            "end_time": "17736.376599",
            "tags": {
                "title": "Chapter 6"
            }
        }
    ],
    "format": {
        "filename": “/tmp/Audiobooks/OnAnarchismUnabridged.m4b",
        "nb_streams": 3,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "19304.513000",
        "size": "213154331",
        "bit_rate": "88333",
        "probe_score": 100,
        "tags": {
            "major_brand": "M4B ",
            "minor_version": "0",
            "compatible_brands": "M4B mp42isom",
            "creation_time": "2016-09-02 23:36:26",
            "title": "On Anarchism (Unabridged)",
            "artist": "Noam Chomsky, Nathan Schneider (introduction)",
            "album": "On Anarchism (Unabridged)",
            "genre": "Audiobook",
            "comment": "In these essays, Chomsky redeems one of the most maligned ideologies, anarchism, and places it at the foundation of his political thinking....",
            "copyright": "169;2013 Noam Chomsky; Introduction 2013 Nathan Schneider",
            "date": "2014"
        }
    }
}
nerab commented 8 years ago

Nice, thank you. ffprobe even seems to work for mp3s:

$ ffprobe -v quiet -print_format json -show_format -show_streams -show_chapters test/fixtures/iTunes.mp3
{
    ...
    "format": {
        "filename": "test/fixtures/iTunes.mp3",
        "nb_streams": 1,
        "nb_programs": 0,
        "format_name": "mp3",
        "format_long_name": "MP2/3 (MPEG audio layer 2/3)",
        "start_time": "0.000000",
        "duration": "2.899405",
        "size": "58119",
        "bit_rate": "160361",
        "probe_score": 51,
        "tags": {
            "title": "iTunes Name",
            "artist": "iTunes Artist",
            "album_artist": "iTunes Album Artist",
            "TCM": "iTunes Composer",
            "album": "iTunes Album",
            "TT1": "iTunes Grouping",
            "track": "42/99",
            "TPA": "11",
            "TT3": "iTunes Description (Video Pane)",
            "TBP": "111",
            "genre": "iTunes Genre",
            "date": "1970"
        }
    }
}

This could replace the ruby-audioinfo gem entirely.

fadookie commented 5 years ago

I was able to use dropcaster today to create a feed of m4a files (AAC) encoded by iTunes. Dunno if anything changed but it seems to "just work" now (at least on macOS).

nerab commented 2 years ago

Great to hear!