Token导航 LogoToken导航TokenDH.com
研究检索external-servicegithub未标认证来源可访问许可证需确认审计通过

media-asset-management媒体资产管理

Agent Skill

media-asset-management 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

190

周安装

8

GitHub Stars

61

下载量

67
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:media-asset-management(媒体资产管理)
来源仓库:https://github.com/melodic-software/claude-code-plugins
仓库路径:skills/media-asset-management
安装命令:
npx skills add https://github.com/melodic-software/claude-code-plugins --skill media-asset-management
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/melodic-software/claude-code-plugins --skill media-asset-management

简介

media-asset-management 提供媒体资产管理与检索能力,适用于多宿主环境中的资源定位。

  • 可用于媒体文件元数据筛选、来源仓库内容查找和分类管理。
  • 通过 npx skills add 从 claude-code-plugins 仓库安装并使用。
  • 使用前应核实权限、维护状态及是否触发联网或文件系统访问。
  • 建议参考原始文档了解支持的资产类型和操作限制。

SKILL.md

Media Asset Management

Guidance for designing digital asset management systems, media libraries, and upload pipelines for headless CMS.

When to Use This Skill

  • Designing media library architecture
  • Implementing file upload pipelines
  • Planning asset metadata schemas
  • Configuring storage providers
  • Building media search and filtering

Media Asset Model

Core Entity

public class MediaItem
{
    public Guid Id { get; set; }

    // File information
    public string FileName { get; set; } = string.Empty;
    public string Extension { get; set; } = string.Empty;
    public string MimeType { get; set; } = string.Empty;
    public long SizeBytes { get; set; }

    // Storage
    public string StorageProvider { get; set; } = string.Empty;
    public string StoragePath { get; set; } = string.Empty;
    public string PublicUrl { get; set; } = string.Empty;

    // Organization
    public Guid? FolderId { get; set; }
    public MediaFolder? Folder { get; set; }
    public List<string> Tags { get; set; } = new();

    // Metadata
    public MediaMetadata Metadata { get; set; } = new();

    // Audit
    public string UploadedBy { get; set; } = string.Empty;
    public DateTime UploadedUtc { get; set; }
    public DateTime? ModifiedUtc { get; set; }
}

public class MediaMetadata
{
    // Common
    public string? Title { get; set; }
    public string? Description { get; set; }
    public string? Alt { get; set; }
    public string? Caption { get; set; }
    public string? Credit { get; set; }

    // Image-specific
    public int? Width { get; set; }
    public int? Height { get; set; }
    public string? ColorSpace { get; set; }

    // Document-specific
    public int? PageCount { get; set; }
    public string? Author { get; set; }

    // Video-specific
    public TimeSpan? Duration { get; set; }
    public string? Codec { get; set; }
    public int? Bitrate { get; set; }

    // EXIF/XMP
    public Dictionary<string, string> ExifData { get; set; } = new();
}

public class MediaFolder
{
    public Guid Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Path { get; set; } = string.Empty;
    public Guid? ParentId { get; set; }
    public List<MediaFolder> Children { get; set; } = new();
}

Storage Architecture

Storage Provider Abstraction

public interface IMediaStorageProvider
{
    string ProviderName { get; }

    Task<string> UploadAsync(Stream stream, string path, string contentType);
    Task<Stream> DownloadAsync(string path);
    Task DeleteAsync(string path);
    Task<bool> ExistsAsync(string path);
    string GetPublicUrl(string path);
}

// Azure Blob Storage
public class AzureBlobStorageProvider : IMediaStorageProvider
{
    public string ProviderName => "AzureBlob";

    public async Task<string> UploadAsync(
        Stream stream, string path, string contentType)
    {
        var blobClient = _containerClient.GetBlobClient(path);

        await blobClient.UploadAsync(stream, new BlobHttpHeaders
        {
            ContentType = contentType,
            CacheControl = "public, max-age=31536000"
        });

        return path;
    }

    public string GetPublicUrl(string path)
    {
        return $"{_containerClient.Uri}/{path}";
    }
}

// AWS S3
public class S3StorageProvider : IMediaStorageProvider
{
    public string ProviderName => "S3";

    public async Task<string> UploadAsync(
        Stream stream, string path, string contentType)
    {
        var request = new PutObjectRequest
        {
            BucketName = _bucketName,
            Key = path,
            InputStream = stream,
            ContentType = contentType,
            CannedACL = S3CannedACL.PublicRead
        };

        await _s3Client.PutObjectAsync(request);
        return path;
    }
}

// Local file system
public class LocalStorageProvider : IMediaStorageProvider
{
    public string ProviderName => "Local";

    public async Task<string> UploadAsync(
        Stream stream, string path, string contentType)
    {
        var fullPath = Path.Combine(_basePath, path);
        Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);

