Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion cmd/mark2note/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ Flags:
--xhs-visibility <name> override Xiaohongshu visibility for --publish-xhs/--prepare-xhs (public or only-self)
--xhs-schedule-at <time> override Xiaohongshu schedule time for --publish-xhs/--prepare-xhs (YYYY-MM-DD HH:MM:SS)
--collection <name> override Xiaohongshu collection for --publish-xhs/--prepare-xhs
--declare-original override Xiaohongshu original declaration for --publish-xhs/--prepare-xhs
--import-photos import generated PNG files into Apple Photos after export
--import-album <name> Apple Photos album name for imported PNG files
--import-timeout <d> Apple Photos PNG import timeout (default: 2m0s)
Expand Down Expand Up @@ -275,6 +276,7 @@ func parseOptions(args []string) (Options, error) {
fs.StringVar(&opts.XHSVisibility, "xhs-visibility", opts.XHSVisibility, "Xiaohongshu visibility for auto publish")
fs.StringVar(&opts.XHSScheduleAt, "xhs-schedule-at", opts.XHSScheduleAt, "Xiaohongshu schedule time for auto publish")
fs.StringVar(&opts.XHSCollection, "collection", opts.XHSCollection, "Xiaohongshu collection name for auto publish")
fs.BoolVar(&opts.XHSDeclareOriginal, "declare-original", opts.XHSDeclareOriginal, "declare original content for auto publish")
fs.BoolVar(&opts.ImportPhotos, "import-photos", opts.ImportPhotos, "import generated PNG files into Apple Photos after export")
fs.StringVar(&opts.ImportAlbum, "import-album", opts.ImportAlbum, "Apple Photos album name for imported PNG files")
fs.DurationVar(&opts.ImportTimeout, "import-timeout", opts.ImportTimeout, "Apple Photos PNG import timeout")
Expand Down Expand Up @@ -350,6 +352,7 @@ func parseOptions(args []string) (Options, error) {
xhsVisibilityChanged := false
xhsScheduleAtChanged := false
xhsCollectionChanged := false
xhsDeclareOriginalChanged := false
xhsContentModeChanged := false
stopBeforeSubmitChanged := false
fs.Visit(func(f *flag.Flag) {
Expand Down Expand Up @@ -400,6 +403,8 @@ func parseOptions(args []string) (Options, error) {
xhsScheduleAtChanged = true
case "collection":
xhsCollectionChanged = true
case "declare-original":
xhsDeclareOriginalChanged = true
case "stop-before-submit":
stopBeforeSubmitChanged = true
}
Expand All @@ -426,6 +431,7 @@ func parseOptions(args []string) (Options, error) {
opts.XHSVisibilityChanged = xhsVisibilityChanged
opts.XHSScheduleAtChanged = xhsScheduleAtChanged
opts.XHSCollectionChanged = xhsCollectionChanged
opts.XHSDeclareOriginalChanged = xhsDeclareOriginalChanged
opts.XHSContentModeChanged = xhsContentModeChanged
opts.StopBeforeSubmitChanged = stopBeforeSubmitChanged
if opts.XHSTagsChanged && !opts.PublishXHS && !opts.PrepareXHS {
Expand All @@ -437,6 +443,9 @@ func parseOptions(args []string) (Options, error) {
if opts.XHSCollectionChanged && !opts.PublishXHS && !opts.PrepareXHS {
return Options{}, fmt.Errorf("--collection requires --publish-xhs or --prepare-xhs\n\n%s", usageText())
}
if opts.XHSDeclareOriginalChanged && !opts.PublishXHS && !opts.PrepareXHS {
return Options{}, fmt.Errorf("--declare-original requires --publish-xhs or --prepare-xhs\n\n%s", usageText())
}
if opts.XHSContentModeChanged && !opts.PublishXHS && !opts.PrepareXHS {
return Options{}, fmt.Errorf("--xhs-content-mode requires --publish-xhs or --prepare-xhs\n\n%s", usageText())
}
Expand Down Expand Up @@ -933,10 +942,18 @@ func buildAutoPublishXHSOptions(renderOpts Options, renderResult app.Result) (ap
cliOpts.Collection = strings.TrimSpace(renderOpts.XHSCollection)
cliOpts.CollectionChanged = true
}
if renderOpts.XHSDeclareOriginalChanged {
cliOpts.DeclareOriginal = renderOpts.XHSDeclareOriginal
cliOpts.DeclareOriginalChanged = true
if !renderOpts.XHSDeclareOriginal {
cliOpts.OriginalDeclarationType = ""
cliOpts.OriginalDeclarationTypeChanged = true
}
}
if renderOpts.StopBeforeSubmitChanged {
cliOpts.StopBeforeSubmit = renderOpts.StopBeforeSubmit
}
if electronicPickles {
if electronicPickles && (!renderOpts.XHSDeclareOriginalChanged || renderOpts.XHSDeclareOriginal) {
cliOpts.DeclareOriginal = true
cliOpts.DeclareOriginalChanged = true
cliOpts.OriginalDeclarationType = xhs.OriginalDeclarationTypeAIGenerated
Expand Down
48 changes: 48 additions & 0 deletions cmd/mark2note/main_publish_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,54 @@ func TestBuildAutoPublishXHSTopicsUsesAIForElectronicPickles(t *testing.T) {
}
}

func TestBuildAutoPublishXHSOptionsCanDisableElectronicPicklesOriginality(t *testing.T) {
originalReadFile := readFile
originalLoadConfig := loadConfig
originalBuildPublishTopics := buildPublishTopics
originalBuildPublishTitle := buildPublishTitle
defer func() {
readFile = originalReadFile
loadConfig = originalLoadConfig
buildPublishTopics = originalBuildPublishTopics
buildPublishTitle = originalBuildPublishTitle
}()

imagePath := filepath.Join(t.TempDir(), "p01-cover.png")
if err := os.WriteFile(imagePath, []byte("png"), 0o644); err != nil {
t.Fatalf("WriteFile() error = %v", err)
}
readFile = func(path string) ([]byte, error) {
return []byte("# 每日电子榨菜|2026-06-10\n\n## 小红书卡片\n\n### 第 1 页|视频标题\n"), nil
}
loadConfig = func(path string) (*config.Config, error) {
declareOriginal := true
allowContentCopy := false
topicGenerationEnabled := true
return &config.Config{XHS: config.XHSCfg{Publish: config.XHSPublishCfg{
Account: "walker",
DeclareOriginal: &declareOriginal,
OriginalDeclarationType: "ai_generated",
AllowContentCopy: &allowContentCopy,
TopicGeneration: config.XHSTopicGenerationCfg{Enabled: &topicGenerationEnabled},
}}}, nil
}
buildPublishTopics = func(cfg *config.Config, markdown string, title string) ([]string, error) {
return []string{"电子榨菜", "综艺"}, nil
}
buildPublishTitle = func(cfg *config.Config, markdown string, title string, maxRunes int) (string, error) {
t.Fatal("buildPublishTitle called for title within limit")
return "", nil
}

got, err := buildAutoPublishXHSOptions(Options{InputPath: "article.md", ConfigPath: "configs/config.yaml", XHSDeclareOriginal: false, XHSDeclareOriginalChanged: true}, app.Result{ImagePaths: []string{imagePath}})
if err != nil {
t.Fatalf("buildAutoPublishXHSOptions() error = %v", err)
}
if got.DeclareOriginal || got.OriginalDeclarationType != "" {
t.Fatalf("originality flags = declare:%v type:%q, want disabled without declaration type", got.DeclareOriginal, got.OriginalDeclarationType)
}
}

func TestBuildAutoPublishXHSOptionsDeclaresElectronicPicklesAIGenerated(t *testing.T) {
originalReadFile := readFile
originalLoadConfig := loadConfig
Expand Down
10 changes: 10 additions & 0 deletions cmd/mark2note/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,16 @@ func TestParseOptionsParsesAutoPublishXHSFlags(t *testing.T) {
}
}

func TestParseOptionsParsesAutoPublishXHSDeclareOriginalOverride(t *testing.T) {
opts, err := parseOptions([]string{"--input", "article.md", "--publish-xhs", "--declare-original=false"})
if err != nil {
t.Fatalf("parseOptions() error = %v", err)
}
if !opts.PublishXHS || !opts.XHSDeclareOriginalChanged || opts.XHSDeclareOriginal {
t.Fatalf("opts = %#v, want publish-xhs declare original false override", opts)
}
}

func TestParseOptionsParsesPrepareXHS(t *testing.T) {
opts, err := parseOptions([]string{"--input", "article.md", "--prepare-xhs"})
if err != nil {
Expand Down
108 changes: 55 additions & 53 deletions internal/app/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,59 +40,61 @@ type LiveOptions struct {
}

type Options struct {
OutDir string
ChromePath string
ChromePathChanged bool
Jobs int
InputPath string
FromDeckPath string
ConfigPath string
OutDirChanged bool
Theme string
Author string
PromptExtra string
AssetManifestPath string
CardManifestPath string
AutoPosters bool
PosterSources []string
PublishXHS bool
PrepareXHS bool
StopBeforeSubmit bool
StopBeforeSubmitChanged bool
XHSTags []string
XHSTagsChanged bool
XHSMode string
XHSModeChanged bool
XHSVisibility string
XHSVisibilityChanged bool
XHSScheduleAt string
XHSScheduleAtChanged bool
XHSCollection string
XHSCollectionChanged bool
XHSContentMode string
XHSContentModeChanged bool
Animated AnimatedOptions
Live LiveOptions
ImportPhotos bool
ImportAlbum string
ImportTimeout time.Duration
ImportPhotosChanged bool
ImportAlbumChanged bool
ImportTimeoutChanged bool
AnimatedEnabledChanged bool
AnimatedFormatChanged bool
AnimatedDurationChanged bool
AnimatedFPSChanged bool
LiveEnabledChanged bool
LivePhotoFormatChanged bool
LiveCoverFrameChanged bool
LiveAssembleChanged bool
LiveOutputDirChanged bool
LiveImportPhotosChanged bool
LiveImportAlbumChanged bool
LiveImportTimeoutChanged bool
ViewportWidth int
ViewportHeight int
OutDir string
ChromePath string
ChromePathChanged bool
Jobs int
InputPath string
FromDeckPath string
ConfigPath string
OutDirChanged bool
Theme string
Author string
PromptExtra string
AssetManifestPath string
CardManifestPath string
AutoPosters bool
PosterSources []string
PublishXHS bool
PrepareXHS bool
StopBeforeSubmit bool
StopBeforeSubmitChanged bool
XHSTags []string
XHSTagsChanged bool
XHSMode string
XHSModeChanged bool
XHSVisibility string
XHSVisibilityChanged bool
XHSScheduleAt string
XHSScheduleAtChanged bool
XHSCollection string
XHSCollectionChanged bool
XHSDeclareOriginal bool
XHSDeclareOriginalChanged bool
XHSContentMode string
XHSContentModeChanged bool
Animated AnimatedOptions
Live LiveOptions
ImportPhotos bool
ImportAlbum string
ImportTimeout time.Duration
ImportPhotosChanged bool
ImportAlbumChanged bool
ImportTimeoutChanged bool
AnimatedEnabledChanged bool
AnimatedFormatChanged bool
AnimatedDurationChanged bool
AnimatedFPSChanged bool
LiveEnabledChanged bool
LivePhotoFormatChanged bool
LiveCoverFrameChanged bool
LiveAssembleChanged bool
LiveOutputDirChanged bool
LiveImportPhotosChanged bool
LiveImportAlbumChanged bool
LiveImportTimeoutChanged bool
ViewportWidth int
ViewportHeight int
}

type Result struct {
Expand Down