        await using var fileStream = File.Create(fullPath);
        await stream.CopyToAsync(fileStream);

        return path;
    }
}

Path Generation

public class MediaPathGenerator
{
    public string GeneratePath(string fileName, PathStrategy strategy)
    {
        var ext = Path.GetExtension(fileName);
        var name = Path.GetFileNameWithoutExtension(fileName);
        var safeName = Slugify(name);

        return strategy switch
        {
            PathStrategy.DateBased => $"{DateTime.UtcNow:yyyy/MM/dd}/{safeName}-{Guid.NewGuid():N}{ext}",
            PathStrategy.HashBased => $"{ComputeHash(fileName)[..2]}/{ComputeHash(fileName)[2..4]}/{Guid.NewGuid():N}{ext}",
            PathStrategy.Flat => $"{Guid.NewGuid():N}{ext}",
            PathStrategy.OriginalName => $"{safeName}-{DateTime.UtcNow:yyyyMMddHHmmss}{ext}",
            _ => throw new ArgumentOutOfRangeException()
        };
    }
}

public enum PathStrategy
{
    DateBased,      // 2025/01/15/image-abc123.jpg
    HashBased,      // ab/cd/abc123.jpg
    Flat,           // abc123.jpg
    OriginalName    // my-image-20250115103045.jpg
}

Upload Pipeline

Upload Service

public class MediaUploadService
{
    public async Task<MediaItem> UploadAsync(
        Stream stream,
        string fileName,
        string contentType,
        UploadOptions? options = null)
    {
        options ??= new UploadOptions();

        // Validate
        ValidateFile(fileName, contentType, stream.Length, options);

        // Generate path
        var path = _pathGenerator.GeneratePath(fileName, options.PathStrategy);

        // Process (resize, optimize)
        var processedStream = await ProcessMediaAsync(stream, contentType, options);

        // Upload to storage
        var storagePath = await _storageProvider.UploadAsync(
            processedStream, path, contentType);

        // Extract metadata
        var metadata = await ExtractMetadataAsync(processedStream, contentType);

        // Create record
        var mediaItem = new MediaItem
        {
            Id = Guid.NewGuid(),
            FileName = fileName,
            Extension = Path.GetExtension(fileName),
            MimeType = contentType,
            SizeBytes = processedStream.Length,
            StorageProvider = _storageProvider.ProviderName,
            StoragePath = storagePath,
            PublicUrl = _storageProvider.GetPublicUrl(storagePath),
            FolderId = options.FolderId,
            Tags = options.Tags ?? new List<string>(),
            Metadata = metadata,
            UploadedBy = _currentUser.UserId,
            UploadedUtc = DateTime.UtcNow
        };

        await _repository.AddAsync(mediaItem);

        // Raise event
        await _mediator.Publish(new MediaUploadedEvent(mediaItem));

        return mediaItem;
    }

    private void ValidateFile(
        string fileName, string contentType, long size, UploadOptions options)
    {
        // Check file size
        if (size > options.MaxFileSizeBytes)
            throw new MediaValidationException($"File exceeds maximum size of {options.MaxFileSizeBytes} bytes");

        // Check allowed types
        if (options.AllowedMimeTypes?.Any() == true &&
            !options.AllowedMimeTypes.Contains(contentType))
            throw new MediaValidationException($"File type {contentType} is not allowed");

        // Check extension
        var ext = Path.GetExtension(fileName).ToLowerInvariant();
        if (options.BlockedExtensions?.Contains(ext) == true)
            throw new MediaValidationException($"File extension {ext} is blocked");
    }
}

public class UploadOptions
{
    public Guid? FolderId { get; set; }
    public List<string>? Tags { get; set; }
    public PathStrategy PathStrategy { get; set; } = PathStrategy.DateBased;
    public long MaxFileSizeBytes { get; set; } = 10 * 1024 * 1024; // 10MB
    public List<string>? AllowedMimeTypes { get; set; }
    public List<string>? BlockedExtensions { get; set; }
    public bool ExtractMetadata { get; set; } = true;
    public ImageProcessingOptions? ImageOptions { get; set; }
}

Metadata Extraction

public class MetadataExtractor
{
    public async Task<MediaMetadata> ExtractAsync(Stream stream, string contentType)
    {
        var metadata = new MediaMetadata();

        if (contentType.StartsWith("image/"))
        {
            await ExtractImageMetadataAsync(stream, metadata);
        }
        else if (contentType.StartsWith("video/"))
        {
            await ExtractVideoMetadataAsync(stream, metadata);
        }
        else if (contentType == "application/pdf")
        {
            await ExtractPdfMetadataAsync(stream, metadata);
        }

        return metadata;
    }

    private async Task ExtractImageMetadataAsync(Stream stream, MediaMetadata metadata)
    {
        using var image = await Image.LoadAsync(stream);

        metadata.Width = image.Width;
        metadata.Height = image.Height;

        // Extract EXIF
        if (image.Metadata.ExifProfile != null)
        {
            foreach (var value in image.Metadata.ExifProfile.Values)
            {
                metadata.ExifData[value.Tag.ToString()] = value.GetValue()?.ToString() ?? "";
            }
        }
    }
}

Media Library Features

Folder Management

public class MediaFolderService
{
    public async Task<MediaFolder> CreateFolderAsync(string name, Guid? parentId = null)
    {
        var folder = new MediaFolder
        {
            Id = Guid.NewGuid(),
            Name = name,
            ParentId = parentId,
            Path = await BuildPathAsync(name, parentId)
        };

        await _repository.AddAsync(folder);
        return folder;
    }

    public async Task<List<MediaFolder>> GetFolderTreeAsync()
    {
        var folders = await _repository.GetAllAsync();
        return BuildTree(folders.Where(f => f.ParentId == null));
    }
}

Media Search

public class MediaSearchService
{
    public async Task<PagedResult<MediaItem>> SearchAsync(MediaSearchQuery query)
    {
        var queryable = _context.MediaItems.AsQueryable();

        // Filter by folder
        if (query.FolderId.HasValue)
        {
            queryable = queryable.Where(m => m.FolderId == query.FolderId);
        }

        // Filter by type
        if (!string.IsNullOrEmpty(query.MediaType))
        {
            queryable = query.MediaType switch
            {
                "image" => queryable.Where(m => m.MimeType.StartsWith("image/")),
                "video" => queryable.Where(m => m.MimeType.StartsWith("video/")),
                "document" => queryable.Where(m =>
                    m.MimeType == "application/pdf" ||
                    m.MimeType.Contains("document")),
                _ => queryable
            };
        }

        // Filter by tags
        if (query.Tags?.Any() == true)
        {
            queryable = queryable.Where(m =>
                query.Tags.All(t => m.Tags.Contains(t)));
        }

        // Search text
        if (!string.IsNullOrEmpty(query.SearchText))
        {
            var search = query.SearchText.ToLower();
            queryable = queryable.Where(m =>
                m.FileName.ToLower().Contains(search) ||
                m.Metadata.Title!.ToLower().Contains(search) ||
                m.Metadata.Description!.ToLower().Contains(search));
        }

        // Apply sorting
        queryable = query.SortBy switch
        {
            "name" => queryable.OrderBy(m => m.FileName),
            "date" => queryable.OrderByDescending(m => m.UploadedUtc),
            "size" => queryable.OrderByDescending(m => m.SizeBytes),
            _ => queryable.OrderByDescending(m => m.UploadedUtc)
        };

        return await queryable.ToPagedResultAsync(query.Page, query.PageSize);
    }
}

public class MediaSearchQuery
{
    public Guid? FolderId { get; set; }
    public string? MediaType { get; set; }
    public List<string>? Tags { get; set; }
    public string? SearchText { get; set; }
    public string? SortBy { get; set; }
    public int Page { get; set; } = 1;
    public int PageSize { get; set; } = 20;
}

Media API

Endpoints

POST   /api/media/upload              # Upload single file
POST   /api/media/upload/bulk         # Bulk upload
GET    /api/media                     # List/search media
GET    /api/media/{id}                # Get media item
DELETE /api/media/{id}                # Delete media
PATCH  /api/media/{id}                # Update metadata

# Folders
GET    /api/media/folders             # Get folder tree
POST   /api/media/folders             # Create folder
DELETE /api/media/folders/{id}        # Delete folder

Media Response

{
  "data": {
    "id": "media-123",
    "fileName": "hero-image.jpg",
    "mimeType": "image/jpeg",
    "sizeBytes": 245678,
    "url": "https://cdn.example.com/media/2025/01/15/hero-image-abc123.jpg",
    "metadata": {
      "title": "Homepage Hero",
      "alt": "Team working together",
      "width": 1920,
      "height": 1080
    },
    "folder": {
      "id": "folder-456",
      "name": "Homepage",
      "path": "/Marketing/Homepage"
    },
    "tags": ["hero", "homepage", "team"],
    "uploadedBy": "user-789",
    "uploadedUtc": "2025-01-15T10:30:00Z"
  }
}

Related Skills

  • image-optimization - Image processing and optimization
  • cdn-media-delivery - CDN configuration and delivery
  • content-type-modeling - Media fields in content types

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.87%
按下载量换算23

Claude

32.99%
按下载量换算22

Cursor

21.4%
按下载量换算14

Gemini CLI

9.2%
按下载量换算6

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

external-service

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills